diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml index 1ba1b0388d6376..b84ecaa88b65a7 100644 --- a/.github/workflows/check_dependencies.yml +++ b/.github/workflows/check_dependencies.yml @@ -50,7 +50,7 @@ jobs: - run: make fix-depends - - run: git diff --no-ext-diff --ignore-submodules --exit-code + - run: git diff --color --no-ext-diff --ignore-submodules --exit-code - uses: ./.github/actions/slack with: diff --git a/.github/workflows/check_misc.yml b/.github/workflows/check_misc.yml index 0039fa69718a75..e900f0912372a2 100644 --- a/.github/workflows/check_misc.yml +++ b/.github/workflows/check_misc.yml @@ -70,7 +70,7 @@ jobs: run: | date +"mon=%-m"%n"day=%-d" >> $GITHUB_OUTPUT env: - TZ: Tokyo/Asia + TZ: Asia/Tokyo - id: deprecation run: | @@ -82,11 +82,6 @@ jobs: fi continue-on-error: ${{ steps.now.outputs.mon < 12 }} - - name: Check if date in man pages is up-to-date - run: | - make V=1 GIT=git BASERUBY=ruby update-man-date - git diff --color --no-ext-diff --ignore-submodules --exit-code -- man - - name: Check if to generate documents id: rdoc run: | diff --git a/NEWS.md b/NEWS.md index 90119c910b2b6e..100c4d72f19383 100644 --- a/NEWS.md +++ b/NEWS.md @@ -68,7 +68,7 @@ releases. ### The following bundled gems are updated. -* minitest 6.0.1 +* minitest 6.0.2 * test-unit 3.7.7 * rss 0.3.2 * net-imap 0.6.3 diff --git a/bootstraptest/test_ractor.rb b/bootstraptest/test_ractor.rb index 9242dce7dcb6b4..08f9b73c13192b 100644 --- a/bootstraptest/test_ractor.rb +++ b/bootstraptest/test_ractor.rb @@ -507,14 +507,14 @@ def test n } # To copy the object, now Marshal#dump is used -assert_equal "allocator undefined for Thread", %q{ +assert_match /can not copy unshareable object/, %q{ obj = Thread.new{} begin r = Ractor.new obj do |msg| msg end - rescue TypeError => e - e.message #=> no _dump_data is defined for class Thread + rescue Ractor::Error => e + e.message else 'ng' end diff --git a/class.c b/class.c index 81e740b2099ef7..d126e5a3f6f80b 100644 --- a/class.c +++ b/class.c @@ -1422,7 +1422,6 @@ make_singleton_class(VALUE obj) RBASIC_SET_CLASS(obj, klass); rb_singleton_class_attached(klass, obj); rb_yjit_invalidate_no_singleton_class(orig_class); - rb_zjit_invalidate_no_singleton_class(orig_class); SET_METACLASS_OF(klass, METACLASS_OF(rb_class_real(orig_class))); return klass; @@ -2884,16 +2883,18 @@ singleton_class_of(VALUE obj, bool ensure_eigenclass) } void -rb_freeze_singleton_class(VALUE x) -{ - /* should not propagate to meta-meta-class, and so on */ - if (!RCLASS_SINGLETON_P(x)) { - VALUE klass = RBASIC_CLASS(x); - if (klass && // no class when hidden from ObjectSpace - FL_TEST_RAW(klass, FL_SINGLETON) && - !OBJ_FROZEN_RAW(klass)) { - OBJ_FREEZE(klass); - } +rb_freeze_singleton_class(VALUE attached_object) +{ + VALUE klass; + + /* Freeze singleton classes of singleton class, as singleton class is frozen, and so on */ + /* In each iteration, check the current object's class pointer is the singleton class of the object. */ + while ((klass = RBASIC_CLASS(attached_object)) && + FL_TEST_RAW(klass, FL_SINGLETON) && + !OBJ_FROZEN_RAW(klass) && + (RCLASS_ATTACHED_OBJECT(klass) == attached_object)) { + attached_object = klass; + OBJ_FREEZE(attached_object); } } diff --git a/defs/gmake.mk b/defs/gmake.mk index 475ad61602d4aa..087aa1b549676b 100644 --- a/defs/gmake.mk +++ b/defs/gmake.mk @@ -276,7 +276,7 @@ pull-github: fetch-github $(call pull-github,$(PR)) define pull-github - $(eval GITHUB_MERGE_BASE := $(shell $(GIT_LOG_FORMAT)%H -1) + $(eval GITHUB_MERGE_BASE := $(shell $(GIT_IN_SRC) rev-parse HEAD) $(eval GITHUB_MERGE_BRANCH := $(shell $(GIT_IN_SRC) symbolic-ref --short HEAD)) $(eval GITHUB_MERGE_WORKTREE := $(shell mktemp -d "$(srcdir)/gh-$(1)-XXXXXX")) $(GIT_IN_SRC) worktree prune @@ -434,7 +434,7 @@ ifneq ($(DOT_WAIT),) endif ifeq ($(HAVE_GIT),yes) -REVISION_LATEST := $(shell $(GIT_LOG_FORMAT)%H -1 2>/dev/null) +REVISION_LATEST := $(shell $(GIT_IN_SRC) rev-parse HEAD) else REVISION_LATEST := update endif diff --git a/ext/json/simd/simd.h b/ext/json/simd/simd.h index 3bb86acdece37b..611b41b0668420 100644 --- a/ext/json/simd/simd.h +++ b/ext/json/simd/simd.h @@ -133,16 +133,6 @@ ALWAYS_INLINE(static) int string_scan_simd_neon(const char **ptr, const char *en return 0; } -static inline uint8x16x4_t load_uint8x16_4(const unsigned char *table) -{ - uint8x16x4_t tab; - tab.val[0] = vld1q_u8(table); - tab.val[1] = vld1q_u8(table+16); - tab.val[2] = vld1q_u8(table+32); - tab.val[3] = vld1q_u8(table+48); - return tab; -} - #endif /* ARM Neon Support.*/ #if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) diff --git a/ext/openssl/ossl.c b/ext/openssl/ossl.c index 98127fcba02314..d3270f31f5b916 100644 --- a/ext/openssl/ossl.c +++ b/ext/openssl/ossl.c @@ -34,7 +34,11 @@ ossl_##name##_ary2sk0(VALUE ary) \ " of class ##type##"); \ } \ x = dup(val); /* NEED TO DUP */ \ - sk_##type##_push(sk, x); \ + if (!sk_##type##_push(sk, x)) { \ + type##_free(x); \ + sk_##type##_pop_free(sk, type##_free); \ + ossl_raise(eOSSLError, NULL); \ + } \ } \ return (VALUE)sk; \ } \ diff --git a/ext/openssl/ossl_bio.c b/ext/openssl/ossl_bio.c index 4edde5091d0933..cc03c5d5f720eb 100644 --- a/ext/openssl/ossl_bio.c +++ b/ext/openssl/ossl_bio.c @@ -32,7 +32,11 @@ ossl_membio2str(BIO *bio) int state; BUF_MEM *buf; - BIO_get_mem_ptr(bio, &buf); + if (BIO_get_mem_ptr(bio, &buf) <= 0) { + BIO_free(bio); + ossl_raise(eOSSLError, "BIO_get_mem_ptr"); + } + ret = ossl_str_new(buf->data, buf->length, &state); BIO_free(bio); if (state) diff --git a/gc.c b/gc.c index 2a4dec32e9119f..26e65a90f3bc74 100644 --- a/gc.c +++ b/gc.c @@ -1278,6 +1278,7 @@ rb_gc_obj_needs_cleanup_p(VALUE obj) case imemo_ifunc: case imemo_memo: case imemo_svar: + case imemo_callcache: case imemo_throw_data: return false; default: @@ -2270,15 +2271,6 @@ 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; @@ -4062,23 +4054,6 @@ vm_weak_table_foreach_update_weak_key(st_data_t *key, st_data_t *value, st_data_ return ret; } -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_sym_set_foreach(VALUE *sym_ptr, void *data) { @@ -4275,17 +4250,6 @@ 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("Unreachable"); default: @@ -5054,12 +5018,6 @@ rb_raw_obj_info_buitin_type(char *const buff, const size_t buff_size, const VALU APPEND_F("r:%d", r->pub.id); } } - else { - const char * const type_name = rb_objspace_data_type_name(obj); - if (type_name) { - APPEND_F("%s", type_name); - } - } break; } case T_IMEMO: { diff --git a/gc/default/default.c b/gc/default/default.c index 046aa146f73055..1099d6e0dc11e5 100644 --- a/gc/default/default.c +++ b/gc/default/default.c @@ -2175,10 +2175,6 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace, RBASIC(obj)->shape_id = 0; #endif - int t = flags & RUBY_T_MASK; - if (t == T_CLASS || t == T_MODULE || t == T_ICLASS) { - RVALUE_AGE_SET_CANDIDATE(objspace, obj); - } #if RACTOR_CHECK_MODE void rb_ractor_setup_belonging(VALUE obj); @@ -4407,8 +4403,16 @@ gc_aging(rb_objspace_t *objspace, VALUE obj) if (!RVALUE_PAGE_WB_UNPROTECTED(page, obj)) { if (!RVALUE_OLD_P(objspace, obj)) { - gc_report(3, objspace, "gc_aging: YOUNG: %s\n", rb_obj_info(obj)); - RVALUE_AGE_INC(objspace, obj); + int t = BUILTIN_TYPE(obj); + if (t == T_CLASS || t == T_MODULE || t == T_ICLASS) { + gc_report(3, objspace, "gc_aging: YOUNG class: %s\n", rb_obj_info(obj)); + RVALUE_AGE_SET(obj, RVALUE_OLD_AGE); + RVALUE_OLD_UNCOLLECTIBLE_SET(objspace, obj); + } + else { + gc_report(3, objspace, "gc_aging: YOUNG: %s\n", rb_obj_info(obj)); + RVALUE_AGE_INC(objspace, obj); + } } else if (is_full_marking(objspace)) { GC_ASSERT(RVALUE_PAGE_UNCOLLECTIBLE(page, obj) == FALSE); diff --git a/gc/gc.h b/gc/gc.h index 5979b4a00193e2..469a4902f03365 100644 --- a/gc/gc.h +++ b/gc/gc.h @@ -31,7 +31,6 @@ 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 }; diff --git a/gems/bundled_gems b/gems/bundled_gems index d27f29682153b0..a9262ffa1236b6 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -6,7 +6,7 @@ # - revision: revision in repository-url to test # if `revision` is not given, "v"+`version` or `version` will be used. -minitest 6.0.1 https://github.com/minitest/minitest +minitest 6.0.2 https://github.com/minitest/minitest power_assert 3.0.1 https://github.com/ruby/power_assert rake 13.3.1 https://github.com/ruby/rake test-unit 3.7.7 https://github.com/test-unit/test-unit diff --git a/imemo.c b/imemo.c index b8132aca69ff5f..3300ec4b7c24f6 100644 --- a/imemo.c +++ b/imemo.c @@ -63,29 +63,27 @@ rb_imemo_tmpbuf_new(void) } void * -rb_alloc_tmp_buffer_with_count(volatile VALUE *store, size_t size, size_t cnt) +rb_alloc_tmp_buffer(volatile VALUE *store, long len) { + if (len < 0) { + rb_raise(rb_eArgError, "negative buffer size (or size too big)"); + } + /* Keep the order; allocate an empty imemo first then xmalloc, to * get rid of potential memory leak */ rb_imemo_tmpbuf_t *tmpbuf = (rb_imemo_tmpbuf_t *)rb_imemo_tmpbuf_new(); *store = (VALUE)tmpbuf; - void *ptr = ruby_xmalloc(size); + void *ptr = ruby_xmalloc(len); tmpbuf->ptr = ptr; - tmpbuf->size = size; + tmpbuf->size = len; return ptr; } void * -rb_alloc_tmp_buffer(volatile VALUE *store, long len) +rb_alloc_tmp_buffer_with_count(volatile VALUE *store, size_t size, size_t cnt) { - long cnt; - - if (len < 0 || (cnt = (long)roomof(len, sizeof(VALUE))) < 0) { - rb_raise(rb_eArgError, "negative buffer size (or size too big)"); - } - - return rb_alloc_tmp_buffer_with_count(store, len, cnt); + return rb_alloc_tmp_buffer(store, (long)size); } void diff --git a/include/ruby/internal/memory.h b/include/ruby/internal/memory.h index cd099f85db9b31..eb9c1d252580f7 100644 --- a/include/ruby/internal/memory.h +++ b/include/ruby/internal/memory.h @@ -741,8 +741,7 @@ static inline void * rb_alloc_tmp_buffer2(volatile VALUE *store, long count, size_t elsize) { const size_t total_size = rbimpl_size_mul_or_raise(RBIMPL_CAST((size_t)count), elsize); - const size_t cnt = (total_size + sizeof(VALUE) - 1) / sizeof(VALUE); - return rb_alloc_tmp_buffer_with_count(store, total_size, cnt); + return rb_alloc_tmp_buffer(store, (long)total_size); } RBIMPL_SYMBOL_EXPORT_BEGIN() diff --git a/internal/imemo.h b/internal/imemo.h index ed569317461011..c5c0d005f1ffa8 100644 --- a/internal/imemo.h +++ b/internal/imemo.h @@ -139,7 +139,6 @@ static inline int imemo_type_p(VALUE imemo, enum imemo_type imemo_type); static inline bool imemo_throw_data_p(VALUE imemo); static inline struct vm_ifunc *rb_vm_ifunc_proc_new(rb_block_call_func_t func, const void *data); static inline void *RB_IMEMO_TMPBUF_PTR(VALUE v); -static inline void *rb_imemo_tmpbuf_set_ptr(VALUE v, void *ptr); static inline void MEMO_V1_SET(struct MEMO *m, VALUE v); static inline void MEMO_V2_SET(struct MEMO *m, VALUE v); @@ -193,30 +192,16 @@ RB_IMEMO_TMPBUF_PTR(VALUE v) return p->ptr; } -static inline void * -rb_imemo_tmpbuf_set_ptr(VALUE v, void *ptr) -{ - return ((rb_imemo_tmpbuf_t *)v)->ptr = ptr; -} - static inline VALUE rb_imemo_tmpbuf_new_from_an_RString(VALUE str) { - const void *src; VALUE imemo; - rb_imemo_tmpbuf_t *tmpbuf; - void *dst; size_t len; StringValue(str); - /* create tmpbuf to keep the pointer before xmalloc */ - imemo = rb_imemo_tmpbuf_new(); - tmpbuf = (rb_imemo_tmpbuf_t *)imemo; len = RSTRING_LEN(str); - src = RSTRING_PTR(str); - dst = ruby_xmalloc(len); - memcpy(dst, src, len); - tmpbuf->ptr = dst; + rb_alloc_tmp_buffer(&imemo, len); + memcpy(RB_IMEMO_TMPBUF_PTR(imemo), RSTRING_PTR(str), len); return imemo; } diff --git a/lib/prism.rb b/lib/prism.rb index 5b3ce8752c267d..6b34ab12bfc6e5 100644 --- a/lib/prism.rb +++ b/lib/prism.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled # The Prism Ruby parser. # @@ -37,14 +39,16 @@ module Prism # Raised when requested to parse as the currently running Ruby version but Prism has no support for it. class CurrentVersionError < ArgumentError # Initialize a new exception for the given ruby version string. + #-- + #: (String version) -> void def initialize(version) message = +"invalid version: Requested to parse as `version: 'current'`; " - segments = + major, minor, = if version.match?(/\A\d+\.\d+.\d+\z/) version.split(".").map(&:to_i) end - if segments && ((segments[0] < 3) || (segments[0] == 3 && segments[1] < 3)) + if major && minor && ((major < 3) || (major == 3 && minor < 3)) message << " #{version} is below the minimum supported syntax." else message << " #{version} is unknown. Please update the `prism` gem." @@ -61,6 +65,8 @@ def initialize(version) # resembles the return value of Ripper.lex. # # For supported options, see Prism.parse. + #-- + #: (String source, **untyped options) -> LexCompat::Result def self.lex_compat(source, **options) LexCompat.new(source, **options).result # steep:ignore end @@ -69,9 +75,37 @@ def self.lex_compat(source, **options) # load(source, serialized, freeze) -> ParseResult # # Load the serialized AST using the source as a reference into a tree. + #-- + #: (String source, String serialized, ?bool freeze) -> ParseResult def self.load(source, serialized, freeze = false) Serialize.load_parse(source, serialized, freeze) end + + # @rbs! + # VERSION: String + # BACKEND: :CEXT | :FFI + # + # interface _Stream + # def gets: (?Integer integer) -> (String | nil) + # end + # + # def self.parse: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseResult + # def self.profile: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> void + # def self.lex: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> LexResult + # def self.parse_lex: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseLexResult + # def self.dump: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> String + # def self.parse_comments: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> Array[Comment] + # def self.parse_success?: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> bool + # def self.parse_failure?: (String source, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> bool + # def self.parse_stream: (_Stream stream, ?filepath: String, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseResult + # def self.parse_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseResult + # def self.profile_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> void + # def self.lex_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> LexResult + # def self.parse_lex_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> ParseLexResult + # def self.dump_file: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> String + # def self.parse_file_comments: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> Array[Comment] + # def self.parse_file_success?: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> bool + # def self.parse_file_failure?: (String filepath, ?command_line: String, ?encoding: Encoding | false, ?freeze: bool, ?frozen_string_literal: bool, ?line: Integer, ?main_script: bool, ?partial_script: bool, ?scopes: Array[Array[Symbol]], ?version: String) -> bool end require_relative "prism/polyfill/byteindex" diff --git a/lib/prism/desugar_compiler.rb b/lib/prism/desugar_compiler.rb index 7d4201c1c47b50..c64d03f64ae1ec 100644 --- a/lib/prism/desugar_compiler.rb +++ b/lib/prism/desugar_compiler.rb @@ -1,12 +1,18 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism class DesugarAndWriteNode # :nodoc: include DSL - attr_reader :node, :default_source, :read_class, :write_class, :arguments + attr_reader :node #: ClassVariableAndWriteNode | ConstantAndWriteNode | GlobalVariableAndWriteNode | InstanceVariableAndWriteNode | LocalVariableAndWriteNode + attr_reader :default_source #: Source + attr_reader :read_class, :write_class #: Symbol + attr_reader :arguments #: Hash[Symbol, untyped] + #: ((ClassVariableAndWriteNode | ConstantAndWriteNode | GlobalVariableAndWriteNode | InstanceVariableAndWriteNode | LocalVariableAndWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void def initialize(node, default_source, read_class, write_class, **arguments) @node = node @default_source = default_source @@ -16,6 +22,8 @@ def initialize(node, default_source, read_class, write_class, **arguments) end # Desugar `x &&= y` to `x && x = y` + #-- + #: () -> node def compile and_node( location: node.location, @@ -36,8 +44,12 @@ def compile class DesugarOrWriteDefinedNode # :nodoc: include DSL - attr_reader :node, :default_source, :read_class, :write_class, :arguments + attr_reader :node #: ClassVariableOrWriteNode | ConstantOrWriteNode | GlobalVariableOrWriteNode + attr_reader :default_source #: Source + attr_reader :read_class, :write_class #: Symbol + attr_reader :arguments #: Hash[Symbol, untyped] + #: ((ClassVariableOrWriteNode | ConstantOrWriteNode | GlobalVariableOrWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void def initialize(node, default_source, read_class, write_class, **arguments) @node = node @default_source = default_source @@ -47,6 +59,8 @@ def initialize(node, default_source, read_class, write_class, **arguments) end # Desugar `x ||= y` to `defined?(x) ? x : x = y` + #-- + #: () -> node def compile if_node( location: node.location, @@ -87,8 +101,12 @@ def compile class DesugarOperatorWriteNode # :nodoc: include DSL - attr_reader :node, :default_source, :read_class, :write_class, :arguments + attr_reader :node #: ClassVariableOperatorWriteNode | ConstantOperatorWriteNode | GlobalVariableOperatorWriteNode | InstanceVariableOperatorWriteNode | LocalVariableOperatorWriteNode + attr_reader :default_source #: Source + attr_reader :read_class, :write_class #: Symbol + attr_reader :arguments #: Hash[Symbol, untyped] + #: ((ClassVariableOperatorWriteNode | ConstantOperatorWriteNode | GlobalVariableOperatorWriteNode | InstanceVariableOperatorWriteNode | LocalVariableOperatorWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void def initialize(node, default_source, read_class, write_class, **arguments) @node = node @default_source = default_source @@ -98,6 +116,8 @@ def initialize(node, default_source, read_class, write_class, **arguments) end # Desugar `x += y` to `x = x + y` + #-- + #: () -> node def compile binary_operator_loc = node.binary_operator_loc.chop @@ -131,8 +151,12 @@ def compile class DesugarOrWriteNode # :nodoc: include DSL - attr_reader :node, :default_source, :read_class, :write_class, :arguments + attr_reader :node #: InstanceVariableOrWriteNode | LocalVariableOrWriteNode + attr_reader :default_source #: Source + attr_reader :read_class, :write_class #: Symbol + attr_reader :arguments #: Hash[Symbol, untyped] + #: ((InstanceVariableOrWriteNode | LocalVariableOrWriteNode) node, Source default_source, Symbol read_class, Symbol write_class, **untyped arguments) -> void def initialize(node, default_source, read_class, write_class, **arguments) @node = node @default_source = default_source @@ -142,6 +166,8 @@ def initialize(node, default_source, read_class, write_class, **arguments) end # Desugar `x ||= y` to `x || x = y` + #-- + #: () -> node def compile or_node( location: node.location, @@ -162,90 +188,105 @@ def compile private_constant :DesugarAndWriteNode, :DesugarOrWriteNode, :DesugarOrWriteDefinedNode, :DesugarOperatorWriteNode class ClassVariableAndWriteNode + #: () -> node def desugar # :nodoc: DesugarAndWriteNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile end end class ClassVariableOrWriteNode + #: () -> node def desugar # :nodoc: DesugarOrWriteDefinedNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile end end class ClassVariableOperatorWriteNode + #: () -> node def desugar # :nodoc: DesugarOperatorWriteNode.new(self, source, :class_variable_read_node, :class_variable_write_node, name: name).compile end end class ConstantAndWriteNode + #: () -> node def desugar # :nodoc: DesugarAndWriteNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile end end class ConstantOrWriteNode + #: () -> node def desugar # :nodoc: DesugarOrWriteDefinedNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile end end class ConstantOperatorWriteNode + #: () -> node def desugar # :nodoc: DesugarOperatorWriteNode.new(self, source, :constant_read_node, :constant_write_node, name: name).compile end end class GlobalVariableAndWriteNode + #: () -> node def desugar # :nodoc: DesugarAndWriteNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile end end class GlobalVariableOrWriteNode + #: () -> node def desugar # :nodoc: DesugarOrWriteDefinedNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile end end class GlobalVariableOperatorWriteNode + #: () -> node def desugar # :nodoc: DesugarOperatorWriteNode.new(self, source, :global_variable_read_node, :global_variable_write_node, name: name).compile end end class InstanceVariableAndWriteNode + #: () -> node def desugar # :nodoc: DesugarAndWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile end end class InstanceVariableOrWriteNode + #: () -> node def desugar # :nodoc: DesugarOrWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile end end class InstanceVariableOperatorWriteNode + #: () -> node def desugar # :nodoc: DesugarOperatorWriteNode.new(self, source, :instance_variable_read_node, :instance_variable_write_node, name: name).compile end end class LocalVariableAndWriteNode + #: () -> node def desugar # :nodoc: DesugarAndWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile end end class LocalVariableOrWriteNode + #: () -> node def desugar # :nodoc: DesugarOrWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile end end class LocalVariableOperatorWriteNode + #: () -> node def desugar # :nodoc: DesugarOperatorWriteNode.new(self, source, :local_variable_read_node, :local_variable_write_node, name: name, depth: depth).compile end @@ -259,6 +300,8 @@ class DesugarCompiler < MutationCompiler # becomes # # `@@foo && @@foo = bar` + #-- + #: (ClassVariableAndWriteNode node) -> node def visit_class_variable_and_write_node(node) node.desugar end @@ -268,6 +311,8 @@ def visit_class_variable_and_write_node(node) # becomes # # `defined?(@@foo) ? @@foo : @@foo = bar` + #-- + #: (ClassVariableOrWriteNode node) -> node def visit_class_variable_or_write_node(node) node.desugar end @@ -277,6 +322,8 @@ def visit_class_variable_or_write_node(node) # becomes # # `@@foo = @@foo + bar` + #-- + #: (ClassVariableOperatorWriteNode node) -> node def visit_class_variable_operator_write_node(node) node.desugar end @@ -286,6 +333,8 @@ def visit_class_variable_operator_write_node(node) # becomes # # `Foo && Foo = bar` + #-- + #: (ConstantAndWriteNode node) -> node def visit_constant_and_write_node(node) node.desugar end @@ -295,6 +344,8 @@ def visit_constant_and_write_node(node) # becomes # # `defined?(Foo) ? Foo : Foo = bar` + #-- + #: (ConstantOrWriteNode node) -> node def visit_constant_or_write_node(node) node.desugar end @@ -304,6 +355,8 @@ def visit_constant_or_write_node(node) # becomes # # `Foo = Foo + bar` + #-- + #: (ConstantOperatorWriteNode node) -> node def visit_constant_operator_write_node(node) node.desugar end @@ -313,6 +366,8 @@ def visit_constant_operator_write_node(node) # becomes # # `$foo && $foo = bar` + #-- + #: (GlobalVariableAndWriteNode node) -> node def visit_global_variable_and_write_node(node) node.desugar end @@ -322,6 +377,8 @@ def visit_global_variable_and_write_node(node) # becomes # # `defined?($foo) ? $foo : $foo = bar` + #-- + #: (GlobalVariableOrWriteNode node) -> node def visit_global_variable_or_write_node(node) node.desugar end @@ -331,6 +388,8 @@ def visit_global_variable_or_write_node(node) # becomes # # `$foo = $foo + bar` + #-- + #: (GlobalVariableOperatorWriteNode node) -> node def visit_global_variable_operator_write_node(node) node.desugar end @@ -340,6 +399,8 @@ def visit_global_variable_operator_write_node(node) # becomes # # `@foo && @foo = bar` + #-- + #: (InstanceVariableAndWriteNode node) -> node def visit_instance_variable_and_write_node(node) node.desugar end @@ -349,6 +410,8 @@ def visit_instance_variable_and_write_node(node) # becomes # # `@foo || @foo = bar` + #-- + #: (InstanceVariableOrWriteNode node) -> node def visit_instance_variable_or_write_node(node) node.desugar end @@ -358,6 +421,8 @@ def visit_instance_variable_or_write_node(node) # becomes # # `@foo = @foo + bar` + #-- + #: (InstanceVariableOperatorWriteNode node) -> node def visit_instance_variable_operator_write_node(node) node.desugar end @@ -367,6 +432,8 @@ def visit_instance_variable_operator_write_node(node) # becomes # # `foo && foo = bar` + #-- + #: (LocalVariableAndWriteNode node) -> node def visit_local_variable_and_write_node(node) node.desugar end @@ -376,6 +443,8 @@ def visit_local_variable_and_write_node(node) # becomes # # `foo || foo = bar` + #-- + #: (LocalVariableOrWriteNode node) -> node def visit_local_variable_or_write_node(node) node.desugar end @@ -385,6 +454,8 @@ def visit_local_variable_or_write_node(node) # becomes # # `foo = foo + bar` + #-- + #: (LocalVariableOperatorWriteNode node) -> node def visit_local_variable_operator_write_node(node) node.desugar end diff --git a/lib/prism/lex_compat.rb b/lib/prism/lex_compat.rb index 3d5cbfcddc0edd..99d8daacdd26cf 100644 --- a/lib/prism/lex_compat.rb +++ b/lib/prism/lex_compat.rb @@ -1,25 +1,57 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism + # @rbs! + # module Translation + # class Ripper + # EXPR_NONE: Integer + # EXPR_BEG: Integer + # EXPR_MID: Integer + # EXPR_END: Integer + # EXPR_CLASS: Integer + # EXPR_VALUE: Integer + # EXPR_ARG: Integer + # EXPR_CMDARG: Integer + # EXPR_ENDARG: Integer + # EXPR_ENDFN: Integer + # + # class Lexer < Ripper + # class State + # def self.[]: (Integer value) -> State + # end + # end + # end + # end + # This class is responsible for lexing the source using prism and then # converting those tokens to be compatible with Ripper. In the vast majority # of cases, this is a one-to-one mapping of the token type. Everything else # generally lines up. However, there are a few cases that require special # handling. class LexCompat # :nodoc: + # @rbs! + # # A token produced by the Ripper lexer that Prism is replicating. + # type lex_compat_token = [[Integer, Integer], Symbol, String, untyped] + # A result class specialized for holding tokens produced by the lexer. class Result < Prism::Result # The list of tokens that were produced by the lexer. - attr_reader :value + attr_reader :value #: Array[lex_compat_token] # Create a new lex compat result object with the given values. + #-- + #: (Array[lex_compat_token] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) @value = value super(comments, magic_comments, data_loc, errors, warnings, source) end # Implement the hash pattern matching interface for Result. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: super.merge!(value: value) end @@ -205,16 +237,19 @@ module Heredoc # :nodoc: # order back into the token stream and set the state of the last token to # the state that the heredoc was opened in. class PlainHeredoc # :nodoc: - attr_reader :tokens + attr_reader :tokens #: Array[lex_compat_token] + #: () -> void def initialize @tokens = [] end + #: (lex_compat_token token) -> void def <<(token) tokens << token end + #: () -> Array[lex_compat_token] def to_a tokens end @@ -224,21 +259,25 @@ def to_a # that need to be split on "\\\n" to mimic Ripper's behavior. We also need # to keep track of the state that the heredoc was opened in. class DashHeredoc # :nodoc: - attr_reader :split, :tokens + attr_reader :split #: bool + attr_reader :tokens #: Array[lex_compat_token] + #: (bool split) -> void def initialize(split) @split = split @tokens = [] end + #: (lex_compat_token token) -> void def <<(token) tokens << token end + #: () -> Array[lex_compat_token] def to_a embexpr_balance = 0 - tokens.each_with_object([]) do |token, results| #$ Array[Token] + tokens.each_with_object([]) do |token, results| #$ Array[lex_compat_token] case token[1] when :on_embexpr_beg embexpr_balance += 1 @@ -285,8 +324,13 @@ def to_a class DedentingHeredoc # :nodoc: TAB_WIDTH = 8 - attr_reader :tokens, :dedent_next, :dedent, :embexpr_balance + attr_reader :tokens #: Array[lex_compat_token] + attr_reader :dedent_next #: bool + attr_reader :dedent #: Integer? + attr_reader :embexpr_balance #: Integer + # @rbs @ended_on_newline: bool + #: () -> void def initialize @tokens = [] @dedent_next = true @@ -298,6 +342,8 @@ def initialize # As tokens are coming in, we track the minimum amount of common leading # whitespace on plain string content tokens. This allows us to later # remove that amount of whitespace from the beginning of each line. + # + #: (lex_compat_token token) -> void def <<(token) case token[1] when :on_embexpr_beg, :on_heredoc_beg @@ -310,7 +356,7 @@ def <<(token) line = token[2] if dedent_next && !(line.strip.empty? && line.end_with?("\n")) - leading = line[/\A(\s*)\n?/, 1] + leading = line[/\A(\s*)\n?/, 1] #: String next_dedent = 0 leading.each_char do |char| @@ -335,11 +381,12 @@ def <<(token) tokens << token end + #: () -> Array[lex_compat_token] def to_a # If every line in the heredoc is blank, we still need to split up the # string content token into multiple tokens. if dedent.nil? - results = [] #: Array[Token] + results = [] #: Array[lex_compat_token] embexpr_balance = 0 tokens.each do |token| @@ -374,7 +421,7 @@ def to_a # If the minimum common whitespace is 0, then we need to concatenate # string nodes together that are immediately adjacent. if dedent == 0 - results = [] #: Array[Token] + results = [] #: Array[lex_compat_token] embexpr_balance = 0 index = 0 @@ -407,7 +454,7 @@ def to_a # insert on_ignored_sp tokens for the amount of dedent that we need to # perform. We also need to remove the dedent from the beginning of # each line of plain string content tokens. - results = [] #: Array[Token] + results = [] #: Array[lex_compat_token] dedent_next = true embexpr_balance = 0 @@ -446,7 +493,8 @@ def to_a # line or this line doesn't start with whitespace, then we # should concatenate the rest of the string to match ripper. if dedent == 0 && (!dedent_next || !line.start_with?(/\s/)) - line = splits[index..].join + unjoined = splits[index..] #: Array[String] + line = unjoined.join index = splits.length end @@ -511,6 +559,8 @@ def to_a # Here we will split between the two types of heredocs and return the # object that will store their tokens. + #-- + #: (lex_compat_token opening) -> (PlainHeredoc | DashHeredoc | DedentingHeredoc) def self.build(opening) case opening[2][2] when "~" @@ -530,31 +580,38 @@ def self.build(opening) BOM_FLUSHED = RUBY_VERSION >= "3.3.0" private_constant :BOM_FLUSHED - attr_reader :options + attr_reader :options #: Hash[Symbol, untyped] + # @rbs @source: String - def initialize(code, **options) - @code = code + #: (String source, **untyped options) -> void + def initialize(source, **options) + @source = source @options = options end + #: () -> Result def result - tokens = [] #: Array[LexCompat::Token] + tokens = [] #: Array[lex_compat_token] state = :default heredoc_stack = [[]] #: Array[Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc]] - result = Prism.lex(@code, **options) + result = Prism.lex(@source, **options) source = result.source result_value = result.value - previous_state = nil #: State? + previous_state = nil #: Translation::Ripper::Lexer::State? last_heredoc_end = nil #: Integer? - eof_token = nil + eof_token = nil #: Token? bom = source.slice(0, 3) == "\xEF\xBB\xBF" - result_value.each_with_index do |(token, lex_state), index| - lineno = token.location.start_line - column = token.location.start_column + result_value.each_with_index do |(prism_token, prism_state), index| + lineno = prism_token.location.start_line + column = prism_token.location.start_column + + event = RIPPER.fetch(prism_token.type) + value = prism_token.value + lex_state = Translation::Ripper::Lexer::State[prism_state] # If there's a UTF-8 byte-order mark as the start of the file, then for # certain tokens ripper sets the first token back by 3 bytes. It also @@ -566,43 +623,38 @@ def result if index == 0 && column == 0 && !BOM_FLUSHED flushed = - case token.type + case prism_token.type when :BACK_REFERENCE, :INSTANCE_VARIABLE, :CLASS_VARIABLE, :GLOBAL_VARIABLE, :NUMBERED_REFERENCE, :PERCENT_LOWER_I, :PERCENT_LOWER_X, :PERCENT_LOWER_W, :PERCENT_UPPER_I, :PERCENT_UPPER_W, :STRING_BEGIN true when :REGEXP_BEGIN, :SYMBOL_BEGIN - token.value.start_with?("%") + value.start_with?("%") else false end unless flushed column -= 3 - value = token.value value.prepend(String.new("\xEF\xBB\xBF", encoding: value.encoding)) end end end - event = RIPPER.fetch(token.type) - value = token.value - lex_state = Translation::Ripper::Lexer::State[lex_state] - - token = + lex_compat_token = case event when :on___end__ # Ripper doesn't include the rest of the token in the event, so we need to # trim it down to just the content on the first line. - value = value[0..value.index("\n")] + value = value[0..value.index("\n")] #: String [[lineno, column], event, value, lex_state] when :on_comment [[lineno, column], event, value, lex_state] when :on_heredoc_end # Heredoc end tokens can be emitted in an odd order, so we don't # want to bother comparing the state on them. - last_heredoc_end = token.location.end_offset + last_heredoc_end = prism_token.location.end_offset [[lineno, column], event, value, lex_state] when :on_embexpr_end [[lineno, column], event, value, lex_state] @@ -615,7 +667,7 @@ def result end tokens << [[lineno, column], event, line, lex_state] end - tokens.pop + tokens.pop #: lex_compat_token when :on_regexp_end # On regex end, Ripper scans and then sets end state, so the ripper # lexed output is begin, when it should be end. prism sets lex state @@ -647,7 +699,7 @@ def result [[lineno, column], event, value, lex_state] when :on_eof - eof_token = token + eof_token = prism_token previous_token = result_value[index - 1][0] # If we're at the end of the file and the previous token was a @@ -662,7 +714,7 @@ def result # Use the greater offset of the two to determine the start of # the trailing whitespace. start_offset = [previous_token.location.end_offset, last_heredoc_end].compact.max - end_offset = token.location.start_offset + end_offset = prism_token.location.start_offset if start_offset < end_offset if bom @@ -677,7 +729,7 @@ def result [[lineno, column], event, value, lex_state] else [[lineno, column], event, value, lex_state] - end + end #: lex_compat_token previous_state = lex_state @@ -694,19 +746,19 @@ def result when :default # The default state is when there are no heredocs at all. In this # state we can append the token to the list of tokens and move on. - tokens << token + tokens << lex_compat_token # If we get the declaration of a heredoc, then we open a new heredoc # and move into the heredoc_opened state. if event == :on_heredoc_beg state = :heredoc_opened - heredoc_stack.last << Heredoc.build(token) + heredoc_stack.last << Heredoc.build(lex_compat_token) end when :heredoc_opened # The heredoc_opened state is when we've seen the declaration of a # heredoc and are now lexing the body of the heredoc. In this state we # push tokens onto the most recently created heredoc. - heredoc_stack.last.last << token + heredoc_stack.last.last << lex_compat_token case event when :on_heredoc_beg @@ -714,7 +766,7 @@ def result # heredoc, this means we have nested heredocs. In this case we'll # push a new heredoc onto the stack and stay in the heredoc_opened # state since we're now lexing the body of the new heredoc. - heredoc_stack << [Heredoc.build(token)] + heredoc_stack << [Heredoc.build(lex_compat_token)] when :on_heredoc_end # If we receive the end of a heredoc, then we're done lexing the # body of the heredoc. In this case we now have a completed heredoc @@ -723,10 +775,10 @@ def result state = :heredoc_closed end when :heredoc_closed - if %i[on_nl on_ignored_nl on_comment].include?(event) || (event == :on_tstring_content && value.end_with?("\n")) + if %i[on_nl on_ignored_nl on_comment].include?(event) || ((event == :on_tstring_content) && value.end_with?("\n")) if heredoc_stack.size > 1 - flushing = heredoc_stack.pop - heredoc_stack.last.last << token + flushing = heredoc_stack.pop #: Array[Heredoc::PlainHeredoc | Heredoc::DashHeredoc | Heredoc::DedentingHeredoc] + heredoc_stack.last.last << lex_compat_token flushing.each do |heredoc| heredoc.to_a.each do |flushed_token| @@ -738,12 +790,12 @@ def result next end elsif event == :on_heredoc_beg - tokens << token + tokens << lex_compat_token state = :heredoc_opened - heredoc_stack.last << Heredoc.build(token) + heredoc_stack.last << Heredoc.build(lex_compat_token) next elsif heredoc_stack.size > 1 - heredoc_stack[-2].last << token + heredoc_stack[-2].last << lex_compat_token next end @@ -754,13 +806,15 @@ def result heredoc_stack.last.clear state = :default - tokens << token + tokens << lex_compat_token end end # Drop the EOF token from the list. The EOF token may not be # present if the source was syntax invalid - tokens = tokens[0...-1] if tokens.dig(-1, 1) == :on_eof + if tokens.dig(-1, 1) == :on_eof + tokens = tokens[0...-1] #: Array[lex_compat_token] + end # We sort by location because Ripper.lex sorts. tokens.sort_by! do |token| @@ -775,8 +829,9 @@ def result private + #: (Array[lex_compat_token] tokens, Source source, Location? data_loc, bool bom, Token? eof_token) -> Array[lex_compat_token] def post_process_tokens(tokens, source, data_loc, bom, eof_token) - new_tokens = [] + new_tokens = [] #: Array[lex_compat_token] prev_token_state = Translation::Ripper::Lexer::State[Translation::Ripper::EXPR_BEG] prev_token_end = bom ? 3 : 0 @@ -806,8 +861,8 @@ def post_process_tokens(tokens, source, data_loc, bom, eof_token) next_whitespace_index = continuation_index + 1 next_whitespace_index += 1 if sp_value.byteslice(next_whitespace_index) == "\r" next_whitespace_index += 1 - first_whitespace = sp_value[0...continuation_index] - continuation = sp_value[continuation_index...next_whitespace_index] + first_whitespace = sp_value[0...continuation_index] #: String + continuation = sp_value[continuation_index...next_whitespace_index] #: String second_whitespace = sp_value[next_whitespace_index..] || "" new_tokens << [[sp_line, sp_column], :on_sp, first_whitespace, prev_token_state] unless first_whitespace.empty? diff --git a/lib/prism/node_ext.rb b/lib/prism/node_ext.rb index a05123d1bb3aa5..4457c26fbe04cf 100644 --- a/lib/prism/node_ext.rb +++ b/lib/prism/node_ext.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled #-- # Here we are reopening the prism module to provide methods on nodes that aren't @@ -7,9 +9,9 @@ #++ module Prism class Node + #: (*String replacements) -> void def deprecated(*replacements) # :nodoc: - location = caller_locations(1, 1) - location = location[0].label if location + location = caller_locations(1, 1)&.[](0)&.label suggest = replacements.map { |replacement| "#{self.class}##{replacement}" } warn(<<~MSG, uplevel: 1, category: :deprecated) @@ -23,7 +25,9 @@ def deprecated(*replacements) # :nodoc: module RegularExpressionOptions # :nodoc: # Returns a numeric value that represents the flags that were used to create # the regular expression. - def options + #-- + #: (Integer flags) -> Integer + def self.options(flags) o = 0 o |= Regexp::IGNORECASE if flags.anybits?(RegularExpressionFlags::IGNORE_CASE) o |= Regexp::EXTENDED if flags.anybits?(RegularExpressionFlags::EXTENDED) @@ -35,43 +39,87 @@ def options end class InterpolatedMatchLastLineNode < Node - include RegularExpressionOptions + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + #-- + #: () -> Integer + def options + RegularExpressionOptions.options(flags) + end end class InterpolatedRegularExpressionNode < Node - include RegularExpressionOptions + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + #-- + #: () -> Integer + def options + RegularExpressionOptions.options(flags) + end end class MatchLastLineNode < Node - include RegularExpressionOptions + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + #-- + #: () -> Integer + def options + RegularExpressionOptions.options(flags) + end end class RegularExpressionNode < Node - include RegularExpressionOptions + # Returns a numeric value that represents the flags that were used to create + # the regular expression. + #-- + #: () -> Integer + def options + RegularExpressionOptions.options(flags) + end end private_constant :RegularExpressionOptions module HeredocQuery # :nodoc: # Returns true if this node was represented as a heredoc in the source code. - def heredoc? + #-- + #: (String? opening) -> bool? + def self.heredoc?(opening) + # @type self: InterpolatedStringNode | InterpolatedXStringNode | StringNode | XStringNode opening&.start_with?("<<") end end class InterpolatedStringNode < Node - include HeredocQuery + # Returns true if this node was represented as a heredoc in the source code. + #-- + #: () -> bool? + def heredoc? + HeredocQuery.heredoc?(opening) + end end class InterpolatedXStringNode < Node - include HeredocQuery + # Returns true if this node was represented as a heredoc in the source code. + #-- + #: () -> bool? + def heredoc? + HeredocQuery.heredoc?(opening) + end end class StringNode < Node - include HeredocQuery + # Returns true if this node was represented as a heredoc in the source code. + #-- + #: () -> bool? + def heredoc? + HeredocQuery.heredoc?(opening) + end # Occasionally it's helpful to treat a string as if it were interpolated so # that there's a consistent interface for working with strings. + #-- + #: () -> InterpolatedStringNode def to_interpolated InterpolatedStringNode.new( source, @@ -86,10 +134,17 @@ def to_interpolated end class XStringNode < Node - include HeredocQuery + # Returns true if this node was represented as a heredoc in the source code. + #-- + #: () -> bool? + def heredoc? + HeredocQuery.heredoc?(opening) + end # Occasionally it's helpful to treat a string as if it were interpolated so # that there's a consistent interface for working with strings. + #-- + #: () -> InterpolatedXStringNode def to_interpolated InterpolatedXStringNode.new( source, @@ -107,6 +162,8 @@ def to_interpolated class ImaginaryNode < Node # Returns the value of the node as a Ruby Complex. + #-- + #: () -> Complex def value Complex(0, numeric.value) end @@ -114,12 +171,16 @@ def value class RationalNode < Node # Returns the value of the node as a Ruby Rational. + #-- + #: () -> Rational def value Rational(numerator, denominator) end # Returns the value of the node as an IntegerNode or a FloatNode. This # method is deprecated in favor of #value or #numerator/#denominator. + #-- + #: () -> (IntegerNode | FloatNode) def numeric deprecated("value", "numerator", "denominator") @@ -134,11 +195,15 @@ def numeric class ConstantReadNode < Node # Returns the list of parts for the full name of this constant. # For example: [:Foo] + #-- + #: () -> Array[Symbol] def full_name_parts [name] end # Returns the full name of this constant. For example: "Foo" + #-- + #: () -> String def full_name name.to_s end @@ -147,11 +212,15 @@ def full_name class ConstantWriteNode < Node # Returns the list of parts for the full name of this constant. # For example: [:Foo] + #-- + #: () -> Array[Symbol] def full_name_parts [name] end # Returns the full name of this constant. For example: "Foo" + #-- + #: () -> String def full_name name.to_s end @@ -173,6 +242,8 @@ class MissingNodesInConstantPathError < StandardError; end # Returns the list of parts for the full name of this constant path. # For example: [:Foo, :Bar] + #-- + #: () -> Array[Symbol] def full_name_parts parts = [] #: Array[Symbol] current = self #: node? @@ -195,6 +266,8 @@ def full_name_parts end # Returns the full name of this constant path. For example: "Foo::Bar" + #-- + #: () -> String def full_name full_name_parts.join("::") end @@ -202,10 +275,12 @@ def full_name # Previously, we had a child node on this class that contained either a # constant read or a missing node. To not cause a breaking change, we # continue to supply that API. + #-- + #: () -> (ConstantReadNode | MissingNode) def child deprecated("name", "name_loc") - if name + if (name = self.name) ConstantReadNode.new(source, -1, name_loc, 0, name) else MissingNode.new(source, -1, location, 0) @@ -216,9 +291,11 @@ def child class ConstantPathTargetNode < Node # Returns the list of parts for the full name of this constant path. # For example: [:Foo, :Bar] + #-- + #: () -> Array[Symbol] def full_name_parts parts = - case parent + case (parent = self.parent) when ConstantPathNode, ConstantReadNode parent.full_name_parts when nil @@ -228,7 +305,7 @@ def full_name_parts raise ConstantPathNode::DynamicPartsInConstantPathError, "Constant target path contains dynamic parts. Cannot compute full name" end - if name.nil? + if (name = self.name).nil? raise ConstantPathNode::MissingNodesInConstantPathError, "Constant target path contains missing nodes. Cannot compute full name" end @@ -236,6 +313,8 @@ def full_name_parts end # Returns the full name of this constant path. For example: "Foo::Bar" + #-- + #: () -> String def full_name full_name_parts.join("::") end @@ -243,10 +322,12 @@ def full_name # Previously, we had a child node on this class that contained either a # constant read or a missing node. To not cause a breaking change, we # continue to supply that API. + #-- + #: () -> (ConstantReadNode | MissingNode) def child deprecated("name", "name_loc") - if name + if (name = self.name) ConstantReadNode.new(source, -1, name_loc, 0, name) else MissingNode.new(source, -1, location, 0) @@ -257,11 +338,15 @@ def child class ConstantTargetNode < Node # Returns the list of parts for the full name of this constant. # For example: [:Foo] + #-- + #: () -> Array[Symbol] def full_name_parts [name] end # Returns the full name of this constant. For example: "Foo" + #-- + #: () -> String def full_name name.to_s end @@ -269,6 +354,8 @@ def full_name class ParametersNode < Node # Mirrors the Method#parameters method. + #-- + #: () -> Array[[Symbol, Symbol] | [Symbol]] def signature names = [] #: Array[[Symbol, Symbol] | [Symbol]] @@ -278,7 +365,7 @@ def signature optionals.each { |param| names << [:opt, param.name] } - if rest && rest.is_a?(RestParameterNode) + if (rest = self.rest).is_a?(RestParameterNode) names << [:rest, rest.name || :*] end @@ -309,7 +396,7 @@ def signature keyopt.each { |param| names << [:key, param.name] } - case keyword_rest + case (keyword_rest = self.keyword_rest) when ForwardingParameterNode names.concat([[:rest, :*], [:keyrest, :**], [:block, :&]]) when KeywordRestParameterNode @@ -318,7 +405,7 @@ def signature names << [:nokey] end - case block + case (block = self.block) when BlockParameterNode names << [:block, block.name || :&] when NoBlockParameterNode @@ -339,6 +426,8 @@ class CallNode < Node # can be any amount of space between the message and the = sign. However, # sometimes you want the location of the full message including the inner # space and the = sign. This method provides that. + #-- + #: () -> Location? def full_message_loc attribute_write? ? message_loc&.adjoin("=") : message_loc end @@ -347,6 +436,8 @@ def full_message_loc class CallOperatorWriteNode < Node # Returns the binary operator used to modify the receiver. This method is # deprecated in favor of #binary_operator. + #-- + #: () -> Symbol def operator deprecated("binary_operator") binary_operator @@ -354,6 +445,8 @@ def operator # Returns the location of the binary operator used to modify the receiver. # This method is deprecated in favor of #binary_operator_loc. + #-- + #: () -> Location def operator_loc deprecated("binary_operator_loc") binary_operator_loc @@ -363,6 +456,8 @@ def operator_loc class ClassVariableOperatorWriteNode < Node # Returns the binary operator used to modify the receiver. This method is # deprecated in favor of #binary_operator. + #-- + #: () -> Symbol def operator deprecated("binary_operator") binary_operator @@ -370,6 +465,8 @@ def operator # Returns the location of the binary operator used to modify the receiver. # This method is deprecated in favor of #binary_operator_loc. + #-- + #: () -> Location def operator_loc deprecated("binary_operator_loc") binary_operator_loc @@ -379,6 +476,8 @@ def operator_loc class ConstantOperatorWriteNode < Node # Returns the binary operator used to modify the receiver. This method is # deprecated in favor of #binary_operator. + #-- + #: () -> Symbol def operator deprecated("binary_operator") binary_operator @@ -386,6 +485,8 @@ def operator # Returns the location of the binary operator used to modify the receiver. # This method is deprecated in favor of #binary_operator_loc. + #-- + #: () -> Location def operator_loc deprecated("binary_operator_loc") binary_operator_loc @@ -395,6 +496,8 @@ def operator_loc class ConstantPathOperatorWriteNode < Node # Returns the binary operator used to modify the receiver. This method is # deprecated in favor of #binary_operator. + #-- + #: () -> Symbol def operator deprecated("binary_operator") binary_operator @@ -402,6 +505,8 @@ def operator # Returns the location of the binary operator used to modify the receiver. # This method is deprecated in favor of #binary_operator_loc. + #-- + #: () -> Location def operator_loc deprecated("binary_operator_loc") binary_operator_loc @@ -411,6 +516,8 @@ def operator_loc class GlobalVariableOperatorWriteNode < Node # Returns the binary operator used to modify the receiver. This method is # deprecated in favor of #binary_operator. + #-- + #: () -> Symbol def operator deprecated("binary_operator") binary_operator @@ -418,6 +525,8 @@ def operator # Returns the location of the binary operator used to modify the receiver. # This method is deprecated in favor of #binary_operator_loc. + #-- + #: () -> Location def operator_loc deprecated("binary_operator_loc") binary_operator_loc @@ -427,6 +536,8 @@ def operator_loc class IndexOperatorWriteNode < Node # Returns the binary operator used to modify the receiver. This method is # deprecated in favor of #binary_operator. + #-- + #: () -> Symbol def operator deprecated("binary_operator") binary_operator @@ -434,6 +545,8 @@ def operator # Returns the location of the binary operator used to modify the receiver. # This method is deprecated in favor of #binary_operator_loc. + #-- + #: () -> Location def operator_loc deprecated("binary_operator_loc") binary_operator_loc @@ -443,6 +556,8 @@ def operator_loc class InstanceVariableOperatorWriteNode < Node # Returns the binary operator used to modify the receiver. This method is # deprecated in favor of #binary_operator. + #-- + #: () -> Symbol def operator deprecated("binary_operator") binary_operator @@ -450,6 +565,8 @@ def operator # Returns the location of the binary operator used to modify the receiver. # This method is deprecated in favor of #binary_operator_loc. + #-- + #: () -> Location def operator_loc deprecated("binary_operator_loc") binary_operator_loc @@ -459,6 +576,8 @@ def operator_loc class LocalVariableOperatorWriteNode < Node # Returns the binary operator used to modify the receiver. This method is # deprecated in favor of #binary_operator. + #-- + #: () -> Symbol def operator deprecated("binary_operator") binary_operator @@ -466,6 +585,8 @@ def operator # Returns the location of the binary operator used to modify the receiver. # This method is deprecated in favor of #binary_operator_loc. + #-- + #: () -> Location def operator_loc deprecated("binary_operator_loc") binary_operator_loc @@ -475,6 +596,8 @@ def operator_loc class CaseMatchNode < Node # Returns the else clause of the case match node. This method is deprecated # in favor of #else_clause. + #-- + #: () -> ElseNode? def consequent deprecated("else_clause") else_clause @@ -484,6 +607,8 @@ def consequent class CaseNode < Node # Returns the else clause of the case node. This method is deprecated in # favor of #else_clause. + #-- + #: () -> ElseNode? def consequent deprecated("else_clause") else_clause @@ -493,6 +618,8 @@ def consequent class IfNode < Node # Returns the subsequent if/elsif/else clause of the if node. This method is # deprecated in favor of #subsequent. + #-- + #: () -> (IfNode | ElseNode)? def consequent deprecated("subsequent") subsequent @@ -502,6 +629,8 @@ def consequent class RescueNode < Node # Returns the subsequent rescue clause of the rescue node. This method is # deprecated in favor of #subsequent. + #-- + #: () -> RescueNode? def consequent deprecated("subsequent") subsequent @@ -511,6 +640,8 @@ def consequent class UnlessNode < Node # Returns the else clause of the unless node. This method is deprecated in # favor of #else_clause. + #-- + #: () -> ElseNode? def consequent deprecated("else_clause") else_clause diff --git a/lib/prism/parse_result.rb b/lib/prism/parse_result.rb index 07529c4295cc87..9825d559aff9ba 100644 --- a/lib/prism/parse_result.rb +++ b/lib/prism/parse_result.rb @@ -1,7 +1,16 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism + # @rbs! + # # An internal interface for a cache that can be used to compute code + # # units from byte offsets. + # interface _CodeUnitsCache + # def []: (Integer byte_offset) -> Integer + # end + # This represents a source of Ruby code that has been parsed. It is used in # conjunction with locations to allow them to resolve line numbers and source # ranges. @@ -10,6 +19,8 @@ class Source # be used instead of `new` and it will return either a `Source` or a # specialized and more performant `ASCIISource` if no multibyte characters # are present in the source code. + #-- + #: (String source, ?Integer start_line, ?Array[Integer] offsets) -> Source def self.for(source, start_line = 1, offsets = []) if source.ascii_only? ASCIISource.new(source, start_line, offsets) @@ -34,15 +45,17 @@ def self.for(source, start_line = 1, offsets = []) end # The source code that this source object represents. - attr_reader :source + attr_reader :source #: String # The line number where this source starts. - attr_reader :start_line + attr_reader :start_line #: Integer # The list of newline byte offsets in the source code. - attr_reader :offsets + attr_reader :offsets #: Array[Integer] # Create a new source object with the given source code. + #-- + #: (String source, ?Integer start_line, ?Array[Integer] offsets) -> void def initialize(source, start_line = 1, offsets = []) @source = source @start_line = start_line # set after parsing is done @@ -50,33 +63,45 @@ def initialize(source, start_line = 1, offsets = []) end # Replace the value of start_line with the given value. + #-- + #: (Integer start_line) -> void def replace_start_line(start_line) @start_line = start_line end # Replace the value of offsets with the given value. + #-- + #: (Array[Integer] offsets) -> void def replace_offsets(offsets) @offsets.replace(offsets) end # Returns the encoding of the source code, which is set by parameters to the # parser or by the encoding magic comment. + #-- + #: () -> Encoding def encoding source.encoding end # Returns the lines of the source code as an array of strings. + #-- + #: () -> Array[String] def lines source.lines end # Perform a byteslice on the source code using the given byte offset and # byte length. + #-- + #: (Integer byte_offset, Integer length) -> String def slice(byte_offset, length) source.byteslice(byte_offset, length) or raise end # Converts the line number and column in bytes to a byte offset. + #-- + #: (Integer line, Integer column) -> Integer def byte_offset(line, column) normal = line - @start_line raise IndexError if normal < 0 @@ -87,33 +112,45 @@ def byte_offset(line, column) # Binary search through the offsets to find the line number for the given # byte offset. + #-- + #: (Integer byte_offset) -> Integer def line(byte_offset) start_line + find_line(byte_offset) end # Return the byte offset of the start of the line corresponding to the given # byte offset. + #-- + #: (Integer byte_offset) -> Integer def line_start(byte_offset) offsets[find_line(byte_offset)] end # Returns the byte offset of the end of the line corresponding to the given # byte offset. + #-- + #: (Integer byte_offset) -> Integer def line_end(byte_offset) offsets[find_line(byte_offset) + 1] || source.bytesize end # Return the column in bytes for the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def column(byte_offset) byte_offset - line_start(byte_offset) end # Return the character offset for the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def character_offset(byte_offset) (source.byteslice(0, byte_offset) or raise).length end # Return the column in characters for the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def character_column(byte_offset) character_offset(byte_offset) - character_offset(line_start(byte_offset)) end @@ -130,6 +167,8 @@ def character_column(byte_offset) # possible that the given byte offset will not occur on a character # boundary. Second, it's possible that the source code will contain a # character that has no equivalent in the given encoding. + #-- + #: (Integer byte_offset, Encoding encoding) -> Integer def code_units_offset(byte_offset, encoding) byteslice = (source.byteslice(0, byte_offset) or raise).encode(encoding, invalid: :replace, undef: :replace) @@ -142,17 +181,23 @@ def code_units_offset(byte_offset, encoding) # Generate a cache that targets a specific encoding for calculating code # unit offsets. + #-- + #: (Encoding encoding) -> CodeUnitsCache def code_units_cache(encoding) CodeUnitsCache.new(source, encoding) end # Returns the column in code units for the given encoding for the # given byte offset. + #-- + #: (Integer byte_offset, Encoding encoding) -> Integer def code_units_column(byte_offset, encoding) code_units_offset(byte_offset, encoding) - code_units_offset(line_start(byte_offset), encoding) end # Freeze this object and the objects it contains. + #-- + #: () -> void def deep_freeze source.freeze offsets.freeze @@ -163,6 +208,8 @@ def deep_freeze # Binary search through the offsets to find the line number for the given # byte offset. + #-- + #: (Integer byte_offset) -> Integer def find_line(byte_offset) # :nodoc: index = offsets.bsearch_index { |offset| offset > byte_offset } || offsets.length index - 1 @@ -185,30 +232,47 @@ def find_line(byte_offset) # :nodoc: # class CodeUnitsCache class UTF16Counter # :nodoc: + # @rbs @source: String + # @rbs @encoding: Encoding + + #: (String source, Encoding encoding) -> void def initialize(source, encoding) @source = source @encoding = encoding end + #: (Integer byte_offset, Integer byte_length) -> Integer def count(byte_offset, byte_length) - @source.byteslice(byte_offset, byte_length).encode(@encoding, invalid: :replace, undef: :replace).bytesize / 2 + (@source.byteslice(byte_offset, byte_length) or raise).encode(@encoding, invalid: :replace, undef: :replace).bytesize / 2 end end class LengthCounter # :nodoc: + # @rbs @source: String + # @rbs @encoding: Encoding + + #: (String source, Encoding encoding) -> void def initialize(source, encoding) @source = source @encoding = encoding end + #: (Integer byte_offset, Integer byte_length) -> Integer def count(byte_offset, byte_length) - @source.byteslice(byte_offset, byte_length).encode(@encoding, invalid: :replace, undef: :replace).length + (@source.byteslice(byte_offset, byte_length) or raise).encode(@encoding, invalid: :replace, undef: :replace).length end end private_constant :UTF16Counter, :LengthCounter + # @rbs @source: String + # @rbs @counter: UTF16Counter | LengthCounter + # @rbs @cache: Hash[Integer, Integer] + # @rbs @offsets: Array[Integer] + # Initialize a new cache with the given source and encoding. + #-- + #: (String source, Encoding encoding) -> void def initialize(source, encoding) @source = source @counter = @@ -223,6 +287,8 @@ def initialize(source, encoding) end # Retrieve the code units offset from the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def [](byte_offset) @cache[byte_offset] ||= if (index = @offsets.bsearch_index { |offset| offset > byte_offset }).nil? @@ -249,11 +315,15 @@ def [](byte_offset) # at that point we will treat everything as single-byte characters. class ASCIISource < Source # Return the character offset for the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def character_offset(byte_offset) byte_offset end # Return the column in characters for the given byte offset. + #-- + #: (Integer byte_offset) -> Integer def character_column(byte_offset) byte_offset - line_start(byte_offset) end @@ -264,6 +334,8 @@ def character_column(byte_offset) # This method is tested with UTF-8, UTF-16, and UTF-32. If there is the # concept of code units that differs from the number of characters in other # encodings, it is not captured here. + #-- + #: (Integer byte_offset, Encoding encoding) -> Integer def code_units_offset(byte_offset, encoding) byte_offset end @@ -271,6 +343,8 @@ def code_units_offset(byte_offset, encoding) # Returns a cache that is the identity function in order to maintain the # same interface. We can do this because code units are always equivalent to # byte offsets for ASCII-only sources. + #-- + #: (Encoding encoding) -> _CodeUnitsCache def code_units_cache(encoding) ->(byte_offset) { byte_offset } end @@ -278,6 +352,8 @@ def code_units_cache(encoding) # Specialized version of `code_units_column` that does not depend on # `code_units_offset`, which is a more expensive operation. This is # essentially the same as `Prism::Source#column`. + #-- + #: (Integer byte_offset, Encoding encoding) -> Integer def code_units_column(byte_offset, encoding) byte_offset - line_start(byte_offset) end @@ -287,18 +363,23 @@ def code_units_column(byte_offset, encoding) class Location # A Source object that is used to determine more information from the given # offset and length. - attr_reader :source + attr_reader :source #: Source protected :source # The byte offset from the beginning of the source where this location # starts. - attr_reader :start_offset + attr_reader :start_offset #: Integer # The length of this location in bytes. - attr_reader :length + attr_reader :length #: Integer + + # @rbs @leading_comments: Array[Comment]? + # @rbs @trailing_comments: Array[Comment]? # Create a new location object with the given source, start byte offset, and # byte length. + #-- + #: (Source source, Integer start_offset, Integer length) -> void def initialize(source, start_offset, length) @source = source @start_offset = start_offset @@ -313,53 +394,73 @@ def initialize(source, start_offset, length) # These are the comments that are associated with this location that exist # before the start of this location. + #-- + #: () -> Array[Comment] def leading_comments @leading_comments ||= [] end # Attach a comment to the leading comments of this location. + #-- + #: (Comment comment) -> void def leading_comment(comment) leading_comments << comment end # These are the comments that are associated with this location that exist # after the end of this location. + #-- + #: () -> Array[Comment] def trailing_comments @trailing_comments ||= [] end # Attach a comment to the trailing comments of this location. + #-- + #: (Comment comment) -> void def trailing_comment(comment) trailing_comments << comment end # Returns all comments that are associated with this location (both leading # and trailing comments). + #-- + #: () -> Array[Comment] def comments - [*@leading_comments, *@trailing_comments] + [*@leading_comments, *@trailing_comments] #: Array[Comment] end # Create a new location object with the given options. + #-- + #: (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location def copy(source: self.source, start_offset: self.start_offset, length: self.length) Location.new(source, start_offset, length) end # Returns a new location that is the result of chopping off the last byte. + #-- + #: () -> Location def chop copy(length: length == 0 ? length : length - 1) end # Returns a string representation of this location. + #-- + #: () -> String def inspect # :nodoc: "#" end # Returns all of the lines of the source code associated with this location. + #-- + #: () -> Array[String] def source_lines source.lines end # The source code that this location represents. + #-- + #: () -> String def slice source.slice(start_offset, length) end @@ -367,6 +468,8 @@ def slice # The source code that this location represents starting from the beginning # of the line that this location starts on to the end of the line that this # location ends on. + #-- + #: () -> String def slice_lines line_start = source.line_start(start_offset) line_end = source.line_end(end_offset) @@ -375,118 +478,160 @@ def slice_lines # The character offset from the beginning of the source where this location # starts. + #-- + #: () -> Integer def start_character_offset source.character_offset(start_offset) end # The offset from the start of the file in code units of the given encoding. + #-- + #: (Encoding encoding) -> Integer def start_code_units_offset(encoding = Encoding::UTF_16LE) source.code_units_offset(start_offset, encoding) end # The start offset from the start of the file in code units using the given # cache to fetch or calculate the value. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_start_code_units_offset(cache) cache[start_offset] end # The byte offset from the beginning of the source where this location ends. + #-- + #: () -> Integer def end_offset start_offset + length end # The character offset from the beginning of the source where this location # ends. + #-- + #: () -> Integer def end_character_offset source.character_offset(end_offset) end # The offset from the start of the file in code units of the given encoding. + #-- + #: (Encoding encoding) -> Integer def end_code_units_offset(encoding = Encoding::UTF_16LE) source.code_units_offset(end_offset, encoding) end # The end offset from the start of the file in code units using the given # cache to fetch or calculate the value. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_end_code_units_offset(cache) cache[end_offset] end # The line number where this location starts. + #-- + #: () -> Integer def start_line source.line(start_offset) end # The content of the line where this location starts before this location. + #-- + #: () -> String def start_line_slice offset = source.line_start(start_offset) source.slice(offset, start_offset - offset) end # The line number where this location ends. + #-- + #: () -> Integer def end_line source.line(end_offset) end # The column in bytes where this location starts from the start of # the line. + #-- + #: () -> Integer def start_column source.column(start_offset) end # The column in characters where this location ends from the start of # the line. + #-- + #: () -> Integer def start_character_column source.character_column(start_offset) end # The column in code units of the given encoding where this location # starts from the start of the line. + #-- + #: (?Encoding encoding) -> Integer def start_code_units_column(encoding = Encoding::UTF_16LE) source.code_units_column(start_offset, encoding) end # The start column in code units using the given cache to fetch or calculate # the value. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_start_code_units_column(cache) cache[start_offset] - cache[source.line_start(start_offset)] end # The column in bytes where this location ends from the start of the # line. + #-- + #: () -> Integer def end_column source.column(end_offset) end # The column in characters where this location ends from the start of # the line. + #-- + #: () -> Integer def end_character_column source.character_column(end_offset) end # The column in code units of the given encoding where this location # ends from the start of the line. + #-- + #: (?Encoding encoding) -> Integer def end_code_units_column(encoding = Encoding::UTF_16LE) source.code_units_column(end_offset, encoding) end # The end column in code units using the given cache to fetch or calculate # the value. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_end_code_units_column(cache) cache[end_offset] - cache[source.line_start(end_offset)] end # Implement the hash pattern matching interface for Location. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: { start_offset: start_offset, end_offset: end_offset } end # Implement the pretty print interface for Location. + #-- + #: (PP q) -> void def pretty_print(q) # :nodoc: q.text("(#{start_line},#{start_column})-(#{end_line},#{end_column})") end # Returns true if the given other location is equal to this location. + #-- + #: (untyped other) -> bool def ==(other) Location === other && other.start_offset == start_offset && @@ -496,6 +641,8 @@ def ==(other) # Returns a new location that stretches from this location to the given # other location. Raises an error if this location is not before the other # location or if they don't share the same source. + #-- + #: (Location other) -> Location def join(other) raise "Incompatible sources" if source != other.source raise "Incompatible locations" if start_offset > other.start_offset @@ -506,6 +653,8 @@ def join(other) # Join this location with the first occurrence of the string in the source # that occurs after this location on the same line, and return the new # location. This will raise an error if the string does not exist. + #-- + #: (String string) -> Location def adjoin(string) line_suffix = source.slice(end_offset, source.line_end(end_offset) - end_offset) @@ -520,22 +669,37 @@ def adjoin(string) # base class for all comment types. class Comment # The Location of this comment in the source. - attr_reader :location + attr_reader :location #: Location # Create a new comment object with the given location. + #-- + #: (Location location) -> void def initialize(location) @location = location end # Implement the hash pattern matching interface for Comment. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: { location: location } end # Returns the content of the comment by slicing it from the source code. + #-- + #: () -> String def slice location.slice end + + # Returns true if this comment happens on the same line as other code and + # false if the comment is by itself. This can only be true for inline + # comments and should be false for block comments. + #-- + #: () -> bool + def trailing? + raise NotImplementedError, "trailing? is not implemented for #{self.class}" + end end # InlineComment objects are the most common. They correspond to comments in @@ -543,11 +707,15 @@ def slice class InlineComment < Comment # Returns true if this comment happens on the same line as other code and # false if the comment is by itself. + #-- + #: () -> bool def trailing? !location.start_line_slice.strip.empty? end # Returns a string representation of this comment. + #-- + #: () -> String def inspect # :nodoc: "#" end @@ -557,11 +725,15 @@ def inspect # :nodoc: # and =end. class EmbDocComment < Comment # Returns false. This can only be true for inline comments. + #-- + #: () -> bool def trailing? false end # Returns a string representation of this comment. + #-- + #: () -> String def inspect # :nodoc: "#" end @@ -570,33 +742,43 @@ def inspect # :nodoc: # This represents a magic comment that was encountered during parsing. class MagicComment # A Location object representing the location of the key in the source. - attr_reader :key_loc + attr_reader :key_loc #: Location # A Location object representing the location of the value in the source. - attr_reader :value_loc + attr_reader :value_loc #: Location # Create a new magic comment object with the given key and value locations. + #-- + #: (Location key_loc, Location value_loc) -> void def initialize(key_loc, value_loc) @key_loc = key_loc @value_loc = value_loc end # Returns the key of the magic comment by slicing it from the source code. + #-- + #: () -> String def key key_loc.slice end # Returns the value of the magic comment by slicing it from the source code. + #-- + #: () -> String def value value_loc.slice end # Implement the hash pattern matching interface for MagicComment. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: { key_loc: key_loc, value_loc: value_loc } end # Returns a string representation of this magic comment. + #-- + #: () -> String def inspect # :nodoc: "#" end @@ -606,18 +788,20 @@ def inspect # :nodoc: class ParseError # The type of error. This is an _internal_ symbol that is used for # communicating with translation layers. It is not meant to be public API. - attr_reader :type + attr_reader :type #: Symbol # The message associated with this error. - attr_reader :message + attr_reader :message #: String # A Location object representing the location of this error in the source. - attr_reader :location + attr_reader :location #: Location # The level of this error. - attr_reader :level + attr_reader :level #: Symbol # Create a new error object with the given message and location. + #-- + #: (Symbol type, String message, Location location, Symbol level) -> void def initialize(type, message, location, level) @type = type @message = message @@ -626,11 +810,15 @@ def initialize(type, message, location, level) end # Implement the hash pattern matching interface for ParseError. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: { type: type, message: message, location: location, level: level } end # Returns a string representation of this error. + #-- + #: () -> String def inspect # :nodoc: "#" end @@ -640,18 +828,20 @@ def inspect # :nodoc: class ParseWarning # The type of warning. This is an _internal_ symbol that is used for # communicating with translation layers. It is not meant to be public API. - attr_reader :type + attr_reader :type #: Symbol # The message associated with this warning. - attr_reader :message + attr_reader :message #: String # A Location object representing the location of this warning in the source. - attr_reader :location + attr_reader :location #: Location # The level of this warning. - attr_reader :level + attr_reader :level #: Symbol # Create a new warning object with the given message and location. + #-- + #: (Symbol type, String message, Location location, Symbol level) -> void def initialize(type, message, location, level) @type = type @message = message @@ -660,11 +850,15 @@ def initialize(type, message, location, level) end # Implement the hash pattern matching interface for ParseWarning. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: { type: type, message: message, location: location, level: level } end # Returns a string representation of this warning. + #-- + #: () -> String def inspect # :nodoc: "#" end @@ -675,26 +869,28 @@ def inspect # :nodoc: # and any errors that were encountered. class Result # The list of comments that were encountered during parsing. - attr_reader :comments + attr_reader :comments #: Array[Comment] # The list of magic comments that were encountered during parsing. - attr_reader :magic_comments + attr_reader :magic_comments #: Array[MagicComment] # An optional location that represents the location of the __END__ marker # and the rest of the content of the file. This content is loaded into the # DATA constant when the file being parsed is the main file being executed. - attr_reader :data_loc + attr_reader :data_loc #: Location? # The list of errors that were generated during parsing. - attr_reader :errors + attr_reader :errors #: Array[ParseError] # The list of warnings that were generated during parsing. - attr_reader :warnings + attr_reader :warnings #: Array[ParseWarning] # A Source instance that represents the source code that was parsed. - attr_reader :source + attr_reader :source #: Source # Create a new result object with the given values. + #-- + #: (Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void def initialize(comments, magic_comments, data_loc, errors, warnings, source) @comments = comments @magic_comments = magic_comments @@ -705,28 +901,38 @@ def initialize(comments, magic_comments, data_loc, errors, warnings, source) end # Implement the hash pattern matching interface for Result. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: { comments: comments, magic_comments: magic_comments, data_loc: data_loc, errors: errors, warnings: warnings } end # Returns the encoding of the source code that was parsed. + #-- + #: () -> Encoding def encoding source.encoding end # Returns true if there were no errors during parsing and false if there # were. + #-- + #: () -> bool def success? errors.empty? end # Returns true if there were errors during parsing and false if there were # not. + #-- + #: () -> bool def failure? !success? end # Create a code units cache for the given encoding. + #-- + #: (Encoding encoding) -> _CodeUnitsCache def code_units_cache(encoding) source.code_units_cache(encoding) end @@ -743,32 +949,42 @@ class ParseResult < Result private_constant :Newlines # The syntax tree that was parsed from the source code. - attr_reader :value + attr_reader :value #: ProgramNode # Create a new parse result object with the given values. + #-- + #: (ProgramNode value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) @value = value super(comments, magic_comments, data_loc, errors, warnings, source) end # Implement the hash pattern matching interface for ParseResult. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: super.merge!(value: value) end # Attach the list of comments to their respective locations in the tree. + #-- + #: () -> void def attach_comments! Comments.new(self).attach! # steep:ignore end # Walk the tree and mark nodes that are on a new line, loosely emulating # the behavior of CRuby's `:line` tracepoint event. + #-- + #: () -> void def mark_newlines! value.accept(Newlines.new(source.offsets.size)) # steep:ignore end # Returns a string representation of the syntax tree with the errors # displayed inline. + #-- + #: () -> String def errors_format Errors.new(self).format end @@ -777,15 +993,19 @@ def errors_format # This is a result specific to the `lex` and `lex_file` methods. class LexResult < Result # The list of tokens that were parsed from the source code. - attr_reader :value + attr_reader :value #: Array[[Token, Integer]] # Create a new lex result object with the given values. + #-- + #: (Array[[Token, Integer]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) @value = value super(comments, magic_comments, data_loc, errors, warnings, source) end # Implement the hash pattern matching interface for LexResult. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: super.merge!(value: value) end @@ -795,15 +1015,19 @@ def deconstruct_keys(keys) # :nodoc: class ParseLexResult < Result # A tuple of the syntax tree and the list of tokens that were parsed from # the source code. - attr_reader :value + attr_reader :value #: [ProgramNode, Array[[Token, Integer]]] # Create a new parse lex result object with the given values. + #-- + #: ([ProgramNode, Array[[Token, Integer]]] value, Array[Comment] comments, Array[MagicComment] magic_comments, Location? data_loc, Array[ParseError] errors, Array[ParseWarning] warnings, Source source) -> void def initialize(value, comments, magic_comments, data_loc, errors, warnings, source) @value = value super(comments, magic_comments, data_loc, errors, warnings, source) end # Implement the hash pattern matching interface for ParseLexResult. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: super.merge!(value: value) end @@ -812,16 +1036,20 @@ def deconstruct_keys(keys) # :nodoc: # This represents a token from the Ruby source. class Token # The Source object that represents the source this token came from. - attr_reader :source + attr_reader :source #: Source private :source # The type of token that this token is. - attr_reader :type + attr_reader :type #: Symbol # A byteslice of the source that this token represents. - attr_reader :value + attr_reader :value #: String + + # @rbs @location: Location | Integer # Create a new token object with the given type, value, and location. + #-- + #: (Source source, Symbol type, String value, Location | Integer location) -> void def initialize(source, type, value, location) @source = source @type = type @@ -830,11 +1058,15 @@ def initialize(source, type, value, location) end # Implement the hash pattern matching interface for Token. + #-- + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: { type: type, value: value, location: location } end # A Location object representing the location of this token in the source. + #-- + #: () -> Location def location location = @location return location if location.is_a?(Location) @@ -842,6 +1074,8 @@ def location end # Implement the pretty print interface for Token. + #-- + #: (PP q) -> void def pretty_print(q) # :nodoc: q.group do q.text(type.to_s) @@ -857,6 +1091,8 @@ def pretty_print(q) # :nodoc: end # Returns true if the given other token is equal to this token. + #-- + #: (untyped other) -> bool def ==(other) Token === other && other.type == type && @@ -864,12 +1100,16 @@ def ==(other) end # Returns a string representation of this token. + #-- + #: () -> String def inspect # :nodoc: location super end # Freeze this object and the objects it contains. + #-- + #: () -> void def deep_freeze value.freeze location.freeze @@ -884,14 +1124,16 @@ def deep_freeze class Scope # The list of local variables that are defined in this scope. This should be # defined as an array of symbols. - attr_reader :locals + attr_reader :locals #: Array[Symbol] # The list of local variables that are forwarded to the next scope. This # should by defined as an array of symbols containing the specific values of # :*, :**, :&, or :"...". - attr_reader :forwarding + attr_reader :forwarding #: Array[Symbol] # Create a new scope object with the given locals and forwarding. + #-- + #: (Array[Symbol] locals, Array[Symbol] forwarding) -> void def initialize(locals, forwarding) @locals = locals @forwarding = forwarding @@ -901,6 +1143,8 @@ def initialize(locals, forwarding) # Create a new scope with the given locals and forwarding options that is # suitable for passing into one of the Prism.* methods that accepts the # `scopes` option. + #-- + #: (?locals: Array[Symbol], ?forwarding: Array[Symbol]) -> Scope def self.scope(locals: [], forwarding: []) Scope.new(locals, forwarding) end diff --git a/lib/prism/parse_result/comments.rb b/lib/prism/parse_result/comments.rb index 3e93316aff8662..df80792d39cd0e 100644 --- a/lib/prism/parse_result/comments.rb +++ b/lib/prism/parse_result/comments.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism class ParseResult < Result @@ -18,32 +20,49 @@ class ParseResult < Result # the comment. Otherwise it will favor attaching to the nearest location # that is after the comment. class Comments + # @rbs! + # # An internal interface for a target that comments can be attached + # # to. This is either going to be a NodeTarget or a CommentTarget. + # interface _CommentTarget + # def start_offset: () -> Integer + # def end_offset: () -> Integer + # def encloses?: (Comment) -> bool + # def leading_comment: (Comment) -> void + # def trailing_comment: (Comment) -> void + # end + # A target for attaching comments that is based on a specific node's # location. class NodeTarget # :nodoc: - attr_reader :node + attr_reader :node #: node + #: (node node) -> void def initialize(node) @node = node end + #: () -> Integer def start_offset node.start_offset end + #: () -> Integer def end_offset node.end_offset end + #: (Comment comment) -> bool def encloses?(comment) start_offset <= comment.location.start_offset && comment.location.end_offset <= end_offset end + #: (Comment comment) -> void def leading_comment(comment) node.location.leading_comment(comment) end + #: (Comment comment) -> void def trailing_comment(comment) node.location.trailing_comment(comment) end @@ -52,44 +71,54 @@ def trailing_comment(comment) # A target for attaching comments that is based on a location field on a # node. For example, the `end` token of a ClassNode. class LocationTarget # :nodoc: - attr_reader :location + attr_reader :location #: Location + #: (Location location) -> void def initialize(location) @location = location end + #: () -> Integer def start_offset location.start_offset end + #: () -> Integer def end_offset location.end_offset end + #: (Comment comment) -> bool def encloses?(comment) false end + #: (Comment comment) -> void def leading_comment(comment) location.leading_comment(comment) end + #: (Comment comment) -> void def trailing_comment(comment) location.trailing_comment(comment) end end # The parse result that we are attaching comments to. - attr_reader :parse_result + attr_reader :parse_result #: ParseResult # Create a new Comments object that will attach comments to the given # parse result. + #-- + #: (ParseResult parse_result) -> void def initialize(parse_result) @parse_result = parse_result end # Attach the comments to their respective locations in the tree by # mutating the parse result. + #-- + #: () -> void def attach! parse_result.comments.each do |comment| preceding, enclosing, following = nearest_targets(parse_result.value, comment) @@ -117,11 +146,13 @@ def attach! # Responsible for finding the nearest targets to the given comment within # the context of the given encapsulating node. + #-- + #: (node node, Comment comment) -> [_CommentTarget?, _CommentTarget?, _CommentTarget?] def nearest_targets(node, comment) comment_start = comment.location.start_offset comment_end = comment.location.end_offset - targets = [] #: Array[_Target] + targets = [] #: Array[_CommentTarget] node.comment_targets.map do |value| case value when StatementsNode @@ -134,8 +165,8 @@ def nearest_targets(node, comment) end targets.sort_by!(&:start_offset) - preceding = nil #: _Target? - following = nil #: _Target? + preceding = nil #: _CommentTarget? + following = nil #: _CommentTarget? left = 0 right = targets.length diff --git a/lib/prism/parse_result/errors.rb b/lib/prism/parse_result/errors.rb index 26c376b3ce895f..388309d23d2297 100644 --- a/lib/prism/parse_result/errors.rb +++ b/lib/prism/parse_result/errors.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled require "stringio" @@ -9,14 +11,18 @@ class ParseResult < Result # can be used to format the errors in a human-readable way. class Errors # The parse result that contains the errors. - attr_reader :parse_result + attr_reader :parse_result #: ParseResult # Initialize a new set of errors from the given parse result. + #-- + #: (ParseResult parse_result) -> void def initialize(parse_result) @parse_result = parse_result end # Formats the errors in a human-readable way and return them as a string. + #-- + #: () -> String def format error_lines = {} #: Hash[Integer, Array[ParseError]] parse_result.errors.each do |error| diff --git a/lib/prism/parse_result/newlines.rb b/lib/prism/parse_result/newlines.rb index e7fd62cafedb37..450c7902266df8 100644 --- a/lib/prism/parse_result/newlines.rb +++ b/lib/prism/parse_result/newlines.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism class ParseResult < Result @@ -24,13 +26,20 @@ class ParseResult < Result # that case. We do that to avoid storing the extra `@newline` instance # variable on every node if we don't need it. class Newlines < Visitor + # The map of lines indices to whether or not they have been marked as + # emitting a newline event. + # @rbs @lines: Array[bool] + # Create a new Newlines visitor with the given newline offsets. + #-- + #: (Integer lines) -> void def initialize(lines) - # @type var lines: Integer @lines = Array.new(1 + lines, false) end - # Permit block/lambda nodes to mark newlines within themselves. + # Permit block nodes to mark newlines within themselves. + #-- + #: (BlockNode node) -> void def visit_block_node(node) old_lines = @lines @lines = Array.new(old_lines.size, false) @@ -42,17 +51,39 @@ def visit_block_node(node) end end - alias_method :visit_lambda_node, :visit_block_node + # Permit lambda nodes to mark newlines within themselves. + #-- + #: (LambdaNode node) -> void + def visit_lambda_node(node) + old_lines = @lines + @lines = Array.new(old_lines.size, false) + + begin + super(node) + ensure + @lines = old_lines + end + end - # Mark if/unless nodes as newlines. + # Mark if nodes as newlines. + #-- + #: (IfNode node) -> void def visit_if_node(node) node.newline_flag!(@lines) super(node) end - alias_method :visit_unless_node, :visit_if_node + # Mark unless nodes as newlines. + #-- + #: (UnlessNode node) -> void + def visit_unless_node(node) + node.newline_flag!(@lines) + super(node) + end # Permit statements lists to mark newlines within themselves. + #-- + #: (StatementsNode node) -> void def visit_statements_node(node) node.body.each do |child| child.newline_flag!(@lines) @@ -63,10 +94,16 @@ def visit_statements_node(node) end class Node + # Tracks whether or not this node should emit a newline event when the + # instructions that it represents are executed. + # @rbs @newline_flag: bool + + #: () -> bool def newline_flag? # :nodoc: !!defined?(@newline_flag) end + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: line = location.start_line unless lines[line] @@ -77,48 +114,56 @@ def newline_flag!(lines) # :nodoc: end class BeginNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: # Never mark BeginNode with a newline flag, mark children instead. end end class ParenthesesNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: # Never mark ParenthesesNode with a newline flag, mark children instead. end end class IfNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: predicate.newline_flag!(lines) end end class UnlessNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: predicate.newline_flag!(lines) end end class UntilNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: predicate.newline_flag!(lines) end end class WhileNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: predicate.newline_flag!(lines) end end class RescueModifierNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: expression.newline_flag!(lines) end end class InterpolatedMatchLastLineNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: first = parts.first first.newline_flag!(lines) if first @@ -126,6 +171,7 @@ def newline_flag!(lines) # :nodoc: end class InterpolatedRegularExpressionNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: first = parts.first first.newline_flag!(lines) if first @@ -133,6 +179,7 @@ def newline_flag!(lines) # :nodoc: end class InterpolatedStringNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: first = parts.first first.newline_flag!(lines) if first @@ -140,6 +187,7 @@ def newline_flag!(lines) # :nodoc: end class InterpolatedSymbolNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: first = parts.first first.newline_flag!(lines) if first @@ -147,6 +195,7 @@ def newline_flag!(lines) # :nodoc: end class InterpolatedXStringNode < Node + #: (Array[bool] lines) -> void def newline_flag!(lines) # :nodoc: first = parts.first first.newline_flag!(lines) if first diff --git a/lib/prism/pattern.rb b/lib/prism/pattern.rb index dde9d3b6f96b06..be0493df053677 100644 --- a/lib/prism/pattern.rb +++ b/lib/prism/pattern.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism # A pattern is an object that wraps a Ruby pattern matching expression. The @@ -41,6 +43,8 @@ class Pattern class CompilationError < StandardError # Create a new CompilationError with the given representation of the node # that caused the error. + #-- + #: (String repr) -> void def initialize(repr) # :nodoc: super(<<~ERROR) prism was unable to compile the pattern you provided into a usable @@ -57,10 +61,13 @@ def initialize(repr) # :nodoc: end # The query that this pattern was initialized with. - attr_reader :query + attr_reader :query #: String + # @rbs @compiled: Proc? # Create a new pattern with the given query. The query should be a string # containing a Ruby pattern matching expression. + #-- + #: (String query) -> void def initialize(query) @query = query @compiled = nil @@ -68,6 +75,8 @@ def initialize(query) # Compile the query into a callable object that can be used to match against # nodes. + #-- + #: () -> Proc def compile result = Prism.parse("case nil\nin #{query}\nend") @@ -84,7 +93,10 @@ def compile # pattern. If a block is given, it will be called with each node that # matches the pattern. If no block is given, an enumerator will be returned # that will yield each node that matches the pattern. - def scan(root) + #-- + #: (node root) -> Enumerator[node, void] + #: (node root) { (node) -> void } -> void + def scan(root, &blk) return to_enum(:scan, root) unless block_given? @compiled ||= compile @@ -100,22 +112,32 @@ def scan(root) # Shortcut for combining two procs into one that returns true if both return # true. + #-- + #: (Proc left, Proc right) -> Proc def combine_and(left, right) # :nodoc: ->(other) { left.call(other) && right.call(other) } end # Shortcut for combining two procs into one that returns true if either # returns true. + #-- + #: (Proc left, Proc right) -> Proc def combine_or(left, right) # :nodoc: ->(other) { left.call(other) || right.call(other) } end - # Raise an error because the given node is not supported. + # Raise an error because the given node is not supported. Note purposefully + # not typing this method since it is a no return method that Steep does not + # understand. + #-- + #: (node node) -> bot def compile_error(node) # :nodoc: raise CompilationError, node.inspect end # in [foo, bar, baz] + #-- + #: (ArrayPatternNode node) -> Proc def compile_array_pattern_node(node) # :nodoc: compile_error(node) if !node.rest.nil? || node.posts.any? @@ -141,11 +163,15 @@ def compile_array_pattern_node(node) # :nodoc: end # in foo | bar + #-- + #: (AlternationPatternNode node) -> Proc def compile_alternation_pattern_node(node) # :nodoc: combine_or(compile_node(node.left), compile_node(node.right)) end # in Prism::ConstantReadNode + #-- + #: (ConstantPathNode node) -> Proc def compile_constant_path_node(node) # :nodoc: parent = node.parent @@ -161,11 +187,15 @@ def compile_constant_path_node(node) # :nodoc: # in ConstantReadNode # in String + #-- + #: (ConstantReadNode node) -> Proc def compile_constant_read_node(node) # :nodoc: compile_constant_name(node, node.name) end # Compile a name associated with a constant. + #-- + #: ((ConstantPathNode | ConstantReadNode) node, Symbol name) -> Proc def compile_constant_name(node, name) # :nodoc: if Prism.const_defined?(name, false) clazz = Prism.const_get(name) @@ -182,9 +212,14 @@ def compile_constant_name(node, name) # :nodoc: # in InstanceVariableReadNode[name: Symbol] # in { name: Symbol } + #-- + #: (HashPatternNode node) -> Proc def compile_hash_pattern_node(node) # :nodoc: compile_error(node) if node.rest - compiled_constant = compile_node(node.constant) if node.constant + + if (constant = node.constant) + compiled_constant = compile_node(constant) + end preprocessed = node.elements.to_h do |element| @@ -212,11 +247,15 @@ def compile_hash_pattern_node(node) # :nodoc: end # in nil + #-- + #: (NilNode node) -> Proc def compile_nil_node(node) # :nodoc: ->(attribute) { attribute.nil? } end # in /foo/ + #-- + #: (RegularExpressionNode node) -> Proc def compile_regular_expression_node(node) # :nodoc: regexp = Regexp.new(node.unescaped, node.closing[1..]) @@ -225,6 +264,8 @@ def compile_regular_expression_node(node) # :nodoc: # in "" # in "foo" + #-- + #: (StringNode node) -> Proc def compile_string_node(node) # :nodoc: string = node.unescaped @@ -233,6 +274,8 @@ def compile_string_node(node) # :nodoc: # in :+ # in :foo + #-- + #: (SymbolNode node) -> Proc def compile_symbol_node(node) # :nodoc: symbol = node.unescaped.to_sym @@ -241,6 +284,8 @@ def compile_symbol_node(node) # :nodoc: # Compile any kind of node. Dispatch out to the individual compilation # methods based on the type of node. + #-- + #: (node node) -> Proc def compile_node(node) # :nodoc: case node when AlternationPatternNode diff --git a/lib/prism/prism.gemspec b/lib/prism/prism.gemspec index a155dc3da4689f..ca2db717baffc2 100644 --- a/lib/prism/prism.gemspec +++ b/lib/prism/prism.gemspec @@ -48,6 +48,7 @@ Gem::Specification.new do |spec| "ext/prism/extension.h", "include/prism.h", "include/prism/ast.h", + "include/prism/debug_allocator.h", "include/prism/defines.h", "include/prism/diagnostic.h", "include/prism/encoding.h", @@ -109,37 +110,53 @@ Gem::Specification.new do |spec| "lib/prism/translation/ruby_parser.rb", "lib/prism/visitor.rb", "prism.gemspec", - "rbi/prism.rbi", - "rbi/prism/compiler.rbi", - "rbi/prism/dsl.rbi", - "rbi/prism/inspect_visitor.rbi", - "rbi/prism/node_ext.rbi", - "rbi/prism/node.rbi", - "rbi/prism/parse_result.rbi", - "rbi/prism/reflection.rbi", - "rbi/prism/string_query.rbi", + "rbi/generated/prism.rbi", + "rbi/generated/prism/compiler.rbi", + "rbi/generated/prism/desugar_compiler.rbi", + "rbi/generated/prism/dispatcher.rbi", + "rbi/generated/prism/dot_visitor.rbi", + "rbi/generated/prism/dsl.rbi", + "rbi/generated/prism/inspect_visitor.rbi", + "rbi/generated/prism/lex_compat.rbi", + "rbi/generated/prism/mutation_compiler.rbi", + "rbi/generated/prism/node.rbi", + "rbi/generated/prism/node_ext.rbi", + "rbi/generated/prism/parse_result.rbi", + "rbi/generated/prism/pattern.rbi", + "rbi/generated/prism/reflection.rbi", + "rbi/generated/prism/relocation.rbi", + "rbi/generated/prism/serialize.rbi", + "rbi/generated/prism/string_query.rbi", + "rbi/generated/prism/translation.rbi", + "rbi/generated/prism/visitor.rbi", + "rbi/generated/prism/parse_result/comments.rbi", + "rbi/generated/prism/parse_result/errors.rbi", + "rbi/generated/prism/parse_result/newlines.rbi", "rbi/prism/translation/parser.rbi", "rbi/prism/translation/parser_versions.rbi", "rbi/prism/translation/ripper.rbi", - "rbi/prism/visitor.rbi", - "sig/prism.rbs", - "sig/prism/compiler.rbs", - "sig/prism/dispatcher.rbs", - "sig/prism/dot_visitor.rbs", - "sig/prism/dsl.rbs", - "sig/prism/inspect_visitor.rbs", - "sig/prism/lex_compat.rbs", - "sig/prism/mutation_compiler.rbs", - "sig/prism/node_ext.rbs", - "sig/prism/node.rbs", - "sig/prism/parse_result.rbs", - "sig/prism/parse_result/comments.rbs", - "sig/prism/pattern.rbs", - "sig/prism/reflection.rbs", - "sig/prism/relocation.rbs", - "sig/prism/serialize.rbs", - "sig/prism/string_query.rbs", - "sig/prism/visitor.rbs", + "sig/generated/prism.rbs", + "sig/generated/prism/compiler.rbs", + "sig/generated/prism/desugar_compiler.rbs", + "sig/generated/prism/dispatcher.rbs", + "sig/generated/prism/dot_visitor.rbs", + "sig/generated/prism/dsl.rbs", + "sig/generated/prism/inspect_visitor.rbs", + "sig/generated/prism/lex_compat.rbs", + "sig/generated/prism/mutation_compiler.rbs", + "sig/generated/prism/node.rbs", + "sig/generated/prism/node_ext.rbs", + "sig/generated/prism/parse_result.rbs", + "sig/generated/prism/pattern.rbs", + "sig/generated/prism/reflection.rbs", + "sig/generated/prism/relocation.rbs", + "sig/generated/prism/serialize.rbs", + "sig/generated/prism/string_query.rbs", + "sig/generated/prism/translation.rbs", + "sig/generated/prism/visitor.rbs", + "sig/generated/prism/parse_result/comments.rbs", + "sig/generated/prism/parse_result/errors.rbs", + "sig/generated/prism/parse_result/newlines.rbs", "src/diagnostic.c", "src/encoding.c", "src/node.c", diff --git a/lib/prism/relocation.rb b/lib/prism/relocation.rb index 3e9210a7853431..af0f7928272d8e 100644 --- a/lib/prism/relocation.rb +++ b/lib/prism/relocation.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism # Prism parses deterministically for the same input. This provides a nice @@ -12,6 +14,33 @@ module Prism # "save" nodes and locations using a minimal amount of memory (just the # node_id and a field identifier) and then reify them later. module Relocation + # @rbs! + # type entry_value = untyped + # type entry_values = Hash[Symbol, entry_value] + # + # interface _Value + # def start_line: () -> Integer + # def end_line: () -> Integer + # def start_offset: () -> Integer + # def end_offset: () -> Integer + # def start_character_offset: () -> Integer + # def end_character_offset: () -> Integer + # def cached_start_code_units_offset: (_CodeUnitsCache cache) -> Integer + # def cached_end_code_units_offset: (_CodeUnitsCache cache) -> Integer + # def start_column: () -> Integer + # def end_column: () -> Integer + # def start_character_column: () -> Integer + # def end_character_column: () -> Integer + # def cached_start_code_units_column: (_CodeUnitsCache cache) -> Integer + # def cached_end_code_units_column: (_CodeUnitsCache cache) -> Integer + # def leading_comments: () -> Array[Comment] + # def trailing_comments: () -> Array[Comment] + # end + # + # interface _Field + # def fields: (_Value value) -> entry_values + # end + # An entry in a repository that will lazily reify its values when they are # first accessed. class Entry @@ -21,109 +50,152 @@ class Entry class MissingValueError < StandardError end + # @rbs @repository: Repository? + # @rbs @values: Hash[Symbol, untyped]? + # Initialize a new entry with the given repository. + #-- + #: (Repository repository) -> void def initialize(repository) @repository = repository @values = nil end # Fetch the filepath of the value. + #-- + #: () -> String def filepath fetch_value(:filepath) end # Fetch the start line of the value. + #-- + #: () -> Integer def start_line fetch_value(:start_line) end # Fetch the end line of the value. + #-- + #: () -> Integer def end_line fetch_value(:end_line) end # Fetch the start byte offset of the value. + #-- + #: () -> Integer def start_offset fetch_value(:start_offset) end # Fetch the end byte offset of the value. + #-- + #: () -> Integer def end_offset fetch_value(:end_offset) end # Fetch the start character offset of the value. + #-- + #: () -> Integer def start_character_offset fetch_value(:start_character_offset) end # Fetch the end character offset of the value. + #-- + #: () -> Integer def end_character_offset fetch_value(:end_character_offset) end # Fetch the start code units offset of the value, for the encoding that # was configured on the repository. + #-- + #: () -> Integer def start_code_units_offset fetch_value(:start_code_units_offset) end # Fetch the end code units offset of the value, for the encoding that was # configured on the repository. + #-- + #: () -> Integer def end_code_units_offset fetch_value(:end_code_units_offset) end # Fetch the start byte column of the value. + #-- + #: () -> Integer def start_column fetch_value(:start_column) end # Fetch the end byte column of the value. + #-- + #: () -> Integer def end_column fetch_value(:end_column) end # Fetch the start character column of the value. + #-- + #: () -> Integer def start_character_column fetch_value(:start_character_column) end # Fetch the end character column of the value. + #-- + #: () -> Integer def end_character_column fetch_value(:end_character_column) end # Fetch the start code units column of the value, for the encoding that # was configured on the repository. + #-- + #: () -> Integer def start_code_units_column fetch_value(:start_code_units_column) end # Fetch the end code units column of the value, for the encoding that was # configured on the repository. + #-- + #: () -> Integer def end_code_units_column fetch_value(:end_code_units_column) end # Fetch the leading comments of the value. + #-- + #: () -> Array[CommentsField::Comment] def leading_comments fetch_value(:leading_comments) end # Fetch the trailing comments of the value. + #-- + #: () -> Array[CommentsField::Comment] def trailing_comments fetch_value(:trailing_comments) end # Fetch the leading and trailing comments of the value. + #-- + #: () -> Array[CommentsField::Comment] def comments - leading_comments.concat(trailing_comments) + [*leading_comments, *trailing_comments] end # Reify the values on this entry with the given values. This is an # internal-only API that is called from the repository when it is time to # reify the values. + #-- + #: (entry_values values) -> void def reify!(values) # :nodoc: @repository = nil @values = values @@ -132,6 +204,8 @@ def reify!(values) # :nodoc: private # Fetch a value from the entry, raising an error if it is missing. + #-- + #: (Symbol name) -> entry_value def fetch_value(name) values.fetch(name) do raise MissingValueError, "No value for #{name}, make sure the " \ @@ -140,27 +214,35 @@ def fetch_value(name) end # Return the values from the repository, reifying them if necessary. + #-- + #: () -> entry_values def values - @values || (@repository.reify!; @values) + @values || (@repository&.reify!; @values) #: entry_values end end # Represents the source of a repository that will be reparsed. class Source # The value that will need to be reparsed. - attr_reader :value + attr_reader :value #: untyped # Initialize the source with the given value. + #-- + #: (untyped value) -> void def initialize(value) @value = value end # Reparse the value and return the parse result. + #-- + #: () -> ParseResult def result raise NotImplementedError, "Subclasses must implement #result" end # Create a code units cache for the given encoding. + #-- + #: (Encoding encoding) -> _CodeUnitsCache def code_units_cache(encoding) result.code_units_cache(encoding) end @@ -169,6 +251,8 @@ def code_units_cache(encoding) # A source that is represented by a file path. class SourceFilepath < Source # Reparse the file and return the parse result. + #-- + #: () -> ParseResult def result Prism.parse_file(value) end @@ -177,6 +261,8 @@ def result # A source that is represented by a string. class SourceString < Source # Reparse the string and return the parse result. + #-- + #: () -> ParseResult def result Prism.parse(value) end @@ -185,14 +271,18 @@ def result # A field that represents the file path. class FilepathField # The file path that this field represents. - attr_reader :value + attr_reader :value #: String # Initialize a new field with the given file path. + #-- + #: (String value) -> void def initialize(value) @value = value end # Fetch the file path. + #-- + #: (_Value _value) -> entry_values def fields(_value) { filepath: value } end @@ -201,6 +291,8 @@ def fields(_value) # A field representing the start and end lines. class LinesField # Fetches the start and end line of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { start_line: value.start_line, end_line: value.end_line } end @@ -209,6 +301,8 @@ def fields(value) # A field representing the start and end byte offsets. class OffsetsField # Fetches the start and end byte offset of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { start_offset: value.start_offset, end_offset: value.end_offset } end @@ -217,6 +311,8 @@ def fields(value) # A field representing the start and end character offsets. class CharacterOffsetsField # Fetches the start and end character offset of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { start_character_offset: value.start_character_offset, @@ -229,12 +325,16 @@ def fields(value) class CodeUnitOffsetsField # A pointer to the repository object that is used for lazily creating a # code units cache. - attr_reader :repository + attr_reader :repository #: Repository # The associated encoding for the code units. - attr_reader :encoding + attr_reader :encoding #: Encoding + + # @rbs @cache: _CodeUnitsCache? # Initialize a new field with the associated repository and encoding. + #-- + #: (Repository repository, Encoding encoding) -> void def initialize(repository, encoding) @repository = repository @encoding = encoding @@ -243,6 +343,8 @@ def initialize(repository, encoding) # Fetches the start and end code units offset of a value for a particular # encoding. + #-- + #: (_Value value) -> entry_values def fields(value) { start_code_units_offset: value.cached_start_code_units_offset(cache), @@ -253,6 +355,8 @@ def fields(value) private # Lazily create a code units cache for the associated encoding. + #-- + #: () -> _CodeUnitsCache def cache @cache ||= repository.code_units_cache(encoding) end @@ -261,6 +365,8 @@ def cache # A field representing the start and end byte columns. class ColumnsField # Fetches the start and end byte column of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { start_column: value.start_column, end_column: value.end_column } end @@ -269,6 +375,8 @@ def fields(value) # A field representing the start and end character columns. class CharacterColumnsField # Fetches the start and end character column of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { start_character_column: value.start_character_column, @@ -282,12 +390,16 @@ def fields(value) class CodeUnitColumnsField # The repository object that is used for lazily creating a code units # cache. - attr_reader :repository + attr_reader :repository #: Repository # The associated encoding for the code units. - attr_reader :encoding + attr_reader :encoding #: Encoding + + # @rbs @cache: _CodeUnitsCache? # Initialize a new field with the associated repository and encoding. + #-- + #: (Repository repository, Encoding encoding) -> void def initialize(repository, encoding) @repository = repository @encoding = encoding @@ -296,6 +408,8 @@ def initialize(repository, encoding) # Fetches the start and end code units column of a value for a particular # encoding. + #-- + #: (_Value value) -> entry_values def fields(value) { start_code_units_column: value.cached_start_code_units_column(cache), @@ -306,6 +420,8 @@ def fields(value) private # Lazily create a code units cache for the associated encoding. + #-- + #: () -> _CodeUnitsCache def cache @cache ||= repository.code_units_cache(encoding) end @@ -316,9 +432,11 @@ class CommentsField # An object that represents a slice of a comment. class Comment # The slice of the comment. - attr_reader :slice + attr_reader :slice #: String # Initialize a new comment with the given slice. + # + #: (String slice) -> void def initialize(slice) @slice = slice end @@ -327,6 +445,8 @@ def initialize(slice) private # Create comment objects from the given values. + #-- + #: (entry_value values) -> Array[Comment] def comments(values) values.map { |value| Comment.new(value.slice) } end @@ -335,6 +455,8 @@ def comments(values) # A field representing the leading comments. class LeadingCommentsField < CommentsField # Fetches the leading comments of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { leading_comments: comments(value.leading_comments) } end @@ -343,6 +465,8 @@ def fields(value) # A field representing the trailing comments. class TrailingCommentsField < CommentsField # Fetches the trailing comments of a value. + #-- + #: (_Value value) -> entry_values def fields(value) { trailing_comments: comments(value.trailing_comments) } end @@ -358,15 +482,17 @@ class ConfigurationError < StandardError # The source associated with this repository. This will be either a # SourceFilepath (the most common use case) or a SourceString. - attr_reader :source + attr_reader :source #: Source # The fields that have been configured on this repository. - attr_reader :fields + attr_reader :fields #: Hash[Symbol, _Field] # The entries that have been saved on this repository. - attr_reader :entries + attr_reader :entries #: Hash[Integer, Hash[Symbol, Entry]] # Initialize a new repository with the given source. + #-- + #: (Source source) -> void def initialize(source) @source = source @fields = {} @@ -374,69 +500,93 @@ def initialize(source) end # Create a code units cache for the given encoding from the source. + #-- + #: (Encoding encoding) -> _CodeUnitsCache def code_units_cache(encoding) source.code_units_cache(encoding) end # Configure the filepath field for this repository and return self. + #-- + #: () -> self def filepath raise ConfigurationError, "Can only specify filepath for a filepath source" unless source.is_a?(SourceFilepath) field(:filepath, FilepathField.new(source.value)) end # Configure the lines field for this repository and return self. + #-- + #: () -> self def lines field(:lines, LinesField.new) end # Configure the offsets field for this repository and return self. + #-- + #: () -> self def offsets field(:offsets, OffsetsField.new) end # Configure the character offsets field for this repository and return # self. + #-- + #: () -> self def character_offsets field(:character_offsets, CharacterOffsetsField.new) end # Configure the code unit offsets field for this repository for a specific # encoding and return self. + #-- + #: (Encoding encoding) -> self def code_unit_offsets(encoding) field(:code_unit_offsets, CodeUnitOffsetsField.new(self, encoding)) end # Configure the columns field for this repository and return self. + #-- + #: () -> self def columns field(:columns, ColumnsField.new) end # Configure the character columns field for this repository and return # self. + #-- + #: () -> self def character_columns field(:character_columns, CharacterColumnsField.new) end # Configure the code unit columns field for this repository for a specific # encoding and return self. + #-- + #: (Encoding encoding) -> self def code_unit_columns(encoding) field(:code_unit_columns, CodeUnitColumnsField.new(self, encoding)) end # Configure the leading comments field for this repository and return # self. + #-- + #: () -> self def leading_comments field(:leading_comments, LeadingCommentsField.new) end # Configure the trailing comments field for this repository and return # self. + #-- + #: () -> self def trailing_comments field(:trailing_comments, TrailingCommentsField.new) end # Configure both the leading and trailing comment fields for this # repository and return self. + #-- + #: () -> self def comments leading_comments.trailing_comments end @@ -444,6 +594,8 @@ def comments # This method is called from nodes and locations when they want to enter # themselves into the repository. It it internal-only and meant to be # called from the #save* APIs. + #-- + #: (Integer node_id, Symbol field_name) -> Entry def enter(node_id, field_name) # :nodoc: entry = Entry.new(self) @entries[node_id][field_name] = entry @@ -453,6 +605,8 @@ def enter(node_id, field_name) # :nodoc: # This method is called from the entries in the repository when they need # to reify their values. It is internal-only and meant to be called from # the various value APIs. + #-- + #: () -> void def reify! # :nodoc: result = source.result @@ -466,7 +620,7 @@ def reify! # :nodoc: while (node = queue.shift) @entries[node.node_id].each do |field_name, entry| value = node.public_send(field_name) - values = {} #: Hash[Symbol, untyped] + values = {} #: entry_values fields.each_value do |field| values.merge!(field.fields(value)) @@ -485,6 +639,8 @@ def reify! # :nodoc: # Append the given field to the repository and return the repository so # that these calls can be chained. + #-- + #: (Symbol name, _Field) -> self def field(name, value) raise ConfigurationError, "Cannot specify multiple #{name} fields" if @fields.key?(name) @fields[name] = value @@ -493,11 +649,15 @@ def field(name, value) end # Create a new repository for the given filepath. + #-- + #: (String value) -> Repository def self.filepath(value) Repository.new(SourceFilepath.new(value)) end # Create a new repository for the given string. + #-- + #: (String value) -> Repository def self.string(value) Repository.new(SourceString.new(value)) end diff --git a/lib/prism/string_query.rb b/lib/prism/string_query.rb index 547f58d2fa6c92..99ce57e5fec4c6 100644 --- a/lib/prism/string_query.rb +++ b/lib/prism/string_query.rb @@ -1,29 +1,44 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism # Query methods that allow categorizing strings based on their context for # where they could be valid in a Ruby syntax tree. class StringQuery + # @rbs! + # def self.local?: (String string) -> bool + # def self.constant?: (String string) -> bool + # def self.method_name?: (String string) -> bool + # The string that this query is wrapping. - attr_reader :string + attr_reader :string #: String # Initialize a new query with the given string. + #-- + #: (String string) -> void def initialize(string) @string = string end # Whether or not this string is a valid local variable name. + #-- + #: () -> bool def local? StringQuery.local?(string) end # Whether or not this string is a valid constant name. + #-- + #: () -> bool def constant? StringQuery.constant?(string) end # Whether or not this string is a valid method name. + #-- + #: () -> bool def method_name? StringQuery.method_name?(string) end diff --git a/lib/prism/translation.rb b/lib/prism/translation.rb index 57b57135bcc1f2..5a086a75423443 100644 --- a/lib/prism/translation.rb +++ b/lib/prism/translation.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true # :markup: markdown +#-- +# rbs_inline: enabled module Prism # This module is responsible for converting the prism syntax tree into other diff --git a/lib/prism/translation/parser_current.rb b/lib/prism/translation/parser_current.rb index f13eff6bbe2d90..f7c1070e30c80e 100644 --- a/lib/prism/translation/parser_current.rb +++ b/lib/prism/translation/parser_current.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true # :markup: markdown +#-- # typed: ignore -# module Prism module Translation case RUBY_VERSION diff --git a/method.h b/method.h index 260344d53b58ba..fdbab41cf71bba 100644 --- a/method.h +++ b/method.h @@ -264,7 +264,6 @@ 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); diff --git a/prism/config.yml b/prism/config.yml index 7527e01070b2a5..472ab291a706f9 100644 --- a/prism/config.yml +++ b/prism/config.yml @@ -1046,19 +1046,19 @@ nodes: Represents an array pattern in pattern matching. foo in 1, 2 - ^^^^^^^^^^^ + ^^^^ foo in [1, 2] - ^^^^^^^^^^^^^ + ^^^^^^ foo in *bar - ^^^^^^^^^^^ + ^^^^ foo in Bar[] - ^^^^^^^^^^^^ + ^^^^^ foo in Bar[1, 2, 3] - ^^^^^^^^^^^^^^^^^^^ + ^^^^^^^^^^^^ - name: AssocNode fields: - name: key @@ -1167,7 +1167,7 @@ nodes: Represents the else clause within the begin block. begin x; rescue y; else z; end - ^^^^^^ + ^^^^^^^^^^^ - name: ensure_clause type: node? kind: EnsureNode @@ -1200,7 +1200,7 @@ nodes: The expression that is being passed as a block argument. This can be any [non-void expression](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). foo(&args) - ^^^^^ + ^^^^ - name: operator_loc type: location comment: | @@ -1212,7 +1212,7 @@ nodes: Represents a block argument using `&`. bar(&args) - ^^^^^^^^^^ + ^^^^^ - name: BlockLocalVariableNode flags: ParameterFlags fields: @@ -1753,7 +1753,7 @@ nodes: Represents assigning to a local variable in pattern matching. foo => [bar => baz] - ^^^^^^^^^^^^ + ^^^^^^^^^^ - name: CaseMatchNode fields: - name: predicate @@ -1763,7 +1763,7 @@ nodes: Represents the predicate of the case match. This can be either `nil` or any [non-void expressions](https://github.com/ruby/prism/blob/main/docs/parsing_rules.md#non-void-expression). case true; in false; end - ^^^^ + ^^^^ - name: conditions type: node[] kind: InNode @@ -1779,7 +1779,7 @@ nodes: Represents the else clause of the case match. case true; in false; else; end - ^^^^ + ^^^^^^^^^ - name: case_keyword_loc type: location comment: | @@ -1826,7 +1826,7 @@ nodes: Represents the else clause of the case statement. case true; when false; else; end - ^^^^ + ^^^^^^^^^ - name: case_keyword_loc type: location comment: | @@ -1888,9 +1888,8 @@ nodes: comment: | Represents the body of the class. - class Foo - foo - ^^^ + class Foo; bar; end + ^^^ - name: end_keyword_loc type: location comment: | @@ -4635,7 +4634,7 @@ nodes: The else clause of the unless expression, if present. unless cond then bar else baz end - ^^^^^^^^ + ^^^^^^^^^^^^ - name: end_keyword_loc type: location? comment: | diff --git a/prism/debug_allocator.h b/prism/debug_allocator.h new file mode 100644 index 00000000000000..3e28a95efbcae7 --- /dev/null +++ b/prism/debug_allocator.h @@ -0,0 +1,99 @@ +/** + * @file debug_allocator.h + * + * Decorate allocation function to ensure sizes are correct. + */ +#ifndef PRISM_DEBUG_ALLOCATOR_H +#define PRISM_DEBUG_ALLOCATOR_H + +#include +#include +#include + +static inline void * +pm_debug_malloc(size_t size) +{ + size_t *memory = xmalloc(size + sizeof(size_t)); + memory[0] = size; + return memory + 1; +} + +static inline void * +pm_debug_calloc(size_t nmemb, size_t size) +{ + size_t total_size = nmemb * size; + void *ptr = pm_debug_malloc(total_size); + memset(ptr, 0, total_size); + return ptr; +} + +static inline void * +pm_debug_realloc(void *ptr, size_t size) +{ + if (ptr == NULL) { + return pm_debug_malloc(size); + } + + size_t *memory = (size_t *)ptr; + void *raw_memory = memory - 1; + memory = (size_t *)xrealloc(raw_memory, size + sizeof(size_t)); + memory[0] = size; + return memory + 1; +} + +static inline void +pm_debug_free(void *ptr) +{ + if (ptr != NULL) { + size_t *memory = (size_t *)ptr; + xfree(memory - 1); + } +} + +static inline void +pm_debug_free_sized(void *ptr, size_t old_size) +{ + if (ptr != NULL) { + size_t *memory = (size_t *)ptr; + if (old_size != memory[-1]) { + fprintf(stderr, "[BUG] buffer %p was allocated with size %lu but freed with size %lu\n", ptr, memory[-1], old_size); + abort(); + } + xfree_sized(memory - 1, old_size + sizeof(size_t)); + } +} + +static inline void * +pm_debug_realloc_sized(void *ptr, size_t size, size_t old_size) +{ + if (ptr == NULL) { + if (old_size != 0) { + fprintf(stderr, "[BUG] realloc_sized called with NULL pointer and old size %lu\n", old_size); + abort(); + } + return pm_debug_malloc(size); + } + + size_t *memory = (size_t *)ptr; + if (old_size != memory[-1]) { + fprintf(stderr, "[BUG] buffer %p was allocated with size %lu but realloced with size %lu\n", ptr, memory[-1], old_size); + abort(); + } + return pm_debug_realloc(ptr, size); +} + +#undef xmalloc +#undef xrealloc +#undef xcalloc +#undef xfree +#undef xrealloc_sized +#undef xfree_sized + +#define xmalloc pm_debug_malloc +#define xrealloc pm_debug_realloc +#define xcalloc pm_debug_calloc +#define xfree pm_debug_free +#define xrealloc_sized pm_debug_realloc_sized +#define xfree_sized pm_debug_free_sized + +#endif diff --git a/prism/defines.h b/prism/defines.h index f2e814fa1dfcd8..1c4e5fa053dabf 100644 --- a/prism/defines.h +++ b/prism/defines.h @@ -161,10 +161,12 @@ * ``` * #ifndef PRISM_XALLOCATOR_H * #define PRISM_XALLOCATOR_H - * #define xmalloc my_malloc - * #define xrealloc my_realloc - * #define xcalloc my_calloc - * #define xfree my_free + * #define xmalloc my_malloc + * #define xrealloc my_realloc + * #define xcalloc my_calloc + * #define xfree my_free + * #define xrealloc_sized my_realloc_sized // (optional) + * #define xfree_sized my_free_sized // (optional) * #endif * ``` */ @@ -204,6 +206,28 @@ #endif #endif +#ifndef xfree_sized +/** + * The free_sized function that should be used. This can be overridden with the + * PRISM_XALLOCATOR define. + * If not defined, defaults to calling xfree. + */ + #define xfree_sized(p, s) xfree(((void)(s), (p))) +#endif + +#ifndef xrealloc_sized +/** + * The xrealloc_sized function that should be used. This can be overridden with the + * PRISM_XALLOCATOR define. + * If not defined, defaults to calling xrealloc. + */ + #define xrealloc_sized(p, ns, os) xrealloc((p), ((void)(os), (ns))) +#endif + +#ifdef PRISM_BUILD_DEBUG + #include "prism/debug_allocator.h" +#endif + /** * If PRISM_BUILD_MINIMAL is defined, then we're going to define every possible * switch that will turn off certain features of prism. diff --git a/prism/extension.c b/prism/extension.c index 03182c1a5747c7..fcbc1e6c244fa2 100644 --- a/prism/extension.c +++ b/prism/extension.c @@ -413,7 +413,7 @@ dump(int argc, VALUE *argv, VALUE self) { if (options.freeze) rb_obj_freeze(value); #ifdef PRISM_BUILD_DEBUG - xfree(dup); + xfree_sized(dup, length); #endif pm_string_free(&input); @@ -929,7 +929,7 @@ parse(int argc, VALUE *argv, VALUE self) { VALUE value = parse_input(&input, &options); #ifdef PRISM_BUILD_DEBUG - xfree(dup); + xfree_sized(dup, length); #endif pm_string_free(&input); diff --git a/prism/options.c b/prism/options.c index 09d2a65a6cbcf6..961d52330f8e35 100644 --- a/prism/options.c +++ b/prism/options.c @@ -226,10 +226,10 @@ pm_options_free(pm_options_t *options) { pm_string_free(&scope->locals[local_index]); } - xfree(scope->locals); + xfree_sized(scope->locals, scope->locals_count * sizeof(pm_string_t)); } - xfree(options->scopes); + xfree_sized(options->scopes, options->scopes_count * sizeof(pm_options_scope_t)); } /** diff --git a/prism/prism.c b/prism/prism.c index 4ab1ca4594db98..bfa91f4296cdbf 100644 --- a/prism/prism.c +++ b/prism/prism.c @@ -286,7 +286,7 @@ lex_mode_pop(pm_parser_t *parser) { } else { parser->lex_modes.index--; pm_lex_mode_t *prev = parser->lex_modes.current->prev; - xfree(parser->lex_modes.current); + xfree_sized(parser->lex_modes.current, sizeof(pm_lex_mode_t)); parser->lex_modes.current = prev; } } @@ -777,7 +777,7 @@ pm_parser_scope_shareable_constant_set(pm_parser_t *parser, pm_shareable_constan static void pm_locals_free(pm_locals_t *locals) { if (locals->capacity > 0) { - xfree(locals->locals); + xfree_sized(locals->locals, locals->capacity * sizeof(pm_local_t)); } } @@ -2956,7 +2956,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3010,7 +3010,7 @@ pm_index_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, cons // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3040,7 +3040,7 @@ pm_call_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3071,7 +3071,7 @@ pm_index_operator_write_node_create(pm_parser_t *parser, pm_call_node_t *target, // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3101,7 +3101,7 @@ pm_call_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3132,7 +3132,7 @@ pm_index_or_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3164,7 +3164,7 @@ pm_call_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3192,7 +3192,7 @@ pm_index_target_node_create(pm_parser_t *parser, pm_call_node_t *target) { // Here we're going to free the target, since it is no longer necessary. // However, we don't want to call `pm_node_destroy` because we want to keep // around all of its children since we just reused them. - xfree(target); + xfree_sized(target, sizeof(pm_call_node_t)); return node; } @@ -3873,7 +3873,8 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { // First, get a buffer of the content. size_t length = (size_t) diff; - char *buffer = xmalloc(sizeof(char) * (length + 1)); + const size_t buffer_size = sizeof(char) * (length + 1); + char *buffer = xmalloc(buffer_size); memcpy((void *) buffer, token->start, length); // Next, determine if we need to replace the decimal point because of @@ -3908,7 +3909,7 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { // is in a valid format. However it's good to be safe. if ((eptr != buffer + length) || (errno != 0 && errno != ERANGE)) { PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, token, PM_ERR_FLOAT_PARSE); - xfree((void *) buffer); + xfree_sized(buffer, buffer_size); return 0.0; } @@ -3931,7 +3932,7 @@ pm_double_parse(pm_parser_t *parser, const pm_token_t *token) { } // Finally we can free the buffer and return the value. - xfree((void *) buffer); + xfree_sized(buffer, buffer_size); return value; } @@ -4017,7 +4018,7 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) { digits[0] = '1'; if (fract_length > 1) memset(digits + 1, '0', fract_length - 1); pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + fract_length); - xfree(digits); + xfree_sized(digits, length); pm_integers_reduce(&node->numerator, &node->denominator); return node; @@ -5521,7 +5522,7 @@ pm_multi_write_node_create(pm_parser_t *parser, pm_multi_target_node_t *target, // Explicitly do not call pm_node_destroy here because we want to keep // around all of the information within the MultiWriteNode node. - xfree(target); + xfree_sized(target, sizeof(pm_multi_target_node_t)); return node; } @@ -5646,7 +5647,7 @@ pm_numbered_reference_read_node_number(pm_parser_t *parser, const pm_token_t *to value = 0; } - xfree(digits); + xfree_sized(digits, sizeof(char) * (length + 1)); if ((errno == ERANGE) || (value > NTH_REF_MAX)) { PM_PARSER_WARN_FORMAT(parser, U32(start - parser->start), U32(length), PM_WARN_INVALID_NUMBERED_REFERENCE, (int) (length + 1), (const char *) token->start); @@ -6751,7 +6752,7 @@ pm_string_node_to_symbol_node(pm_parser_t *parser, pm_string_node_t *node, const // We are explicitly _not_ using pm_node_destroy here because we don't want // to trash the unescaped string. We could instead copy the string if we // know that it is owned, but we're taking the fast path for now. - xfree(node); + xfree_sized(node, sizeof(pm_string_node_t)); return new_node; } @@ -6784,7 +6785,7 @@ pm_symbol_node_to_string_node(pm_parser_t *parser, pm_symbol_node_t *node) { // We are explicitly _not_ using pm_node_destroy here because we don't want // to trash the unescaped string. We could instead copy the string if we // know that it is owned, but we're taking the fast path for now. - xfree(node); + xfree_sized(node, sizeof(pm_symbol_node_t)); return new_node; } @@ -7247,7 +7248,7 @@ pm_parser_scope_pop(pm_parser_t *parser) { parser->current_scope = scope->previous; pm_locals_free(&scope->locals); pm_node_list_free(&scope->implicit_parameters); - xfree(scope); + xfree_sized(scope, sizeof(pm_scope_t)); } /******************************************************************************/ @@ -7847,7 +7848,7 @@ context_push(pm_parser_t *parser, pm_context_t context) { static void context_pop(pm_parser_t *parser) { pm_context_node_t *prev = parser->current_context->prev; - xfree(parser->current_context); + xfree_sized(parser->current_context, sizeof(pm_context_node_t)); parser->current_context = prev; } @@ -16639,7 +16640,7 @@ parse_pattern_hash(pm_parser_t *parser, pm_constant_id_list_t *captures, pm_node } pm_hash_pattern_node_t *node = pm_hash_pattern_node_node_list_create(parser, &assocs, rest); - xfree(assocs.nodes); + xfree_sized(assocs.nodes, assocs.capacity * sizeof(pm_node_t *)); pm_static_literals_free(&keys); return node; @@ -17166,7 +17167,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag } } - xfree(nodes.nodes); + xfree_sized(nodes.nodes, nodes.capacity * sizeof(pm_node_t *)); } else if (leading_rest) { // Otherwise, if we parsed a single splat pattern, then we know we have // an array pattern, so we can go ahead and create that node. @@ -19453,7 +19454,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_symbol_node_append(interpolated, first_string); pm_interpolated_symbol_node_append(interpolated, second_string); - xfree(current); + xfree_sized(current, sizeof(pm_symbol_node_t)); current = UP(interpolated); } else { assert(false && "unreachable"); @@ -19538,7 +19539,7 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b pm_interpolated_symbol_node_append(interpolated, first_string); pm_interpolated_symbol_node_append(interpolated, second_string); - xfree(current); + xfree_sized(current, sizeof(pm_symbol_node_t)); current = UP(interpolated); } else { assert(false && "unreachable"); @@ -22252,7 +22253,7 @@ pm_comment_list_free(pm_list_t *list) { next = node->next; pm_comment_t *comment = (pm_comment_t *) node; - xfree(comment); + xfree_sized(comment, sizeof(pm_comment_t)); } } @@ -22267,7 +22268,7 @@ pm_magic_comment_list_free(pm_list_t *list) { next = node->next; pm_magic_comment_t *magic_comment = (pm_magic_comment_t *) node; - xfree(magic_comment); + xfree_sized(magic_comment, sizeof(pm_magic_comment_t)); } } diff --git a/prism/static_literals.c b/prism/static_literals.c index 58a08a8c03ca11..f3a5650d314d83 100644 --- a/prism/static_literals.c +++ b/prism/static_literals.c @@ -183,7 +183,7 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *m } // Finally, free the old node list and update the hash. - xfree(hash->nodes); + xfree_sized(hash->nodes, hash->capacity * sizeof(pm_node_t *)); hash->nodes = new_nodes; hash->capacity = new_capacity; } @@ -221,7 +221,7 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *m */ static void pm_node_hash_free(pm_node_hash_t *hash) { - if (hash->capacity > 0) xfree(hash->nodes); + if (hash->capacity > 0) xfree_sized(hash->nodes, hash->capacity * sizeof(pm_node_t *)); } /** diff --git a/prism/templates/ext/prism/api_node.c.erb b/prism/templates/ext/prism/api_node.c.erb index 69f70240f87b66..e3bcf116cc900a 100644 --- a/prism/templates/ext/prism/api_node.c.erb +++ b/prism/templates/ext/prism/api_node.c.erb @@ -113,7 +113,7 @@ pm_node_stack_pop(pm_node_stack_node_t **stack) { const pm_node_t *visit = current->visit; *stack = current->prev; - xfree(current); + xfree_sized(current, sizeof(pm_node_stack_node_t)); return visit; } diff --git a/prism/templates/lib/prism/compiler.rb.erb b/prism/templates/lib/prism/compiler.rb.erb index 031557a221de3c..13317cac0449e2 100644 --- a/prism/templates/lib/prism/compiler.rb.erb +++ b/prism/templates/lib/prism/compiler.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + module Prism # A compiler is a visitor that returns the value of each node as it visits. # This is as opposed to a visitor which will only walk the tree. This can be @@ -18,22 +21,29 @@ module Prism # class Compiler < Visitor # Visit an individual node. + #-- + #: (node?) -> untyped def visit(node) # :nodoc: node&.accept(self) end # Visit a list of nodes. + #-- + #: (Array[node?]) -> untyped def visit_all(nodes) # :nodoc: nodes.map { |node| node&.accept(self) } end # Visit the child nodes of the given node. + #-- + #: (node) -> Array[untyped] def visit_child_nodes(node) # :nodoc: node.each_child_node.map { |node| node.accept(self) } end <%- nodes.each_with_index do |node, index| -%> <%= "\n" if index != 0 -%> + #: (<%= node.name %>) -> Array[untyped] def visit_<%= node.human %>(node) # :nodoc: node.each_child_node.map { |node| node.accept(self) } end diff --git a/prism/templates/lib/prism/dispatcher.rb.erb b/prism/templates/lib/prism/dispatcher.rb.erb index e4ca84db2421d2..5991b0c9045bed 100644 --- a/prism/templates/lib/prism/dispatcher.rb.erb +++ b/prism/templates/lib/prism/dispatcher.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + module Prism # The dispatcher class fires events for nodes that are found while walking an # AST to all registered listeners. It's useful for performing different types @@ -32,26 +35,35 @@ module Prism # dispatcher.dispatch_once(integer) # class Dispatcher < Visitor - # attr_reader listeners: Hash[Symbol, Array[Listener]] - attr_reader :listeners + # A hash mapping event names to arrays of listeners that should be notified + # when that event is fired. + attr_reader :listeners #: Hash[Symbol, Array[untyped]] # Initialize a new dispatcher. + #-- + #: () -> void def initialize @listeners = {} end # Register a listener for one or more events. + #-- + #: (untyped, *Symbol) -> void def register(listener, *events) register_events(listener, events) end # Register all public methods of a listener that match the pattern # `on__(enter|leave)`. + #-- + #: (untyped) -> void def register_public_methods(listener) register_events(listener, listener.public_methods(false).grep(/\Aon_.+_(?:enter|leave)\z/)) end # Register a listener for the given events. + #-- + #: (untyped, Array[Symbol]) -> void private def register_events(listener, events) # :nodoc: events.each { |event| (listeners[event] ||= []) << listener } end @@ -60,11 +72,14 @@ module Prism alias dispatch visit # Dispatches a single event for `node` to all registered listeners. + #-- + #: (node node) -> void def dispatch_once(node) node.accept(DispatchOnce.new(listeners)) end <%- nodes.each do |node| -%> + #: (<%= node.name %> node) -> void def visit_<%= node.human %>(node) # :nodoc: listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) } super @@ -73,14 +88,17 @@ module Prism <%- end -%> class DispatchOnce < Visitor # :nodoc: - attr_reader :listeners + attr_reader :listeners #: Hash[Symbol, Array[untyped]] + #: (Hash[Symbol, Array[untyped]] listeners) -> void def initialize(listeners) @listeners = listeners end <%- nodes.each do |node| -%> # Dispatch enter and leave events for <%= node.name %> nodes. + #-- + #: (<%= node.name %> node) -> void def visit_<%= node.human %>(node) listeners[:on_<%= node.human %>_enter]&.each { |listener| listener.on_<%= node.human %>_enter(node) } listeners[:on_<%= node.human %>_leave]&.each { |listener| listener.on_<%= node.human %>_leave(node) } diff --git a/prism/templates/lib/prism/dot_visitor.rb.erb b/prism/templates/lib/prism/dot_visitor.rb.erb index 13c53af0d430d8..88ef1e1f36b18e 100644 --- a/prism/templates/lib/prism/dot_visitor.rb.erb +++ b/prism/templates/lib/prism/dot_visitor.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + require "cgi/escape" require "cgi/util" unless defined?(CGI::EscapeExt) @@ -6,14 +9,18 @@ module Prism # subtree into a graphviz dot graph. class DotVisitor < Visitor class Field # :nodoc: - attr_reader :name, :value, :port + attr_reader :name #: String + attr_reader :value #: String? + attr_reader :port #: bool + #: (String name, String? value, bool port) -> void def initialize(name, value, port) @name = name @value = value @port = port end + #: () -> String def to_dot if port "#{name}" @@ -24,17 +31,21 @@ module Prism end class Table # :nodoc: - attr_reader :name, :fields + attr_reader :name #: String + attr_reader :fields #: Array[Field] + #: (String name) -> void def initialize(name) @name = name @fields = [] end + #: (String name, ?String? value, ?port: bool) -> void def field(name, value = nil, port: false) fields << Field.new(name, value, port) end + #: () -> String def to_dot dot = <<~DOT @@ -50,26 +61,31 @@ module Prism end class Digraph # :nodoc: - attr_reader :nodes, :waypoints, :edges + attr_reader :nodes, :waypoints, :edges #: Array[String] + #: () -> void def initialize @nodes = [] @waypoints = [] @edges = [] end + #: (String value) -> void def node(value) nodes << value end + #: (String value) -> void def waypoint(value) waypoints << value end + #: (String value) -> void def edge(value) edges << value end + #: () -> String def to_dot <<~DOT digraph "Prism" { @@ -93,19 +109,24 @@ module Prism private_constant :Field, :Table, :Digraph # The digraph that is being built. - attr_reader :digraph + attr_reader :digraph #: Digraph # Initialize a new dot visitor. + #-- + #: () -> void def initialize @digraph = Digraph.new end # Convert this visitor into a graphviz dot graph string. + #-- + #: () -> String def to_dot digraph.to_dot end <%- nodes.each do |node| -%> + #: (<%= node.name %>) -> void def visit_<%= node.human %>(node) # :nodoc: table = Table.new("<%= node.name %>") id = node_id(node) @@ -151,7 +172,7 @@ module Prism <%- end -%> <%- end -%> - digraph.nodes << <<~DOT + digraph.node(<<~DOT) #{id} [ label=<#{table.to_dot.gsub(/\n/, "\n ")}> ]; @@ -164,11 +185,15 @@ module Prism private # Generate a unique node ID for a node throughout the digraph. + #-- + #: (node) -> String def node_id(node) # :nodoc: "Node_#{node.object_id}" end # Inspect a location to display the start and end line and columns in bytes. + #-- + #: (Location) -> String def location_inspect(location) # :nodoc: "(#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column})" end @@ -176,6 +201,8 @@ module Prism # Inspect a node that has <%= flag.human %> flags to display the flags as a # comma-separated list. + #-- + #: (<%= nodes.filter_map { |node| node.name if node.flags == flag }.join(" | ") %> node) -> String def <%= flag.human %>_inspect(node) # :nodoc: flags = [] #: Array[String] <%- flag.values.each do |value| -%> diff --git a/prism/templates/lib/prism/dsl.rb.erb b/prism/templates/lib/prism/dsl.rb.erb index e16ebb71101921..a75b8b253e240b 100644 --- a/prism/templates/lib/prism/dsl.rb.erb +++ b/prism/templates/lib/prism/dsl.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + module Prism # The DSL module provides a set of methods that can be used to create prism # nodes in a more concise manner. For example, instead of writing: @@ -56,17 +59,31 @@ module Prism extend self # Create a new Source object. + #-- + #: (String string) -> Source def source(string) Source.for(string) end # Create a new Location object. + #-- + #: (?source: Source, ?start_offset: Integer, ?length: Integer) -> Location def location(source: default_source, start_offset: 0, length: 0) Location.new(source, start_offset, length) end <%- nodes.each do |node| -%> + <%- + params = [ + ["source", "Source"], + ["node_id", "Integer"], + ["location", "Location"], + ["flags", "Integer"] + ].concat(node.fields.map { |field| [field.name, field.rbs_class] }) + -%> # Create a new <%= node.name %> node. + #-- + #: (<%= params.map { |(name, type)| "?#{name}: #{type}" }.join(", ") %>) -> <%= node.name %> def <%= node.human %>(<%= ["source: default_source", "node_id: 0", "location: default_location", "flags: 0", *node.fields.map { |field| case field when Prism::Template::NodeField @@ -100,6 +117,8 @@ module Prism <%- flags.each do |flag| -%> # Retrieve the value of one of the <%= flag.name %> flags. + #-- + #: (Symbol name) -> Integer def <%= flag.human.chomp("s") %>(name) case name <%- flag.values.each do |value| -%> @@ -114,18 +133,24 @@ module Prism # The default source object that gets attached to nodes and locations if no # source is specified. + #-- + #: () -> Source def default_source Source.for("") end # The default location object that gets attached to nodes if no location is # specified, which uses the given source. + #-- + #: () -> Location def default_location Location.new(default_source, 0, 0) end # The default node that gets attached to nodes if no node is specified for a # required node field. + #-- + #: (Source source, Location location) -> node def default_node(source, location) MissingNode.new(source, -1, location, 0) end diff --git a/prism/templates/lib/prism/inspect_visitor.rb.erb b/prism/templates/lib/prism/inspect_visitor.rb.erb index 9a33cb8110f076..820f5ae75fd7ee 100644 --- a/prism/templates/lib/prism/inspect_visitor.rb.erb +++ b/prism/templates/lib/prism/inspect_visitor.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + module Prism # This visitor is responsible for composing the strings that get returned by # the various #inspect methods defined on each of the nodes. @@ -7,8 +10,9 @@ module Prism # when we hit an element in that list. In this case, we have a special # command that replaces the subsequent indent with the given value. class Replace # :nodoc: - attr_reader :value + attr_reader :value #: String + #: (String value) -> void def initialize(value) @value = value end @@ -17,18 +21,25 @@ module Prism private_constant :Replace # The current prefix string. - attr_reader :indent # :nodoc: + # :stopdoc: + attr_reader :indent #: String + # :startdoc: # The list of commands that we need to execute in order to compose the # final string. - attr_reader :commands # :nodoc: + #: stopdoc: + attr_reader :commands #: Array[[String | node | Replace, String]] + # :startdoc: + #: (?String indent) -> void def initialize(indent = +"") # :nodoc: @indent = indent @commands = [] end # Compose an inspect string for the given node. + #-- + #: (node node) -> String def self.compose(node) visitor = new node.accept(visitor) @@ -36,6 +47,8 @@ module Prism end # Compose the final string. + #-- + #: () -> String def compose # :nodoc: buffer = +"" replace = nil @@ -65,6 +78,7 @@ module Prism end <%- nodes.each do |node| -%> + #: (<%= node.name %> node) -> void def visit_<%= node.human %>(node) # :nodoc: commands << [inspect_node(<%= node.name.inspect %>, node), indent] <%- (fields = [node.flags || Prism::Template::Flags.empty, *node.fields]).each_with_index do |field, index| -%> @@ -112,12 +126,16 @@ module Prism private # Compose a header for the given node. + #-- + #: (String name, node node) -> String def inspect_node(name, node) # :nodoc: location = node.location "@ #{name} (location: (#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column}))\n" end # Compose a string representing the given inner location field. + #-- + #: (Location? location) -> String def inspect_location(location) # :nodoc: if location "(#{location.start_line},#{location.start_column})-(#{location.end_line},#{location.end_column}) = #{location.slice.inspect}" diff --git a/prism/templates/lib/prism/mutation_compiler.rb.erb b/prism/templates/lib/prism/mutation_compiler.rb.erb index b223860f2f6ff2..2d555048d2e102 100644 --- a/prism/templates/lib/prism/mutation_compiler.rb.erb +++ b/prism/templates/lib/prism/mutation_compiler.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + module Prism # This visitor walks through the tree and copies each node as it is being # visited. This is useful for consumers that want to mutate the tree, as you @@ -5,6 +8,7 @@ module Prism class MutationCompiler < Compiler <%- nodes.each_with_index do |node, index| -%> <%= "\n" if index != 0 -%> + #: (<%= node.name %>) -> node? def visit_<%= node.human %>(node) # :nodoc: <%- fields = node.fields.select { |field| [Prism::Template::NodeField, Prism::Template::OptionalNodeField, Prism::Template::NodeListField].include?(field.class) } -%> <%- if fields.any? -%> diff --git a/prism/templates/lib/prism/node.rb.erb b/prism/templates/lib/prism/node.rb.erb index 8c88529c664f3c..817b59b477b529 100644 --- a/prism/templates/lib/prism/node.rb.erb +++ b/prism/templates/lib/prism/node.rb.erb @@ -1,26 +1,49 @@ -# :markup: markdown +#-- +# rbs_inline: enabled module Prism + # @rbs! + # interface _Repository + # def enter: (Integer node_id, Symbol field_name) -> Relocation::Entry + # end + # + # interface _Node + # def deconstruct: () -> Array[Prism::node?] + # def inspect: () -> String + # end + # + # type node = Node & _Node + # This represents a node in the tree. It is the parent class of all of the # various node types. class Node # A pointer to the source that this node was created from. - attr_reader :source # :nodoc: + # :stopdoc: + attr_reader :source #: Source private :source + # :startdoc: # A unique identifier for this node. This is used in a very specific # use case where you want to keep around a reference to a node without # having to keep around the syntax tree in memory. This unique identifier # will be consistent across multiple parses of the same source code. - attr_reader :node_id + attr_reader :node_id #: Integer + + # The location associated with this node. For lazily loading Location + # objects, we keep it as a packed integer until it is accessed. + # @rbs @location: Location | Integer # Save this node using a saved source so that it can be retrieved later. + #-- + #: (_Repository repository) -> Relocation::Entry def save(repository) repository.enter(node_id, :itself) end # A Location instance that represents the location of this node in the # source. + #-- + #: () -> Location def location location = @location return location if location.is_a?(Location) @@ -28,6 +51,8 @@ module Prism end # Save the location using a saved source so that it can be retrieved later. + #-- + #: (_Repository repository) -> Relocation::Entry def save_location(repository) repository.enter(node_id, :location) end @@ -38,22 +63,30 @@ module Prism # -------------------------------------------------------------------------- # Delegates to [`start_line`](rdoc-ref:Location#start_line) of the associated location object. + #-- + #: () -> Integer def start_line location.start_line end # Delegates to [`end_line`](rdoc-ref:Location#end_line) of the associated location object. + #-- + #: () -> Integer def end_line location.end_line end # Delegates to [`start_offset`](rdoc-ref:Location#start_offset) of the associated location object. + #-- + #: () -> Integer def start_offset location = @location location.is_a?(Location) ? location.start_offset : location >> 32 end # Delegates to [`end_offset`](rdoc-ref:Location#end_offset) of the associated location object. + #-- + #: () -> Integer def end_offset location = @location location.is_a?(Location) ? location.end_offset : ((location >> 32) + (location & 0xFFFFFFFF)) @@ -61,73 +94,99 @@ module Prism # Delegates to [`start_character_offset`](rdoc-ref:Location#start_character_offset) # of the associated location object. + #-- + #: () -> Integer def start_character_offset location.start_character_offset end # Delegates to [`end_character_offset`](rdoc-ref:Location#end_character_offset) # of the associated location object. + #-- + #: () -> Integer def end_character_offset location.end_character_offset end # Delegates to [`cached_start_code_units_offset`](rdoc-ref:Location#cached_start_code_units_offset) # of the associated location object. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_start_code_units_offset(cache) location.cached_start_code_units_offset(cache) end # Delegates to [`cached_end_code_units_offset`](rdoc-ref:Location#cached_end_code_units_offset) # of the associated location object. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_end_code_units_offset(cache) location.cached_end_code_units_offset(cache) end # Delegates to [`start_column`](rdoc-ref:Location#start_column) of the associated location object. + #-- + #: () -> Integer def start_column location.start_column end # Delegates to [`end_column`](rdoc-ref:Location#end_column) of the associated location object. + #-- + #: () -> Integer def end_column location.end_column end # Delegates to [`start_character_column`](rdoc-ref:Location#start_character_column) # of the associated location object. + #-- + #: () -> Integer def start_character_column location.start_character_column end # Delegates to [`end_character_column`](rdoc-ref:Location#end_character_column) # of the associated location object. + #-- + #: () -> Integer def end_character_column location.end_character_column end # Delegates to [`cached_start_code_units_column`](rdoc-ref:Location#cached_start_code_units_column) # of the associated location object. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_start_code_units_column(cache) location.cached_start_code_units_column(cache) end # Delegates to [`cached_end_code_units_column`](rdoc-ref:Location#cached_end_code_units_column) # of the associated location object. + #-- + #: (_CodeUnitsCache cache) -> Integer def cached_end_code_units_column(cache) location.cached_end_code_units_column(cache) end # Delegates to [`leading_comments`](rdoc-ref:Location#leading_comments) of the associated location object. + #-- + #: () -> Array[Comment] def leading_comments location.leading_comments end # Delegates to [`trailing_comments`](rdoc-ref:Location#trailing_comments) of the associated location object. + #-- + #: () -> Array[Comment] def trailing_comments location.trailing_comments end # Delegates to [`comments`](rdoc-ref:Location#comments) of the associated location object. + #-- + #: () -> Array[Comment] def comments location.comments end @@ -135,6 +194,8 @@ module Prism # :section: # Returns all of the lines of the source code associated with this node. + #-- + #: () -> Array[String] def source_lines location.source_lines end @@ -144,6 +205,8 @@ module Prism alias script_lines source_lines # Slice the location of the node from the source. + #-- + #: () -> String def slice location.slice end @@ -151,27 +214,37 @@ module Prism # Slice the location of the node from the source, starting at the beginning # of the line that the location starts on, ending at the end of the line # that the location ends on. + #-- + #: () -> String def slice_lines location.slice_lines end # An bitset of flags for this node. There are certain flags that are common # for all nodes, and then some nodes have specific flags. - attr_reader :flags # :nodoc: + # :stopdoc: + attr_reader :flags #: Integer protected :flags + # :startdoc: # Returns true if the node has the newline flag set. + #-- + #: () -> bool def newline? flags.anybits?(NodeFlags::NEWLINE) end # Returns true if the node has the static literal flag set. + #-- + #: () -> bool def static_literal? flags.anybits?(NodeFlags::STATIC_LITERAL) end # Similar to inspect, but respects the current level of indentation given by # the pretty print object. + #-- + #: (PP q) -> void def pretty_print(q) # :nodoc: q.seplist(inspect.chomp.each_line, -> { q.breakable }) do |line| q.text(line.chomp) @@ -180,6 +253,8 @@ module Prism end # Convert this node into a graphviz dot graph string. + #-- + #: () -> String def to_dot # @type self: node DotVisitor.new.tap { |visitor| accept(visitor) }.to_dot @@ -191,9 +266,11 @@ module Prism # # Important to note is that the column given to this method should be in # bytes, as opposed to characters or code units. + #-- + #: (Integer line, Integer column) -> Array[node] def tunnel(line, column) - queue = [self] #: Array[Prism::node] - result = [] #: Array[Prism::node] + queue = [self] #: Array[node] + result = [] #: Array[node] offset = source.byte_offset(line, column) while (node = queue.shift) @@ -215,9 +292,10 @@ module Prism # particular condition. # # node.breadth_first_search { |node| node.node_id == node_id } - # - def breadth_first_search(&block) - queue = [self] #: Array[Prism::node] + #-- + #: () { (node) -> bool } -> node? + def breadth_first_search(&blk) + queue = [self] #: Array[node] while (node = queue.shift) return node if yield node @@ -233,8 +311,9 @@ module Prism # particular condition. # # node.breadth_first_search_all { |node| node.is_a?(Prism::CallNode) } - # - def breadth_first_search_all(&block) + #-- + #: () { (node) -> bool } -> Array[node] + def breadth_first_search_all(&blk) queue = [self] #: Array[Prism::node] results = [] #: Array[Prism::node] @@ -250,6 +329,8 @@ module Prism # Returns a list of the fields that exist for this node class. Fields # describe the structure of the node. This kind of reflection is useful for # things like recursively visiting each node _and_ field in the tree. + #-- + #: () -> Array[Reflection::Field] def self.fields # This method should only be called on subclasses of Node, not Node # itself. @@ -265,12 +346,16 @@ module Prism # -------------------------------------------------------------------------- # Accepts a visitor and calls back into the specialized visit function. + #-- + #: (_Visitor visitor) -> untyped def accept(visitor) raise NoMethodError, "undefined method `accept' for #{inspect}" end # Returns an array of child nodes, including `nil`s in the place of optional # nodes that were not present. + #-- + #: () -> Array[node?] def child_nodes raise NoMethodError, "undefined method `child_nodes' for #{inspect}" end @@ -280,23 +365,32 @@ module Prism # With a block given, yields each child node. Without a block, returns # an enumerator that contains each child node. Excludes any `nil`s in # the place of optional nodes that were not present. - def each_child_node + #-- + #: () -> Enumerator[node, void] + #: () { (node) -> void } -> void + def each_child_node(&blk) raise NoMethodError, "undefined method `each_child_node' for #{inspect}" end # Returns an array of child nodes, excluding any `nil`s in the place of # optional nodes that were not present. + #-- + #: () -> Array[node] def compact_child_nodes raise NoMethodError, "undefined method `compact_child_nodes' for #{inspect}" end # Returns an array of child nodes and locations that could potentially have # comments attached to them. + #-- + #: () -> Array[node | Location] def comment_targets raise NoMethodError, "undefined method `comment_targets' for #{inspect}" end # Returns a string representation of the node. + #-- + #: () -> String def inspect raise NoMethodError, "undefined method `inspect' for #{inspect}" end @@ -313,6 +407,8 @@ module Prism # it uses a single integer comparison, but also because if you're on CRuby # you can take advantage of the fact that case statements with all symbol # keys will use a jump table. + #-- + #: () -> Symbol def type raise NoMethodError, "undefined method `type' for #{inspect}" end @@ -321,6 +417,8 @@ module Prism # splitting on the type of the node without having to do a long === chain. # Note that like #type, it will still be slower than using == for a single # class, but should be faster in a case statement or an array comparison. + #-- + #: () -> Symbol def self.type raise NoMethodError, "undefined method `type' for #{inspect}" end @@ -331,7 +429,13 @@ module Prism #<%= line %> <%- end -%> class <%= node.name -%> < Node + <%- node.fields.each do |field| -%> + # @rbs @<%= field.name %>: <%= field.rbs_class %> + <%- end -%> + # Initialize a new <%= node.name %> node. + #-- + #: (Source source, Integer node_id, Location location, Integer flags, <%= node.fields.map { |field| "#{field.rbs_class} #{field.name}" }.join(", ") %>) -> void def initialize(<%= ["source", "node_id", "location", "flags", *node.fields.map(&:name)].join(", ") %>) @source = source @node_id = node_id @@ -357,11 +461,15 @@ module Prism # ---------------------------------------------------------------------------------- # See Node.accept. + #-- + #: (_Visitor visitor) -> untyped def accept(visitor) visitor.visit_<%= node.human %>(self) end # See Node.child_nodes. + #-- + #: () -> Array[node?] def child_nodes [<%= node.fields.map { |field| case field @@ -372,7 +480,10 @@ module Prism end # See Node.each_child_node. - def each_child_node + #-- + #: () -> Enumerator[node, void] + #: () { (node) -> void } -> void + def each_child_node(&blk) return to_enum(:each_child_node) unless block_given? <%- node.fields.each do |field| -%> @@ -380,7 +491,7 @@ module Prism <%- when Prism::Template::NodeField -%> yield <%= field.name %> <%- when Prism::Template::OptionalNodeField -%> - yield <%= field.name %> if <%= field.name %> + if (<%= field.name %> = self.<%= field.name %>); yield <%= field.name %>; end <%- when Prism::Template::NodeListField -%> <%= field.name %>.each { |node| yield node } <%- end -%> @@ -388,6 +499,8 @@ module Prism end # See Node.compact_child_nodes. + #-- + #: () -> Array[node] def compact_child_nodes <%- if node.fields.any? { |field| field.is_a?(Prism::Template::OptionalNodeField) } -%> compact = [] #: Array[Prism::node] @@ -396,7 +509,7 @@ module Prism <%- when Prism::Template::NodeField -%> compact << <%= field.name %> <%- when Prism::Template::OptionalNodeField -%> - compact << <%= field.name %> if <%= field.name %> + if (<%= field.name %> = self.<%= field.name %>); compact << <%= field.name %>; end <%- when Prism::Template::NodeListField -%> compact.concat(<%= field.name %>) <%- end -%> @@ -413,6 +526,8 @@ module Prism end # See Node.comment_targets. + #-- + #: () -> Array[node | Location] def comment_targets [<%= node.fields.map { |field| case field @@ -426,26 +541,34 @@ module Prism # copy(**fields) -> <%= node.name %> # # Creates a copy of self with the given fields, using self as the template. + #-- + #: (?node_id: Integer, ?location: Location, ?flags: Integer, <%= node.fields.map { |field| "?#{field.name}: #{field.rbs_class}" }.join(", ") %>) -> <%= node.name %> def copy(<%= (["node_id", "location", "flags"] + node.fields.map(&:name)).map { |field| "#{field}: self.#{field}" }.join(", ") %>) <%= node.name %>.new(<%= ["source", "node_id", "location", "flags", *node.fields.map(&:name)].join(", ") %>) end alias deconstruct child_nodes + #: (Array[Symbol]? keys) -> Hash[Symbol, untyped] def deconstruct_keys(keys) # :nodoc: { <%= (["node_id: node_id", "location: location"] + node.fields.map { |field| "#{field.name}: #{field.name}" }).join(", ") %> } end # See `Node#type`. + #-- + #: () -> :<%= node.human %> def type :<%= node.human %> end # See `Node.type`. + #-- + #: () -> :<%= node.human %> def self.type :<%= node.human %> end + #: () -> String def inspect # :nodoc: InspectVisitor.compose(self) end @@ -456,6 +579,8 @@ module Prism <%- node_flags.values.each do |value| -%> # :category: Flags # <%= value.comment %> + #-- + #: () -> bool def <%= value.name.downcase %>? flags.anybits?(<%= node_flags.name %>::<%= value.name %>) end @@ -476,6 +601,8 @@ module Prism #<%= line %> <%- end -%> <%- end -%> + #-- + #: () -> Location def <%= field.name %> location = @<%= field.name %> return location if location.is_a?(Location) @@ -485,6 +612,8 @@ module Prism # :category: Repository # Save the <%= field.name %> location using the given saved source so that # it can be retrieved later. + #-- + #: (_Repository repository) -> Relocation::Entry def save_<%= field.name %>(repository) repository.enter(node_id, :<%= field.name %>) end @@ -501,6 +630,8 @@ module Prism #<%= line %> <%- end -%> <%- end -%> + #-- + #: () -> Location? def <%= field.name %> location = @<%= field.name %> case location @@ -516,6 +647,8 @@ module Prism # :category: Repository # Save the <%= field.name %> location using the given saved source so that # it can be retrieved later. + #-- + #: (_Repository repository) -> Relocation::Entry? def save_<%= field.name %>(repository) repository.enter(node_id, :<%= field.name %>) unless @<%= field.name %>.nil? end @@ -530,6 +663,8 @@ module Prism #<%= line %> <%- end -%> <%- end -%> + #-- + #: () -> <%= field.rbs_class %> def <%= field.name %> @<%= field.name %> end @@ -547,6 +682,8 @@ module Prism # <%= field.name.delete_suffix("_loc") %> -> String # # Slice the location of <%= field.name %> from the source. + #-- + #: () -> String def <%= field.name.delete_suffix("_loc") %> <%= field.name %>.slice end @@ -558,6 +695,8 @@ module Prism # <%= field.name.delete_suffix("_loc") %> -> String | nil # # Slice the location of <%= field.name %> from the source. + #-- + #: () -> String? def <%= field.name.delete_suffix("_loc") %> <%= field.name %>&.slice end @@ -566,6 +705,7 @@ module Prism <%- end -%> # :section: + #: (untyped other) -> boolish def ===(other) # :nodoc: other.is_a?(<%= node.name %>)<%= " &&" if (fields = [*node.flags, *node.fields]).any? %> <%- fields.each_with_index do |field, index| -%> diff --git a/prism/templates/lib/prism/reflection.rb.erb b/prism/templates/lib/prism/reflection.rb.erb index 6c8b2f4d257714..0012f120b2bf01 100644 --- a/prism/templates/lib/prism/reflection.rb.erb +++ b/prism/templates/lib/prism/reflection.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + module Prism # The Reflection module provides the ability to reflect on the structure of # the syntax tree itself, as opposed to looking at a single syntax tree. This @@ -7,9 +10,11 @@ module Prism # for all other field types. class Field # The name of the field. - attr_reader :name + attr_reader :name #: Symbol # Initializes the field with the given name. + #-- + #: (Symbol name) -> void def initialize(name) @name = name end @@ -83,9 +88,11 @@ module Prism # the bitset should be accessed through their query methods. class FlagsField < Field # The names of the flags in the bitset. - attr_reader :flags + attr_reader :flags #: Array[Symbol] # Initializes the flags field with the given name and flags. + #-- + #: (Symbol name, Array[Symbol] flags) -> void def initialize(name, flags) super(name) @flags = flags @@ -93,6 +100,8 @@ module Prism end # Returns the fields for the given node. + #-- + #: (singleton(Node) node) -> Array[Field] def self.fields_for(node) case node.type <%- nodes.each do |node| -%> diff --git a/prism/templates/lib/prism/serialize.rb.erb b/prism/templates/lib/prism/serialize.rb.erb index 63ef07cb6e9dbd..433b5207883e2e 100644 --- a/prism/templates/lib/prism/serialize.rb.erb +++ b/prism/templates/lib/prism/serialize.rb.erb @@ -1,3 +1,6 @@ +#-- +# rbs_inline: enabled + require "stringio" require_relative "polyfill/unpack1" @@ -20,6 +23,8 @@ module Prism # # The formatting of the source of this method is purposeful to illustrate # the structure of the serialized data. + #-- + #: (String input, String serialized, bool freeze) -> ParseResult def self.load_parse(input, serialized, freeze) input = input.dup source = Source.for(input) @@ -43,7 +48,7 @@ module Prism constant_pool = ConstantPool.new(input, serialized, cpool_base, cpool_size) - node = loader.load_node(constant_pool, encoding, freeze) + node = loader.load_node(constant_pool, encoding, freeze) #: ProgramNode loader.load_constant_pool(constant_pool) raise unless loader.eof? @@ -73,6 +78,8 @@ module Prism # # The formatting of the source of this method is purposeful to illustrate # the structure of the serialized data. + #-- + #: (String input, String serialized, bool freeze) -> LexResult def self.load_lex(input, serialized, freeze) source = Source.for(input) loader = Loader.new(source, serialized) @@ -117,6 +124,8 @@ module Prism # # The formatting of the source of this method is purposeful to illustrate # the structure of the serialized data. + #-- + #: (String input, String serialized, bool freeze) -> Array[Comment] def self.load_parse_comments(input, serialized, freeze) source = Source.for(input) loader = Loader.new(source, serialized) @@ -139,6 +148,8 @@ module Prism # # The formatting of the source of this method is purposeful to illustrate # the structure of the serialized data. + #-- + #: (String input, String serialized, bool freeze) -> ParseLexResult def self.load_parse_lex(input, serialized, freeze) source = Source.for(input) loader = Loader.new(source, serialized) @@ -162,11 +173,11 @@ module Prism constant_pool = ConstantPool.new(input, serialized, cpool_base, cpool_size) - node = loader.load_node(constant_pool, encoding, freeze) + node = loader.load_node(constant_pool, encoding, freeze) #: ProgramNode loader.load_constant_pool(constant_pool) raise unless loader.eof? - value = [node, tokens] + value = [node, tokens] #: [ProgramNode, Array[[Token, Integer]]] result = ParseLexResult.new(value, comments, magic_comments, data_loc, errors, warnings, source) tokens.each do |token| @@ -189,8 +200,14 @@ module Prism end class ConstantPool # :nodoc: - attr_reader :size + attr_reader :size #: Integer + + # @rbs @input: String + # @rbs @serialized: String + # @rbs @base: Integer + # @rbs @pool: Array[Symbol?] + #: (String input, String serialized, Integer base, Integer size) -> void def initialize(input, serialized, base, size) @input = input @serialized = serialized @@ -199,17 +216,18 @@ module Prism @pool = Array.new(size, nil) end + #: (Integer index, Encoding encoding) -> Symbol def get(index, encoding) @pool[index] ||= begin offset = @base + index * 8 - start = @serialized.unpack1("L", offset: offset) - length = @serialized.unpack1("L", offset: offset + 4) + start = @serialized.unpack1("L", offset: offset) #: Integer + length = @serialized.unpack1("L", offset: offset + 4) #: Integer if start.nobits?(1 << 31) - @input.byteslice(start, length).force_encoding(encoding).to_sym + (@input.byteslice(start, length) or raise).force_encoding(encoding).to_sym else - @serialized.byteslice(start & ((1 << 31) - 1), length).force_encoding(encoding).to_sym + (@serialized.byteslice(start & ((1 << 31) - 1), length) or raise).force_encoding(encoding).to_sym end end end @@ -217,6 +235,7 @@ module Prism if RUBY_ENGINE == "truffleruby" # StringIO is synchronized and that adds a high overhead on TruffleRuby. + # @rbs skip class FastStringIO # :nodoc: attr_accessor :pos @@ -246,8 +265,11 @@ module Prism end class Loader # :nodoc: - attr_reader :input, :io, :source + attr_reader :input #: String + attr_reader :io #: StringIO + attr_reader :source #: Source + #: (Source source, String serialized) -> void def initialize(source, serialized) @input = source.source.dup raise unless serialized.encoding == Encoding::BINARY @@ -256,40 +278,46 @@ module Prism define_load_node_lambdas if RUBY_ENGINE != "ruby" end + #: () -> bool def eof? io.getbyte io.eof? end + #: (ConstantPool constant_pool) -> void def load_constant_pool(constant_pool) trailer = 0 constant_pool.size.times do |index| - start, length = io.read(8).unpack("L2") + start, length = (io.read(8) or raise).unpack("L2") #: [Integer, Integer] trailer += length if start.anybits?(1 << 31) end io.read(trailer) end + #: () -> void def load_header raise "Invalid serialization" if io.read(5) != "PRISM" - raise "Invalid serialization" if io.read(3).unpack("C3") != [MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION] + raise "Invalid serialization" if (io.read(3) or raise).unpack("C3") != [MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION] raise "Invalid serialization (location fields must be included but are not)" if io.getbyte != 0 end + #: () -> Encoding def load_encoding - encoding = Encoding.find(io.read(load_varuint)) + encoding = Encoding.find((io.read(load_varuint) or raise)) or raise @input = input.force_encoding(encoding).freeze encoding end + #: (bool freeze) -> Array[Integer] def load_line_offsets(freeze) offsets = Array.new(load_varuint) { load_varuint } offsets.freeze if freeze offsets end + #: (bool freeze) -> Array[Comment] def load_comments(freeze) comments = Array.new(load_varuint) do @@ -297,6 +325,7 @@ module Prism case load_varuint when 0 then InlineComment.new(load_location_object(freeze)) when 1 then EmbDocComment.new(load_location_object(freeze)) + else raise end comment.freeze if freeze @@ -307,6 +336,7 @@ module Prism comments end + #: (bool freeze) -> Array[MagicComment] def load_magic_comments(freeze) magic_comments = Array.new(load_varuint) do @@ -331,10 +361,11 @@ module Prism <%- warnings.each do |warning| -%> <%= warning.name.downcase.to_sym.inspect %>, <%- end -%> - ].freeze + ].freeze #: Array[Symbol] private_constant :DIAGNOSTIC_TYPES + #: () -> Symbol def load_error_level level = io.getbyte @@ -350,6 +381,7 @@ module Prism end end + #: (Encoding encoding, bool freeze) -> Array[ParseError] def load_errors(encoding, freeze) errors = Array.new(load_varuint) do @@ -369,6 +401,7 @@ module Prism errors end + #: () -> Symbol def load_warning_level level = io.getbyte @@ -382,6 +415,7 @@ module Prism end end + #: (Encoding encoding, bool freeze) -> Array[ParseWarning] def load_warnings(encoding, freeze) warnings = Array.new(load_varuint) do @@ -401,8 +435,9 @@ module Prism warnings end + #: () -> Array[[Token, Integer]] def load_tokens - tokens = [] + tokens = [] #: Array[[Token, Integer]] while (type = TOKEN_TYPES.fetch(load_varuint)) start = load_varuint @@ -420,25 +455,29 @@ module Prism # variable-length integer using https://en.wikipedia.org/wiki/LEB128 # This is also what protobuf uses: https://protobuf.dev/programming-guides/encoding/#varints + #-- + #: () -> Integer def load_varuint - n = io.getbyte + n = (io.getbyte or raise) if n < 128 n else n -= 128 shift = 0 - while (b = io.getbyte) >= 128 + while (b = (io.getbyte or raise)) >= 128 n += (b - 128) << (shift += 7) end n + (b << (shift + 7)) end end + #: () -> Integer def load_varsint n = load_varuint (n >> 1) ^ (-(n & 1)) end + #: () -> Integer def load_integer negative = io.getbyte != 0 length = load_varuint @@ -450,14 +489,17 @@ module Prism value end + #: () -> Float def load_double - io.read(8).unpack1("D") + (io.read(8) or raise).unpack1("D") #: Float end + #: () -> Integer def load_uint32 - io.read(4).unpack1("L") + (io.read(4) or raise).unpack1("L") #: Integer end + #: (ConstantPool constant_pool, Encoding encoding, bool freeze) -> node? def load_optional_node(constant_pool, encoding, freeze) if io.getbyte != 0 io.pos -= 1 @@ -465,14 +507,16 @@ module Prism end end + #: (Encoding encoding) -> String def load_embedded_string(encoding) - io.read(load_varuint).force_encoding(encoding).freeze + (io.read(load_varuint) or raise).force_encoding(encoding).freeze end + #: (Encoding encoding) -> String def load_string(encoding) case (type = io.getbyte) when 1 - input.byteslice(load_varuint, load_varuint).force_encoding(encoding).freeze + (input.byteslice(load_varuint, load_varuint) or raise).force_encoding(encoding).freeze when 2 load_embedded_string(encoding) else @@ -480,75 +524,116 @@ module Prism end end + #: (bool freeze) -> Location def load_location_object(freeze) location = Location.new(source, load_varuint, load_varuint) location.freeze if freeze location end + # Load a location object from the serialized data. Note that we are lying + # about the signature a bit here, because we sometimes load it as a packed + # integer instead of an object. + #-- + #: (bool freeze) -> Location def load_location(freeze) return load_location_object(freeze) if freeze - (load_varuint << 32) | load_varuint + (load_varuint << 32) | load_varuint #: Location end + # Load an optional location object from the serialized data if it is + # present. Note that we are lying about the signature a bit here, because + # we sometimes load it as a packed integer instead of an object. + #-- + #: (bool freeze) -> Location? def load_optional_location(freeze) load_location(freeze) if io.getbyte != 0 end + #: (bool freeze) -> Location? def load_optional_location_object(freeze) load_location_object(freeze) if io.getbyte != 0 end + #: (ConstantPool constant_pool, Encoding encoding) -> Symbol def load_constant(constant_pool, encoding) index = load_varuint constant_pool.get(index - 1, encoding) end + #: (ConstantPool constant_pool, Encoding encoding) -> Symbol? def load_optional_constant(constant_pool, encoding) index = load_varuint constant_pool.get(index - 1, encoding) if index != 0 end if RUBY_ENGINE == "ruby" + #: (ConstantPool constant_pool, Encoding encoding, bool freeze) -> node def load_node(constant_pool, encoding, freeze) type = io.getbyte node_id = load_varuint - location = load_location(freeze) - value = case type - <%- nodes.each_with_index do |node, index| -%> - when <%= index + 1 %> then - <%- if node.needs_serialized_length? -%> - load_uint32 - <%- end -%> - <%= node.name %>.new(<%= ["source", "node_id", "location", "load_varuint", *node.fields.map { |field| - case field - when Prism::Template::NodeField then "load_node(constant_pool, encoding, freeze)" - when Prism::Template::OptionalNodeField then "load_optional_node(constant_pool, encoding, freeze)" - when Prism::Template::StringField then "load_string(encoding)" - when Prism::Template::NodeListField then "Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }.tap { |nodes| nodes.freeze if freeze }" - when Prism::Template::ConstantField then "load_constant(constant_pool, encoding)" - when Prism::Template::OptionalConstantField then "load_optional_constant(constant_pool, encoding)" - when Prism::Template::ConstantListField then "Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }" - when Prism::Template::LocationField then "load_location(freeze)" - when Prism::Template::OptionalLocationField then "load_optional_location(freeze)" - when Prism::Template::UInt8Field then "io.getbyte" - when Prism::Template::UInt32Field then "load_varuint" - when Prism::Template::IntegerField then "load_integer" - when Prism::Template::DoubleField then "load_double" - else raise - end - }].join(", ") -%>) + location = load_location(freeze) #: Location + value = + case type + <%- nodes.each_with_index do |node, index| -%> + when <%= index + 1 %> + <%- if node.needs_serialized_length? -%> + load_uint32 + <%- end -%> + <%= node.name %>.new( + source, + node_id, + location, + load_varuint, + <%- node.fields.each do |field| -%> + <%- case field -%> + <%- when Prism::Template::NodeField -%> + load_node(constant_pool, encoding, freeze), #: <%= field.rbs_class %> + <%- when Prism::Template::OptionalNodeField -%> + load_optional_node(constant_pool, encoding, freeze), #: <%= field.rbs_class %> + <%- when Prism::Template::StringField -%> + load_string(encoding), + <%- when Prism::Template::NodeListField -%> + Array.new(load_varuint) do + load_node(constant_pool, encoding, freeze) #: <%= field.element_rbs_class %> + end.tap { |nodes| nodes.freeze if freeze }, + <%- when Prism::Template::ConstantField -%> + load_constant(constant_pool, encoding), + <%- when Prism::Template::OptionalConstantField -%> + load_optional_constant(constant_pool, encoding), + <%- when Prism::Template::ConstantListField -%> + Array.new(load_varuint) { load_constant(constant_pool, encoding) }.tap { |constants| constants.freeze if freeze }, + <%- when Prism::Template::LocationField -%> + load_location(freeze), + <%- when Prism::Template::OptionalLocationField -%> + load_optional_location(freeze), + <%- when Prism::Template::UInt8Field -%> + (io.getbyte or raise), + <%- when Prism::Template::UInt32Field -%> + load_varuint, + <%- when Prism::Template::IntegerField -%> + load_integer, + <%- when Prism::Template::DoubleField -%> + load_double, + <%- else raise -%> + <%- end -%> + <%- end -%> + ) <%- end -%> - end + else + raise "Unknown node type: #{type}" + end value.freeze if freeze value end else + # @rbs skip def load_node(constant_pool, encoding, freeze) - @load_node_lambdas[io.getbyte].call(constant_pool, encoding, freeze) + @load_node_lambdas[(io.getbyte or raise)].call(constant_pool, encoding, freeze) end + # @rbs skip def define_load_node_lambdas @load_node_lambdas = [ nil, @@ -559,24 +644,46 @@ module Prism <%- if node.needs_serialized_length? -%> load_uint32 <%- end -%> - value = <%= node.name %>.new(<%= ["source", "node_id", "location", "load_varuint", *node.fields.map { |field| - case field - when Prism::Template::NodeField then "load_node(constant_pool, encoding, freeze)" - when Prism::Template::OptionalNodeField then "load_optional_node(constant_pool, encoding, freeze)" - when Prism::Template::StringField then "load_string(encoding)" - when Prism::Template::NodeListField then "Array.new(load_varuint) { load_node(constant_pool, encoding, freeze) }" - when Prism::Template::ConstantField then "load_constant(constant_pool, encoding)" - when Prism::Template::OptionalConstantField then "load_optional_constant(constant_pool, encoding)" - when Prism::Template::ConstantListField then "Array.new(load_varuint) { load_constant(constant_pool, encoding) }" - when Prism::Template::LocationField then "load_location(freeze)" - when Prism::Template::OptionalLocationField then "load_optional_location(freeze)" - when Prism::Template::UInt8Field then "io.getbyte" - when Prism::Template::UInt32Field then "load_varuint" - when Prism::Template::IntegerField then "load_integer" - when Prism::Template::DoubleField then "load_double" - else raise - end - }].join(", ") -%>) + value = + <%= node.name %>.new( + source, + node_id, + location, + load_varuint, + <%- node.fields.map do |field| -%> + <%- case field -%> + <%- when Prism::Template::NodeField -%> + load_node(constant_pool, encoding, freeze), #: <%= field.rbs_class %> + <%- when Prism::Template::OptionalNodeField -%> + load_optional_node(constant_pool, encoding, freeze), #: <%= field.rbs_class %> + <%- when Prism::Template::StringField -%> + load_string(encoding), + <%- when Prism::Template::NodeListField -%> + Array.new(load_varuint) do + load_node(constant_pool, encoding, freeze) #: <%= field.element_rbs_class %> + end, + <%- when Prism::Template::ConstantField -%> + load_constant(constant_pool, encoding), + <%- when Prism::Template::OptionalConstantField -%> + load_optional_constant(constant_pool, encoding), + <%- when Prism::Template::ConstantListField -%> + Array.new(load_varuint) { load_constant(constant_pool, encoding) }, + <%- when Prism::Template::LocationField -%> + load_location(freeze), + <%- when Prism::Template::OptionalLocationField -%> + load_optional_location(freeze), + <%- when Prism::Template::UInt8Field -%> + (io.getbyte or raise), + <%- when Prism::Template::UInt32Field -%> + load_varuint, + <%- when Prism::Template::IntegerField -%> + load_integer, + <%- when Prism::Template::DoubleField -%> + load_double, + <%- else raise -%> + <%- end -%> + <%- end -%> + ) value.freeze if freeze value }, @@ -584,6 +691,10 @@ module Prism ] end end + + # @rbs! + # @load_node_lambdas: Array[Proc] + # def define_load_node_lambdas: () -> void end # The token types that can be indexed by their enum values. @@ -592,7 +703,7 @@ module Prism <%- tokens.each do |token| -%> <%= token.name.to_sym.inspect %>, <%- end -%> - ].freeze + ].freeze #: Array[Symbol?] private_constant :MAJOR_VERSION, :MINOR_VERSION, :PATCH_VERSION private_constant :ConstantPool, :FastStringIO, :Loader, :TOKEN_TYPES diff --git a/prism/templates/lib/prism/visitor.rb.erb b/prism/templates/lib/prism/visitor.rb.erb index 76f907724fd5da..f23e87d99ec9b2 100644 --- a/prism/templates/lib/prism/visitor.rb.erb +++ b/prism/templates/lib/prism/visitor.rb.erb @@ -1,4 +1,14 @@ +#-- +# rbs_inline: enabled + module Prism + # @rbs! + # interface _Visitor + # <% nodes.each do |node| %> + # def visit_<%= node.human %>: (<%= node.name %>) -> void + # <% end %> + # end + # A class that knows how to walk down the tree. None of the individual visit # methods are implemented on this visitor, so it forces the consumer to # implement each one that they need. For a default implementation that @@ -6,18 +16,24 @@ module Prism class BasicVisitor # Calls `accept` on the given node if it is not `nil`, which in turn should # call back into this visitor by calling the appropriate `visit_*` method. + #-- + #: (node? node) -> void def visit(node) # @type self: _Visitor node&.accept(self) end # Visits each node in `nodes` by calling `accept` on each one. + #-- + #: (Array[node?] nodes) -> void def visit_all(nodes) # @type self: _Visitor nodes.each { |node| node&.accept(self) } end # Visits the child nodes of `node` by calling `accept` on each one. + #-- + #: (node node) -> void def visit_child_nodes(node) # @type self: _Visitor node.each_child_node { |node| node.accept(self) } @@ -47,6 +63,8 @@ module Prism <%- nodes.each_with_index do |node, index| -%> <%= "\n" if index != 0 -%> # Visit a <%= node.name %> node + #-- + #: (<%= node.name %> node) -> void def visit_<%= node.human %>(node) node.each_child_node { |node| node.accept(self) } end diff --git a/prism/templates/src/diagnostic.c.erb b/prism/templates/src/diagnostic.c.erb index 88f8525f8008fb..a11c7893d3acda 100644 --- a/prism/templates/src/diagnostic.c.erb +++ b/prism/templates/src/diagnostic.c.erb @@ -488,7 +488,7 @@ pm_diagnostic_list_append_format(pm_list_t *list, uint32_t start, uint32_t lengt size_t message_length = (size_t) (result + 1); char *message = (char *) xmalloc(message_length); if (message == NULL) { - xfree(diagnostic); + xfree_sized(diagnostic, sizeof(pm_diagnostic_t)); return false; } @@ -519,7 +519,7 @@ pm_diagnostic_list_free(pm_list_t *list) { pm_diagnostic_t *next = (pm_diagnostic_t *) node->node.next; if (node->owned) xfree((void *) node->message); - xfree(node); + xfree_sized(node, sizeof(pm_diagnostic_t)); node = next; } diff --git a/prism/templates/src/node.c.erb b/prism/templates/src/node.c.erb index f1709a0249c3d7..e42a8e5b700e2d 100644 --- a/prism/templates/src/node.c.erb +++ b/prism/templates/src/node.c.erb @@ -32,7 +32,11 @@ pm_node_list_grow(pm_node_list_t *list, size_t size) { next_capacity = double_capacity; } - pm_node_t **nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * next_capacity); + pm_node_t **nodes = (pm_node_t **) xrealloc_sized( + list->nodes, + sizeof(pm_node_t *) * next_capacity, + sizeof(pm_node_t *) * list->capacity + ); if (nodes == NULL) return false; list->nodes = nodes; @@ -79,7 +83,7 @@ pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other) { void pm_node_list_free(pm_node_list_t *list) { if (list->capacity > 0) { - xfree(list->nodes); + xfree_sized(list->nodes, sizeof(pm_node_t *) * list->capacity); *list = (pm_node_list_t) { 0 }; } } @@ -132,6 +136,7 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { <%- raise -%> <%- end -%> <%- end -%> + xfree_sized(node, sizeof(pm_<%= node.human %>_t)); break; } <%- end -%> @@ -140,7 +145,6 @@ pm_node_destroy(pm_parser_t *parser, pm_node_t *node) { assert(false && "unreachable"); break; } - xfree(node); } /** diff --git a/prism/templates/template.rb b/prism/templates/template.rb index 18da0647a007bd..e571c58bf298a8 100755 --- a/prism/templates/template.rb +++ b/prism/templates/template.rb @@ -150,7 +150,7 @@ def rbs_class if specific_kind specific_kind elsif union_kind - union_kind.join(" | ") + "(#{union_kind.join(" | ")})" else "Prism::node" end @@ -166,16 +166,6 @@ def call_seq_type end end - def rbi_class - if specific_kind - "Prism::#{specific_kind}" - elsif union_kind - "T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")})" - else - "Prism::Node" - end - end - def check_field_kind if union_kind "[#{union_kind.join(', ')}].include?(#{name}.class)" @@ -192,7 +182,7 @@ def rbs_class if specific_kind "#{specific_kind}?" elsif union_kind - [*union_kind, "nil"].join(" | ") + "(#{union_kind.join(" | ")})?" else "Prism::node?" end @@ -208,16 +198,6 @@ def call_seq_type end end - def rbi_class - if specific_kind - "T.nilable(Prism::#{specific_kind})" - elsif union_kind - "T.nilable(T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")}))" - else - "T.nilable(Prism::Node)" - end - end - def check_field_kind if union_kind "[#{union_kind.join(', ')}, NilClass].include?(#{name}.class)" @@ -230,16 +210,20 @@ def check_field_kind # This represents a field on a node that is a list of nodes. We pass them as # references and store them directly on the struct. class NodeListField < NodeKindField - def rbs_class + def element_rbs_class if specific_kind - "Array[#{specific_kind}]" + "#{specific_kind}" elsif union_kind - "Array[#{union_kind.join(" | ")}]" + "#{union_kind.join(" | ")}" else - "Array[Prism::node]" + "Prism::node" end end + def rbs_class + "Array[#{element_rbs_class}]" + end + def call_seq_type if specific_kind "Array[#{specific_kind}]" @@ -250,16 +234,6 @@ def call_seq_type end end - def rbi_class - if specific_kind - "T::Array[Prism::#{specific_kind}]" - elsif union_kind - "T::Array[T.any(#{union_kind.map { |kind| "Prism::#{kind}" }.join(", ")})]" - else - "T::Array[Prism::Node]" - end - end - def java_type "#{super}[]" end @@ -284,10 +258,6 @@ def call_seq_type "Symbol" end - def rbi_class - "Symbol" - end - def java_type JAVA_STRING_TYPE end @@ -304,10 +274,6 @@ def call_seq_type "Symbol | nil" end - def rbi_class - "T.nilable(Symbol)" - end - def java_type JAVA_STRING_TYPE end @@ -324,10 +290,6 @@ def call_seq_type "Array[Symbol]" end - def rbi_class - "T::Array[Symbol]" - end - def java_type "#{JAVA_STRING_TYPE}[]" end @@ -343,10 +305,6 @@ def call_seq_type "String" end - def rbi_class - "String" - end - def java_type "byte[]" end @@ -366,10 +324,6 @@ def call_seq_type "Location" end - def rbi_class - "Prism::Location" - end - def java_type "Location" end @@ -389,10 +343,6 @@ def call_seq_type "Location | nil" end - def rbi_class - "T.nilable(Prism::Location)" - end - def java_type "Location" end @@ -408,10 +358,6 @@ def call_seq_type "Integer" end - def rbi_class - "Integer" - end - def java_type "int" end @@ -427,10 +373,6 @@ def call_seq_type "Integer" end - def rbi_class - "Integer" - end - def java_type "int" end @@ -447,10 +389,6 @@ def call_seq_type "Integer" end - def rbi_class - "Integer" - end - def java_type "Object" end @@ -467,10 +405,6 @@ def call_seq_type "Float" end - def rbi_class - "Float" - end - def java_type "double" end @@ -625,8 +559,7 @@ def render(name, write_to: nil) extension = File.extname(filepath.gsub(".erb", "")) heading = - case extension - when ".rb" + if extension == ".rb" <<~HEADING # frozen_string_literal: true # :markup: markdown @@ -639,24 +572,6 @@ def render(name, write_to: nil) ++ =end - HEADING - when ".rbs" - <<~HEADING - # This file is generated by the templates/template.rb script and should not be - # modified manually. See #{filepath} - # if you are looking to modify the template - - HEADING - when ".rbi" - <<~HEADING - # typed: strict - - =begin - This file is generated by the templates/template.rb script and should not be - modified manually. See #{filepath} - if you are looking to modify the template - =end - HEADING else <<~HEADING @@ -741,16 +656,7 @@ def locals "src/node.c", "src/prettyprint.c", "src/serialize.c", - "src/token_type.c", - "rbi/prism/dsl.rbi", - "rbi/prism/node.rbi", - "rbi/prism/visitor.rbi", - "sig/prism.rbs", - "sig/prism/dsl.rbs", - "sig/prism/mutation_compiler.rbs", - "sig/prism/node.rbs", - "sig/prism/visitor.rbs", - "sig/prism/_private/dot_visitor.rbs" + "src/token_type.c" ] end end diff --git a/prism/util/pm_buffer.c b/prism/util/pm_buffer.c index 2136a7c43eefd5..9e392427c6bae5 100644 --- a/prism/util/pm_buffer.c +++ b/prism/util/pm_buffer.c @@ -50,6 +50,7 @@ pm_buffer_length(const pm_buffer_t *buffer) { static inline bool pm_buffer_append_length(pm_buffer_t *buffer, size_t length) { size_t next_length = buffer->length + length; + const size_t original_capacity = buffer->capacity; if (next_length > buffer->capacity) { if (buffer->capacity == 0) { @@ -60,7 +61,7 @@ pm_buffer_append_length(pm_buffer_t *buffer, size_t length) { buffer->capacity *= 2; } - buffer->value = xrealloc(buffer->value, buffer->capacity); + buffer->value = xrealloc_sized(buffer->value, buffer->capacity, original_capacity); if (buffer->value == NULL) return false; } @@ -353,5 +354,5 @@ pm_buffer_insert(pm_buffer_t *buffer, size_t index, const char *value, size_t le */ void pm_buffer_free(pm_buffer_t *buffer) { - xfree(buffer->value); + xfree_sized(buffer->value, buffer->capacity); } diff --git a/prism/util/pm_constant_pool.c b/prism/util/pm_constant_pool.c index 922ce6a18cfaff..bde7f959ea4318 100644 --- a/prism/util/pm_constant_pool.c +++ b/prism/util/pm_constant_pool.c @@ -33,8 +33,13 @@ pm_constant_id_list_init_capacity(pm_constant_id_list_t *list, size_t capacity) bool pm_constant_id_list_append(pm_constant_id_list_t *list, pm_constant_id_t id) { if (list->size >= list->capacity) { + const size_t original_capacity = list->capacity; list->capacity = list->capacity == 0 ? 8 : list->capacity * 2; - list->ids = (pm_constant_id_t *) xrealloc(list->ids, sizeof(pm_constant_id_t) * list->capacity); + list->ids = (pm_constant_id_t *) xrealloc_sized( + list->ids, + sizeof(pm_constant_id_t) * list->capacity, + sizeof(pm_constant_id_t) * original_capacity + ); if (list->ids == NULL) return false; } @@ -71,7 +76,7 @@ pm_constant_id_list_includes(pm_constant_id_list_t *list, pm_constant_id_t id) { void pm_constant_id_list_free(pm_constant_id_list_t *list) { if (list->ids != NULL) { - xfree(list->ids); + xfree_sized(list->ids, list->capacity * sizeof(pm_constant_id_t)); } } @@ -165,7 +170,7 @@ pm_constant_pool_resize(pm_constant_pool_t *pool) { // pool->constants and pool->buckets are allocated out of the same chunk // of memory, with the buckets coming first. - xfree(pool->buckets); + xfree_sized(pool->buckets, pool->capacity * element_size); pool->constants = next_constants; pool->buckets = next_buckets; pool->capacity = next_capacity; @@ -257,12 +262,12 @@ pm_constant_pool_insert(pm_constant_pool_t *pool, const uint8_t *start, size_t l // an existing constant, then either way we don't want the given // memory. Either it's duplicated with the existing constant or // it's not necessary because we have a shared version. - xfree((void *) start); + xfree_sized((void *) start, length); } else if (bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) { // If we're attempting to insert a shared constant and the // existing constant is owned, then we can free the owned // constant and replace it with the shared constant. - xfree((void *) constant->start); + xfree_sized((void *) constant->start, constant->length); constant->start = start; bucket->type = (unsigned int) (type & 0x3); } @@ -334,9 +339,9 @@ pm_constant_pool_free(pm_constant_pool_t *pool) { // If an id is set on this constant, then we know we have content here. if (bucket->id != PM_CONSTANT_ID_UNSET && bucket->type == PM_CONSTANT_POOL_BUCKET_OWNED) { pm_constant_t *constant = &pool->constants[bucket->id - 1]; - xfree((void *) constant->start); + xfree_sized((void *) constant->start, constant->length); } } - xfree(pool->buckets); + xfree_sized(pool->buckets, pool->capacity * (sizeof(pm_constant_pool_bucket_t) + sizeof(pm_constant_t))); } diff --git a/prism/util/pm_integer.c b/prism/util/pm_integer.c index 4170ecc58db81b..2b77a4b5d2f9d4 100644 --- a/prism/util/pm_integer.c +++ b/prism/util/pm_integer.c @@ -374,7 +374,7 @@ pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, u } } - xfree(bigints); + xfree_sized(bigints, bigints_length * sizeof(pm_integer_t)); bigints = next_bigints; bigints_length = next_length; } @@ -383,7 +383,7 @@ pm_integer_convert_base(pm_integer_t *destination, const pm_integer_t *source, u destination->negative = source->negative; pm_integer_normalize(destination); - xfree(bigints); + xfree_sized(bigints, bigints_length * sizeof(pm_integer_t)); pm_integer_free(&base); } @@ -422,7 +422,7 @@ pm_integer_parse_powof2(pm_integer_t *integer, uint32_t base, const uint8_t *dig static void pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t digits_length) { const size_t batch = 9; - size_t length = (digits_length + batch - 1) / batch; + const size_t length = (digits_length + batch - 1) / batch; uint32_t *values = (uint32_t *) xcalloc(length, sizeof(uint32_t)); uint32_t value = 0; @@ -439,7 +439,7 @@ pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t di // Convert base from 10**9 to 1<<32. pm_integer_convert_base(integer, &((pm_integer_t) { .length = length, .values = values, .value = 0, .negative = false }), 1000000000, ((uint64_t) 1 << 32)); - xfree(values); + xfree_sized(values, length * sizeof(uint32_t)); } /** @@ -448,7 +448,8 @@ pm_integer_parse_decimal(pm_integer_t *integer, const uint8_t *digits, size_t di static void pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t *start, const uint8_t *end) { // Allocate an array to store digits. - uint8_t *digits = xmalloc(sizeof(uint8_t) * (size_t) (end - start)); + const size_t digits_capa = sizeof(uint8_t) * (size_t) (end - start); + uint8_t *digits = xmalloc(digits_capa); size_t digits_length = 0; for (; start < end; start++) { @@ -463,7 +464,7 @@ pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t * pm_integer_parse_powof2(integer, multiplier, digits, digits_length); } - xfree(digits); + xfree_sized(digits, digits_capa); } /** @@ -635,7 +636,7 @@ pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { } // Allocate a buffer that we'll copy the decimal digits into. - size_t digits_length = converted.length * 9; + const size_t digits_length = converted.length * 9; char *digits = xcalloc(digits_length, sizeof(char)); if (digits == NULL) return; @@ -654,7 +655,7 @@ pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { // Finally, append the string to the buffer and free the digits. pm_buffer_append_string(buffer, digits + start_offset, digits_length - start_offset); - xfree(digits); + xfree_sized(digits, sizeof(char) * digits_length); pm_integer_free(&converted); } diff --git a/prism/util/pm_line_offset_list.c b/prism/util/pm_line_offset_list.c index 710fa4b788da44..d55b2f6874d76c 100644 --- a/prism/util/pm_line_offset_list.c +++ b/prism/util/pm_line_offset_list.c @@ -39,7 +39,7 @@ pm_line_offset_list_append(pm_line_offset_list_t *list, uint32_t cursor) { if (list->offsets == NULL) return false; memcpy(list->offsets, original_offsets, list->size * sizeof(uint32_t)); - xfree(original_offsets); + xfree_sized(original_offsets, list->size * sizeof(uint32_t)); } assert(list->size == 0 || cursor > list->offsets[list->size - 1]); @@ -109,5 +109,5 @@ pm_line_offset_list_line_column(const pm_line_offset_list_t *list, uint32_t curs */ void pm_line_offset_list_free(pm_line_offset_list_t *list) { - xfree(list->offsets); + xfree_sized(list->offsets, list->capacity * sizeof(uint32_t)); } diff --git a/prism/util/pm_list.c b/prism/util/pm_list.c index ad2294cd603f4b..940baffb64da35 100644 --- a/prism/util/pm_list.c +++ b/prism/util/pm_list.c @@ -41,7 +41,7 @@ pm_list_free(pm_list_t *list) { while (node != NULL) { next = node->next; - xfree(node); + xfree_sized(node, sizeof(pm_list_node_t)); node = next; } diff --git a/prism/util/pm_string.c b/prism/util/pm_string.c index a7493c468b166e..5ba8c78ec17d9c 100644 --- a/prism/util/pm_string.c +++ b/prism/util/pm_string.c @@ -71,9 +71,10 @@ pm_string_file_handle_open(pm_string_file_handle_t *handle, const char *filepath int length = MultiByteToWideChar(CP_UTF8, 0, filepath, -1, NULL, 0); if (length == 0) return PM_STRING_INIT_ERROR_GENERIC; - handle->path = xmalloc(sizeof(WCHAR) * ((size_t) length)); + const size_t path_size = sizeof(WCHAR) * ((size_t) length); + handle->path = xmalloc(path_size); if ((handle->path == NULL) || (MultiByteToWideChar(CP_UTF8, 0, filepath, -1, handle->path, length) == 0)) { - xfree(handle->path); + xfree_sized(handle->path, path_size); return PM_STRING_INIT_ERROR_GENERIC; } @@ -88,7 +89,7 @@ pm_string_file_handle_open(pm_string_file_handle_t *handle, const char *filepath } } - xfree(handle->path); + xfree_sized(handle->path, path_size); return result; } @@ -215,7 +216,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { if (result != PM_STRING_INIT_SUCCESS) return result; // Get the file size. - DWORD file_size = GetFileSize(handle.file, NULL); + const DWORD file_size = GetFileSize(handle.file, NULL); if (file_size == INVALID_FILE_SIZE) { pm_string_file_handle_close(&handle); return PM_STRING_INIT_ERROR_GENERIC; @@ -245,7 +246,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { // Check the number of bytes read if (bytes_read != file_size) { - xfree(source); + xfree_sized(source, file_size); pm_string_file_handle_close(&handle); return PM_STRING_INIT_ERROR_GENERIC; } @@ -281,7 +282,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { return PM_STRING_INIT_SUCCESS; } - size_t length = (size_t) size; + const size_t length = (size_t) size; uint8_t *source = xmalloc(length); if (source == NULL) { close(fd); @@ -292,7 +293,7 @@ pm_string_file_init(pm_string_t *string, const char *filepath) { close(fd); if (bytes_read == -1) { - xfree(source); + xfree_sized(source, length); return PM_STRING_INIT_ERROR_GENERIC; } diff --git a/process.c b/process.c index 006611d525e68f..126e36ee8d0d2a 100644 --- a/process.c +++ b/process.c @@ -2726,9 +2726,7 @@ open_func(void *ptr) static void rb_execarg_allocate_dup2_tmpbuf(struct rb_execarg *eargp, long len) { - VALUE tmpbuf = rb_imemo_tmpbuf_new(); - rb_imemo_tmpbuf_set_ptr(tmpbuf, ruby_xmalloc(run_exec_dup2_tmpbuf_size(len))); - eargp->dup2_tmpbuf = tmpbuf; + rb_alloc_tmp_buffer(&eargp->dup2_tmpbuf, run_exec_dup2_tmpbuf_size(len)); } static VALUE @@ -3183,8 +3181,7 @@ run_exec_dup2(VALUE ary, VALUE tmpbuf, struct rb_execarg *sargp, char *errmsg, s long n, i; int ret; int extra_fd = -1; - struct rb_imemo_tmpbuf_struct *buf = (void *)tmpbuf; - struct run_exec_dup2_fd_pair *pairs = (void *)buf->ptr; + struct run_exec_dup2_fd_pair *pairs = RB_IMEMO_TMPBUF_PTR(tmpbuf); n = RARRAY_LEN(ary); diff --git a/ractor.c b/ractor.c index df8d46b9fe578d..a6b017ffe3348f 100644 --- a/ractor.c +++ b/ractor.c @@ -2075,6 +2075,9 @@ copy_enter(VALUE obj, struct obj_traverse_replace_data *data) return traverse_skip; } else { + if (!rb_get_alloc_func(rb_obj_class(obj))) { + rb_raise(rb_eRactorError, "can not copy unshareable object %+"PRIsVALUE, obj); + } data->replacement = rb_obj_clone(obj); return traverse_cont; } diff --git a/spec/bundled_gems_spec.rb b/spec/bundled_gems_spec.rb index 0c9b45a4ad66dd..7b8ab64f252daf 100644 --- a/spec/bundled_gems_spec.rb +++ b/spec/bundled_gems_spec.rb @@ -24,15 +24,15 @@ def self.ruby=(ruby) require_relative "bundler/support/rubygems_ext" Spec::Helpers.install_dev_bundler FileUtils.mkdir_p Spec::Path.gem_path + + %w[sinatra rack tilt rack-protection rack-session rack-test mustermann base64 logger compact_index].each do |gem| + path, = Dir[File.expand_path("../.bundle/gems/#{gem}-*/lib", __dir__)] + $LOAD_PATH.unshift(path) if path + end end config.around(:each) do |example| FileUtils.cp_r Spec::Path.pristine_system_gem_path, Spec::Path.system_gem_path - FileUtils.mkdir_p Spec::Path.base_system_gem_path.join("gems") - %w[sinatra rack tilt rack-protection rack-session rack-test mustermann base64 logger compact_index].each do |gem| - path, = Dir[File.expand_path("../.bundle/gems/#{gem}-*", __dir__)] - FileUtils.cp_r path, Spec::Path.base_system_gem_path.join("gems") - end with_gem_path_as(system_gem_path) do Bundler.ui.silence { example.run } diff --git a/test/ruby/test_object.rb b/test/ruby/test_object.rb index f4dfe2251b884f..2f340788be43b2 100644 --- a/test/ruby/test_object.rb +++ b/test/ruby/test_object.rb @@ -1025,6 +1025,48 @@ def test_singleton_class_freeze assert_predicate(ys, :frozen?, '[Bug #19169]') end + def test_singleton_class_of_singleton_class_freeze + x = Object.new + xs = x.singleton_class + xxs = xs.singleton_class + xxxs = xxs.singleton_class + x.freeze + assert_predicate(xs, :frozen?, '[Bug #20319]') + assert_predicate(xxs, :frozen?, '[Bug #20319]') + assert_predicate(xxxs, :frozen?, '[Bug #20319]') + + m = Module.new + y = Object.new + ys = y.singleton_class + ys.prepend(Module.new) + yys = ys.singleton_class + yys.prepend(Module.new) + yyys = yys.singleton_class + yyys.prepend(Module.new) + y.freeze + assert_predicate(ys, :frozen?, '[Bug #20319]') + assert_predicate(yys, :frozen?, '[Bug #20319]') + assert_predicate(yyys, :frozen?, '[Bug #20319]') + + c = Class.new + cs = c.singleton_class + ccs = cs.singleton_class + cccs = ccs.singleton_class + d = Class.new(c) + ds = d.singleton_class + dds = ds.singleton_class + ddds = dds.singleton_class + d.freeze + assert_predicate(d, :frozen?, '[Bug #20319]') + assert_predicate(ds, :frozen?, '[Bug #20319]') + assert_predicate(dds, :frozen?, '[Bug #20319]') + assert_predicate(ddds, :frozen?, '[Bug #20319]') + assert_not_predicate(c, :frozen?, '[Bug #20319]') + assert_not_predicate(cs, :frozen?, '[Bug #20319]') + assert_not_predicate(ccs, :frozen?, '[Bug #20319]') + assert_not_predicate(cccs, :frozen?, '[Bug #20319]') + end + def test_redef_method_missing bug5473 = '[ruby-core:40287]' ['ArgumentError.new("bug5473")', 'ArgumentError, "bug5473"', '"bug5473"'].each do |code| diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb index eaf861946ef8f1..4aeda424598f13 100644 --- a/test/ruby/test_ractor.rb +++ b/test/ruby/test_ractor.rb @@ -213,6 +213,16 @@ def test_ifunc_proc_not_shareable assert_unshareable(pr, /not supported yet/, exception: RuntimeError) end + def test_copy_unshareable_object_error_message + assert_ractor(<<~'RUBY') + pr = proc {} + err = assert_raise(Ractor::Error) do + Ractor.new(pr) {}.join + end + assert_match(/can not copy unshareable object/, err.message) + RUBY + end + def test_ractor_new_raises_isolation_error_if_outer_variables_are_accessed assert_raise(Ractor::IsolationError) do channel = Ractor::Port.new diff --git a/vm.c b/vm.c index a078f9e7344e4c..4f9c1c7402dbf4 100644 --- a/vm.c +++ b/vm.c @@ -3236,6 +3236,7 @@ rb_vm_update_references(void *ptr) vm->self = rb_gc_location(vm->self); vm->mark_object_ary = rb_gc_location(vm->mark_object_ary); vm->orig_progname = rb_gc_location(vm->orig_progname); + vm->cc_refinement_set = rb_gc_location(vm->cc_refinement_set); if (vm->root_box) rb_box_gc_update_references(vm->root_box); @@ -3324,6 +3325,7 @@ rb_vm_mark(void *ptr) rb_gc_mark_movable(vm->orig_progname); rb_gc_mark_movable(vm->coverages); rb_gc_mark_movable(vm->me2counter); + rb_gc_mark_movable(vm->cc_refinement_set); rb_gc_mark_values(RUBY_NSIG, vm->trap_list.cmd); @@ -3414,10 +3416,6 @@ ruby_vm_destruct(rb_vm_t *vm) st_free_table(vm->ci_table); vm->ci_table = NULL; } - if (vm->cc_refinement_table) { - rb_set_free_table(vm->cc_refinement_table); - vm->cc_refinement_table = NULL; - } RB_ALTSTACK_FREE(vm->main_altstack); struct global_object_list *next; @@ -3510,7 +3508,6 @@ vm_memsize(const void *ptr) vm_memsize_builtin_function_table(vm->builtin_function_table) + rb_id_table_memsize(vm->negative_cme_table) + rb_st_memsize(vm->overloaded_cme_table) + - rb_set_memsize(vm->cc_refinement_table) + vm_memsize_constant_cache() ); @@ -4736,6 +4733,8 @@ rb_vm_register_global_object(VALUE obj) } } +VALUE rb_cc_refinement_set_create(void); + void Init_vm_objects(void) { @@ -4744,7 +4743,7 @@ Init_vm_objects(void) /* initialize mark object array, hash */ vm->mark_object_ary = pin_array_list_new(Qnil); vm->ci_table = st_init_table(&vm_ci_hashtype); - vm->cc_refinement_table = rb_set_init_numtable(); + vm->cc_refinement_set = rb_cc_refinement_set_create(); } // Whether JIT is enabled or not, we need to load/undef `#with_jit` for other builtins. diff --git a/vm_core.h b/vm_core.h index 7fcf8ca5c14439..ebc3fc5d27b677 100644 --- a/vm_core.h +++ b/vm_core.h @@ -816,7 +816,7 @@ typedef struct rb_vm_struct { struct rb_id_table *negative_cme_table; st_table *overloaded_cme_table; // cme -> overloaded_cme set_table *unused_block_warning_table; - set_table *cc_refinement_table; + VALUE cc_refinement_set; // This id table contains a mapping from ID to ICs. It does this with ID // keys and nested st_tables as values. The nested tables have ICs as keys diff --git a/vm_method.c b/vm_method.c index 5289bf03f8e741..de7fdb212b4f20 100644 --- a/vm_method.c +++ b/vm_method.c @@ -587,32 +587,6 @@ rb_invalidate_method_caches(struct rb_id_table *cm_tbl, VALUE cc_tbl) } } -static int -invalidate_cc_refinement(st_data_t key, st_data_t data) -{ - VALUE v = (VALUE)key; - void *ptr = rb_asan_poisoned_object_p(v); - rb_asan_unpoison_object(v, false); - - if (rb_gc_pointer_to_heap_p(v) && - !rb_objspace_garbage_object_p(v) && - RBASIC(v)->flags) { // liveness check - const struct rb_callcache *cc = (const struct rb_callcache *)v; - - VM_ASSERT(vm_cc_refinement_p(cc)); - - if (vm_cc_valid(cc)) { - vm_cc_invalidate(cc); - } - } - - if (ptr) { - rb_asan_poison_object(v); - } - - return ST_CONTINUE; -} - static st_index_t vm_ci_hash(VALUE v) { @@ -722,28 +696,94 @@ rb_vm_ci_free(const struct rb_callinfo *ci) st_delete(vm->ci_table, &key, NULL); } -void -rb_vm_insert_cc_refinement(const struct rb_callcache *cc) +struct cc_refinement_entries { + VALUE *entries; + size_t len; + size_t capa; +}; + +static void +cc_refinement_set_free(void *ptr) { - st_data_t key = (st_data_t)cc; + struct cc_refinement_entries *e = ptr; + xfree(e->entries); +} - rb_vm_t *vm = GET_VM(); - RB_VM_LOCK_ENTER(); - { - rb_set_insert(vm->cc_refinement_table, key); +static size_t +cc_refinement_set_memsize(const void *ptr) +{ + const struct cc_refinement_entries *e = ptr; + return e->capa * sizeof(VALUE); +} + +static void +cc_refinement_set_compact(void *ptr) +{ + struct cc_refinement_entries *e = ptr; + for (size_t i = 0; i < e->len; i++) { + e->entries[i] = rb_gc_location(e->entries[i]); } - RB_VM_LOCK_LEAVE(); } -void -rb_vm_delete_cc_refinement(const struct rb_callcache *cc) +static void +cc_refinement_set_handle_weak_references(void *ptr) { - ASSERT_vm_locking(); + struct cc_refinement_entries *e = ptr; + size_t write = 0; + for (size_t read = 0; read < e->len; read++) { + if (rb_gc_handle_weak_references_alive_p(e->entries[read])) { + e->entries[write++] = e->entries[read]; + } + } + e->len = write; +} + +static const rb_data_type_t cc_refinement_set_type = { + "VM/cc_refinement_set", + { + NULL, + cc_refinement_set_free, + cc_refinement_set_memsize, + cc_refinement_set_compact, + cc_refinement_set_handle_weak_references, + }, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE +}; + +VALUE +rb_cc_refinement_set_create(void) +{ + struct cc_refinement_entries *e; + VALUE obj = TypedData_Make_Struct(0, struct cc_refinement_entries, &cc_refinement_set_type, e); + + e->entries = NULL; + e->len = 0; + e->capa = 0; + + rb_gc_declare_weak_references(obj); + + return obj; +} +void +rb_vm_insert_cc_refinement(const struct rb_callcache *cc) +{ rb_vm_t *vm = GET_VM(); - st_data_t key = (st_data_t)cc; + RB_VM_LOCK_ENTER(); + { + struct cc_refinement_entries *e = RTYPEDDATA_GET_DATA(vm->cc_refinement_set); + if (e->len == e->capa) { + size_t new_capa = e->capa == 0 ? 16 : e->capa * 2; + SIZED_REALLOC_N(e->entries, VALUE, new_capa, e->capa); + e->capa = new_capa; + } + e->entries[e->len++] = (VALUE)cc; - rb_set_table_delete(vm->cc_refinement_table, &key); + // We never mark the cc, but we need to issue a writebarrier so that + // the refinement set can be added to the remembered set + RB_OBJ_WRITTEN(vm->cc_refinement_set, Qundef, (VALUE)cc); + } + RB_VM_LOCK_LEAVE(); } void @@ -753,9 +793,23 @@ rb_clear_all_refinement_method_cache(void) RB_VM_LOCK_ENTER(); { - rb_set_table_foreach(vm->cc_refinement_table, invalidate_cc_refinement, (st_data_t)NULL); - rb_set_table_clear(vm->cc_refinement_table); - rb_set_compact_table(vm->cc_refinement_table); + struct cc_refinement_entries *e = RTYPEDDATA_GET_DATA(vm->cc_refinement_set); + for (size_t i = 0; i < e->len; i++) { + VALUE v = e->entries[i]; + + // All objects should be live as weak references are pruned in + // cc_refinement_set_handle_weak_references + VM_ASSERT(rb_gc_pointer_to_heap_p(v)); + VM_ASSERT(!rb_objspace_garbage_object_p(v)); + + const struct rb_callcache *cc = (const struct rb_callcache *)v; + VM_ASSERT(vm_cc_refinement_p(cc)); + + if (vm_cc_valid(cc)) { + vm_cc_invalidate(cc); + } + } + e->len = 0; } RB_VM_LOCK_LEAVE(); @@ -1446,6 +1500,15 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil check_override_opt_method(klass, (VALUE)mid); } + // If a method was added to a singleton class that shadows a method on + // the original class, invalidate JIT code that assumes no shadowing. + if (RCLASS_SINGLETON_P(orig_klass)) { + VALUE super_klass = RCLASS_SUPER(orig_klass); + if (rb_method_entry(super_klass, mid)) { + rb_zjit_invalidate_singleton_class_has_shadowing_method(super_klass); + } + } + return me; } diff --git a/zjit.h b/zjit.h index 47240846ff1db0..82da3c133d7692 100644 --- a/zjit.h +++ b/zjit.h @@ -27,7 +27,7 @@ void rb_zjit_iseq_update_references(void *payload); void rb_zjit_iseq_free(const rb_iseq_t *iseq); void rb_zjit_before_ractor_spawn(void); void rb_zjit_tracing_invalidate_all(void); -void rb_zjit_invalidate_no_singleton_class(VALUE klass); +void rb_zjit_invalidate_singleton_class_has_shadowing_method(VALUE klass); #else #define rb_zjit_entry 0 static inline void rb_zjit_compile_iseq(const rb_iseq_t *iseq, bool jit_exception) {} @@ -39,7 +39,7 @@ static inline void rb_zjit_invalidate_no_ep_escape(const rb_iseq_t *iseq) {} static inline void rb_zjit_constant_state_changed(ID id) {} static inline void rb_zjit_before_ractor_spawn(void) {} static inline void rb_zjit_tracing_invalidate_all(void) {} -static inline void rb_zjit_invalidate_no_singleton_class(VALUE klass) {} +static inline void rb_zjit_invalidate_singleton_class_has_shadowing_method(VALUE klass) {} #endif // #if USE_ZJIT #define rb_zjit_enabled_p (rb_zjit_entry != 0) diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index 0d7c70d5971dae..fa6ec96578db95 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -10,7 +10,7 @@ use std::slice; use crate::backend::current::ALLOC_REGS; use crate::invariants::{ track_bop_assumption, track_cme_assumption, track_no_ep_escape_assumption, track_no_trace_point_assumption, - track_single_ractor_assumption, track_stable_constant_names_assumption, track_no_singleton_class_assumption + track_single_ractor_assumption, track_stable_constant_names_assumption, track_no_singleton_class_shadowing_assumption }; use crate::gc::append_gc_offsets; use crate::payload::{get_or_create_iseq_payload, IseqCodePtrs, IseqVersion, IseqVersionRef, IseqStatus}; @@ -571,7 +571,7 @@ fn gen_insn(cb: &mut CodeBlock, jit: &mut JITState, asm: &mut Assembler, functio Insn::SetGlobal { id, val, state } => no_output!(gen_setglobal(jit, asm, *id, opnd!(val), &function.frame_state(*state))), Insn::GetGlobal { id, state } => gen_getglobal(jit, asm, *id, &function.frame_state(*state)), &Insn::GetLocal { ep_offset, level, use_sp, .. } => gen_getlocal(asm, ep_offset, level, use_sp), - &Insn::IsBlockParamModified { level } => gen_is_block_param_modified(asm, level), + &Insn::IsBlockParamModified { ep } => gen_is_block_param_modified(asm, opnd!(ep)), &Insn::GetBlockParam { ep_offset, level, state } => gen_getblockparam(jit, asm, ep_offset, level, &function.frame_state(state)), &Insn::SetLocal { val, ep_offset, level } => no_output!(gen_setlocal(asm, opnd!(val), function.type_of(val), ep_offset, level)), Insn::GetConstant { klass, id, allow_nil, state } => gen_getconstant(jit, asm, opnd!(klass), *id, opnd!(allow_nil), &function.frame_state(*state)), @@ -766,8 +766,7 @@ fn gen_setlocal(asm: &mut Assembler, val: Opnd, val_type: Type, local_ep_offset: } /// Returns 1 (as CBool) when VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM is set; returns 0 otherwise. -fn gen_is_block_param_modified(asm: &mut Assembler, level: u32) -> Opnd { - let ep = gen_get_ep(asm, level); +fn gen_is_block_param_modified(asm: &mut Assembler, ep: Opnd) -> Opnd { let flags = asm.load(Opnd::mem(VALUE_BITS, ep, SIZEOF_VALUE_I32 * (VM_ENV_DATA_INDEX_FLAGS as i32))); asm.test(flags, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM.into()); asm.csel_nz(Opnd::Imm(1), Opnd::Imm(0)) @@ -905,8 +904,8 @@ pub fn split_patch_point(asm: &mut Assembler, target: &Target, invariant: Invari Invariant::SingleRactorMode => { track_single_ractor_assumption(code_ptr, side_exit_ptr, version); } - Invariant::NoSingletonClass { klass } => { - track_no_singleton_class_assumption(klass, code_ptr, side_exit_ptr, version); + Invariant::NoSingletonClassWithShadowingMethod { klass } => { + track_no_singleton_class_shadowing_assumption(klass, code_ptr, side_exit_ptr, version); } } }); @@ -2816,6 +2815,7 @@ fn gen_function_stub(cb: &mut CodeBlock, iseq_call: IseqCallRef) -> Result Result Result return None, // not public and include_all not known, can't compile }; // Check singleton class assumption first, before emitting other patchpoints - if !fun.assume_no_singleton_classes(block, recv_class, state) { + if !fun.assume_no_singleton_class_method_shadowing(block, recv_class, state) { return None; } fun.push_insn(block, hir::Insn::PatchPoint { invariant: hir::Invariant::NoTracePoint, state }); diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index cc47c447400cae..e6ebdd41fd36f1 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -7,7 +7,7 @@ #![allow(clippy::match_like_matches_macro)] use crate::{ backend::lir::C_ARG_OPNDS, - cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, invariants::has_singleton_class_of, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState, json::Json + cast::IntoUsize, codegen::local_idx_to_ep_offset, cruby::*, invariants::has_singleton_class_method_shadowing, payload::{get_or_create_iseq_payload, IseqPayload}, options::{debug, get_option, DumpHIR}, state::ZJITState, json::Json }; use std::{ cell::RefCell, collections::{BTreeSet, HashMap, HashSet, VecDeque}, ffi::{c_void, c_uint, c_int, CStr}, fmt::Display, mem::{align_of, size_of}, ptr, slice::Iter @@ -118,7 +118,7 @@ impl std::fmt::Display for BranchEdge { } /// Invalidation reasons -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Invariant { /// Basic operation is redefined BOPRedefined { @@ -146,9 +146,9 @@ pub enum Invariant { NoEPEscape(IseqPtr), /// There is one ractor running. If a non-root ractor gets spawned, this is invalidated. SingleRactorMode, - /// Objects of this class have no singleton class. - /// When a singleton class is created for an object of this class, this is invalidated. - NoSingletonClass { + /// No singleton class of an instance of this class has a method that shadows a method + /// on this class. When such a shadowing method is defined, this is invalidated. + NoSingletonClassWithShadowingMethod { klass: VALUE, }, } @@ -284,9 +284,9 @@ impl<'a> std::fmt::Display for InvariantPrinter<'a> { Invariant::NoTracePoint => write!(f, "NoTracePoint"), Invariant::NoEPEscape(iseq) => write!(f, "NoEPEscape({})", &iseq_name(iseq)), Invariant::SingleRactorMode => write!(f, "SingleRactorMode"), - Invariant::NoSingletonClass { klass } => { + Invariant::NoSingletonClassWithShadowingMethod { klass } => { let class_name = get_class_name(klass); - write!(f, "NoSingletonClass({}@{:p})", + write!(f, "NoSingletonClassWithShadowingMethod({}@{:p})", class_name, self.ptr_map.map_ptr(klass.as_ptr::())) } @@ -431,6 +431,16 @@ impl PtrPrintMap { } } +struct Offset(i32); + +impl std::fmt::LowerHex for Offset { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let prefix = if f.alternate() { "0x" } else { "" }; + let bare_hex = format!("{:x}", self.0.abs()); + f.pad_integral(self.0 >= 0, prefix, &bare_hex) + } +} + impl PtrPrintMap { /// Map a pointer for printing pub fn map_ptr(&self, ptr: *const T) -> *const T { @@ -467,8 +477,8 @@ impl PtrPrintMap { self.map_ptr(id as *const c_void) } - fn map_offset(&self, id: i32) -> *const c_void { - self.map_ptr(id as *const c_void) + fn map_offset(&self, id: i32) -> Offset { + Offset(self.map_ptr(id as *const c_void) as i32) } /// Map shape ID into a pointer for printing @@ -650,9 +660,9 @@ pub enum SendFallbackReason { ComplexArgPass, /// Caller has keyword arguments but callee doesn't expect them; need to convert to hash. UnexpectedKeywordArgs, - /// A singleton class has been seen for the receiver class, so we skip the optimization - /// to avoid an invalidation loop. - SingletonClassSeen, + /// A singleton class with a shadowing method has been seen for the receiver class, + /// so we skip the optimization to avoid an invalidation loop. + SingletonClassWithShadowingMethodSeen, /// The super call is passed a block that the optimizer does not support. SuperCallWithBlock, /// When the `super` is in a block, finding the running CME for guarding requires a loop. Not @@ -709,7 +719,7 @@ impl Display for SendFallbackReason { ArgcParamMismatch => write!(f, "Argument count does not match parameter count"), ComplexArgPass => write!(f, "Complex argument passing"), UnexpectedKeywordArgs => write!(f, "Unexpected Keyword Args"), - SingletonClassSeen => write!(f, "Singleton class previously created for receiver class"), + SingletonClassWithShadowingMethodSeen => write!(f, "Singleton class with shadowing method previously seen for receiver class"), SuperFromBlock => write!(f, "super: call from within a block"), SuperCallWithBlock => write!(f, "super: call made with a block"), SuperClassNotFound => write!(f, "super: profiled class cannot be found"), @@ -858,7 +868,7 @@ pub enum Insn { GetLocal { level: u32, ep_offset: u32, use_sp: bool, rest_param: bool }, /// Check whether VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM is set in the environment flags. /// Returns CBool (0/1). - IsBlockParamModified { level: u32 }, + IsBlockParamModified { ep: InsnId }, /// Get the block parameter as a Proc. GetBlockParam { level: u32, ep_offset: u32, state: InsnId }, /// Set a local variable in a higher scope or the heap @@ -1149,7 +1159,7 @@ impl Insn { Insn::IsNil { .. } => effects::Empty, Insn::IsMethodCfunc { .. } => effects::Any, Insn::IsBitEqual { .. } => effects::Empty, - Insn::IsBitNotEqual { .. } => effects::Any, + Insn::IsBitNotEqual { .. } => effects::Empty, Insn::BoxBool { .. } => effects::Empty, Insn::BoxFixnum { .. } => effects::Empty, Insn::UnboxFixnum { .. } => effects::Any, @@ -1158,7 +1168,7 @@ impl Insn { Insn::GetConstant { .. } => effects::Any, Insn::GetConstantPath { .. } => effects::Any, Insn::IsBlockGiven { .. } => Effect::read_write(abstract_heaps::Other, abstract_heaps::Empty), - Insn::FixnumBitCheck { .. } => effects::Any, + Insn::FixnumBitCheck { .. } => effects::Empty, Insn::IsA { .. } => effects::Empty, Insn::GetGlobal { .. } => effects::Any, Insn::SetGlobal { .. } => effects::Any, @@ -1230,18 +1240,18 @@ impl Insn { Insn::FixnumRShift { .. } => effects::Empty, Insn::ObjToString { .. } => effects::Any, Insn::AnyToString { .. } => effects::Any, - Insn::GuardType { .. } => effects::Any, - Insn::GuardTypeNot { .. } => effects::Any, - Insn::GuardBitEquals { .. } => effects::Any, - Insn::GuardAnyBitSet { .. } => effects::Any, - Insn::GuardNoBitsSet { .. } => effects::Any, - Insn::GuardGreaterEq { .. } => effects::Any, - Insn::GuardLess { .. } => effects::Any, - Insn::PatchPoint { .. } => effects::Any, + Insn::GuardType { .. } => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Control), + Insn::GuardTypeNot { .. } => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Control), + Insn::GuardBitEquals { .. } => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Control), + Insn::GuardAnyBitSet { .. } => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Control), + Insn::GuardNoBitsSet { .. } => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Control), + Insn::GuardGreaterEq { .. } => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Control), + Insn::GuardLess { .. } => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Control), + Insn::PatchPoint { .. } => Effect::read_write(abstract_heaps::PatchPoint, abstract_heaps::Control), Insn::SideExit { .. } => effects::Any, - Insn::IncrCounter(_) => effects::Any, - Insn::IncrCounterPtr { .. } => effects::Any, - Insn::CheckInterrupts { .. } => effects::Any, + Insn::IncrCounter(_) => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Other), + Insn::IncrCounterPtr { .. } => Effect::read_write(abstract_heaps::Empty, abstract_heaps::Other), + Insn::CheckInterrupts { .. } => Effect::read_write(abstract_heaps::InterruptFlag, abstract_heaps::Control), Insn::InvokeProc { .. } => effects::Any, Insn::RefineType { .. } => effects::Empty, Insn::HasType { .. } => effects::Empty, @@ -1644,8 +1654,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { &Insn::GetEP { level } => write!(f, "GetEP {level}"), Insn::GetLEP => write!(f, "GetLEP"), Insn::LoadSelf => write!(f, "LoadSelf"), - &Insn::LoadField { recv, id, offset, return_type: _ } => write!(f, "LoadField {recv}, :{}@{:p}", id.contents_lossy(), self.ptr_map.map_offset(offset)), - &Insn::StoreField { recv, id, offset, val } => write!(f, "StoreField {recv}, :{}@{:p}, {val}", id.contents_lossy(), self.ptr_map.map_offset(offset)), + &Insn::LoadField { recv, id, offset, return_type: _ } => write!(f, "LoadField {recv}, :{}@{:#x}", id.contents_lossy(), self.ptr_map.map_offset(offset)), + &Insn::StoreField { recv, id, offset, val } => write!(f, "StoreField {recv}, :{}@{:#x}, {val}", id.contents_lossy(), self.ptr_map.map_offset(offset)), &Insn::WriteBarrier { recv, val } => write!(f, "WriteBarrier {recv}, {val}"), Insn::SetIvar { self_val, id, val, .. } => write!(f, "SetIvar {self_val}, :{}, {val}", id.contents_lossy()), Insn::GetGlobal { id, .. } => write!(f, "GetGlobal :{}", id.contents_lossy()), @@ -1658,8 +1668,8 @@ impl<'a> std::fmt::Display for InsnPrinter<'a> { let name = get_local_var_name_for_printer(self.iseq, level, ep_offset).map_or(String::new(), |x| format!("{x}, ")); write!(f, "GetLocal {name}l{level}, EP@{ep_offset}{}", if rest_param { ", *" } else { "" }) }, - &Insn::IsBlockParamModified { level } => { - write!(f, "IsBlockParamModified l{level}") + &Insn::IsBlockParamModified { ep } => { + write!(f, "IsBlockParamModified {ep}") }, &Insn::SetLocal { val, level, ep_offset } => { let name = get_local_var_name_for_printer(self.iseq, level, ep_offset).map_or(String::new(), |x| format!("{x}, ")); @@ -1903,31 +1913,37 @@ fn can_direct_send(function: &mut Function, block: BlockId, iseq: *const rb_iseq return false; } - // Because we exclude e.g. post parameters above, they are also excluded from the sum below. + // Because we exclude e.g. post parameters above, they are also excluded from the checks below. let lead_num = params.lead_num; let opt_num = params.opt_num; let keyword = params.keyword; let kw_req_num = if keyword.is_null() { 0 } else { unsafe { (*keyword).required_num } }; let kw_total_num = if keyword.is_null() { 0 } else { unsafe { (*keyword).num } }; - // Minimum args: all required positional + all required keywords - let min_argc = lead_num + kw_req_num; - // Maximum args: all positional (required + optional) + all keywords (required + optional) - let max_argc = lead_num + opt_num + kw_total_num; + let kwarg = unsafe { rb_vm_ci_kwarg(ci) }; + let caller_kw_count = if kwarg.is_null() { 0 } else { (unsafe { get_cikw_keyword_len(kwarg) }) as usize }; + let caller_positional = match args.len().checked_sub(caller_kw_count) { + Some(count) => count, + None => { + function.set_dynamic_send_reason(send_insn, ArgcParamMismatch); + return false; + } + }; - can_send = c_int::try_from(args.len()) + let positional_ok = c_int::try_from(caller_positional) .as_ref() - .map(|argc| (min_argc..=max_argc).contains(argc)) + .map(|argc| (lead_num..=lead_num + opt_num).contains(argc)) .unwrap_or(false); - if !can_send { + let keyword_ok = c_int::try_from(caller_kw_count) + .as_ref() + .map(|argc| (kw_req_num..=kw_total_num).contains(argc)) + .unwrap_or(false); + if !positional_ok || !keyword_ok { function.set_dynamic_send_reason(send_insn, ArgcParamMismatch); return false } // asm.ccall() doesn't support 6+ args. Compute the final argc after keyword setup: // final_argc = caller's positional args + callee's total keywords (all kw slots are filled). - let kwarg = unsafe { rb_vm_ci_kwarg(ci) }; - let caller_kw_count = if kwarg.is_null() { 0 } else { (unsafe { get_cikw_keyword_len(kwarg) }) as usize }; - let caller_positional = args.len() - caller_kw_count; // Right now, the JIT entrypoint accepts the block as an param // We may remove it, remove the block_arg addition to match // See: https://github.com/ruby/ruby/pull/15911#discussion_r2710544982 @@ -2153,21 +2169,21 @@ impl Function { } } - /// Assume that objects of a given class will have no singleton class. - /// Returns true if safe to assume so and emits a PatchPoint. - /// Returns false if we've already seen a singleton class for this class, - /// to avoid an invalidation loop. - pub fn assume_no_singleton_classes(&mut self, block: BlockId, klass: VALUE, state: InsnId) -> bool { + /// Assume that no singleton class of an instance of a given class will have a method + /// that shadows a method on the class. Returns true if safe to assume so and emits a + /// PatchPoint. Returns false if we've already seen such shadowing, to avoid an + /// invalidation loop. + pub fn assume_no_singleton_class_method_shadowing(&mut self, block: BlockId, klass: VALUE, state: InsnId) -> bool { if !klass.instance_can_have_singleton_class() { // This class can never have a singleton class, so no patchpoint needed. return true; } - if has_singleton_class_of(klass) { - // We've seen a singleton class for this klass. Disable the optimization - // to avoid an invalidation loop. + if has_singleton_class_method_shadowing(klass) { + // We've seen a singleton class with a shadowing method for this klass. + // Disable the optimization to avoid an invalidation loop. return false; } - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass }, state }); + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClassWithShadowingMethod { klass }, state }); true } @@ -2225,7 +2241,6 @@ impl Function { | PutSpecialObject {..} | GetGlobal {..} | GetLocal {..} - | IsBlockParamModified {..} | SideExit {..} | EntryPoint {..} | LoadPC @@ -2278,6 +2293,7 @@ impl Function { &GuardGreaterEq { left, right, reason, state } => GuardGreaterEq { left: find!(left), right: find!(right), reason, state }, &GuardLess { left, right, state } => GuardLess { left: find!(left), right: find!(right), state }, &IsBlockGiven { lep } => IsBlockGiven { lep: find!(lep) }, + &IsBlockParamModified { ep } => IsBlockParamModified { ep: find!(ep) }, &GetBlockParam { level, ep_offset, state } => GetBlockParam { level, ep_offset, state: find!(state) }, &FixnumAdd { left, right, state } => FixnumAdd { left: find!(left), right: find!(right), state }, &FixnumSub { left, right, state } => FixnumSub { left: find!(left), right: find!(right), state }, @@ -2964,7 +2980,7 @@ impl Function { return false; } self.gen_patch_points_for_optimized_ccall(block, class, method_id, cme, state); - if !self.assume_no_singleton_classes(block, class, state) { + if !self.assume_no_singleton_class_method_shadowing(block, class, state) { return false; } true @@ -3183,8 +3199,8 @@ impl Function { } // Check singleton class assumption first, before emitting other patchpoints - if !self.assume_no_singleton_classes(block, klass, state) { - self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + if !self.assume_no_singleton_class_method_shadowing(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassWithShadowingMethodSeen); self.push_insn_id(block, insn_id); continue; } @@ -3227,8 +3243,8 @@ impl Function { self.push_insn_id(block, insn_id); continue; } // Check singleton class assumption first, before emitting other patchpoints - if !self.assume_no_singleton_classes(block, klass, state) { - self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + if !self.assume_no_singleton_class_method_shadowing(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassWithShadowingMethodSeen); self.push_insn_id(block, insn_id); continue; } self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); @@ -3251,8 +3267,8 @@ impl Function { self.push_insn_id(block, insn_id); continue; } // Check singleton class assumption first, before emitting other patchpoints - if !self.assume_no_singleton_classes(block, klass, state) { - self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + if !self.assume_no_singleton_class_method_shadowing(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassWithShadowingMethodSeen); self.push_insn_id(block, insn_id); continue; } @@ -3289,8 +3305,8 @@ impl Function { self.push_insn_id(block, insn_id); continue; } // Check singleton class assumption first, before emitting other patchpoints - if !self.assume_no_singleton_classes(block, klass, state) { - self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + if !self.assume_no_singleton_class_method_shadowing(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassWithShadowingMethodSeen); self.push_insn_id(block, insn_id); continue; } self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); @@ -3324,8 +3340,8 @@ impl Function { self.push_insn_id(block, insn_id); continue; }; // Check singleton class assumption first, before emitting other patchpoints - if !self.assume_no_singleton_classes(block, klass, state) { - self.set_dynamic_send_reason(insn_id, SingletonClassSeen); + if !self.assume_no_singleton_class_method_shadowing(block, klass, state) { + self.set_dynamic_send_reason(insn_id, SingletonClassWithShadowingMethodSeen); self.push_insn_id(block, insn_id); continue; } self.push_insn(block, Insn::PatchPoint { invariant: Invariant::MethodRedefined { klass, method: mid, cme }, state }); @@ -3400,7 +3416,7 @@ impl Function { }; if recv_type.is_string() { - self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClass { klass: recv_type.class() }, state }); + self.push_insn(block, Insn::PatchPoint { invariant: Invariant::NoSingletonClassWithShadowingMethod { klass: recv_type.class() }, state }); let guard = self.push_insn(block, Insn::GuardType { val, guard_type: types::String, state }); // Infer type so AnyToString can fold off this self.insn_types[guard.0] = self.infer_type(guard); @@ -4117,8 +4133,8 @@ impl Function { } // Check singleton class assumption first, before emitting other patchpoints - if !fun.assume_no_singleton_classes(block, recv_class, state) { - fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + if !fun.assume_no_singleton_class_method_shadowing(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassWithShadowingMethodSeen); return Err(()); } @@ -4156,8 +4172,8 @@ impl Function { // func(int argc, VALUE *argv, VALUE recv) // Check singleton class assumption first, before emitting other patchpoints - if !fun.assume_no_singleton_classes(block, recv_class, state) { - fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + if !fun.assume_no_singleton_class_method_shadowing(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassWithShadowingMethodSeen); return Err(()); } @@ -4274,8 +4290,8 @@ impl Function { } // Check singleton class assumption first, before emitting other patchpoints - if !fun.assume_no_singleton_classes(block, recv_class, state) { - fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + if !fun.assume_no_singleton_class_method_shadowing(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassWithShadowingMethodSeen); return Err(()); } @@ -4356,8 +4372,8 @@ impl Function { return Err(()); } else { // Check singleton class assumption first, before emitting other patchpoints - if !fun.assume_no_singleton_classes(block, recv_class, state) { - fun.set_dynamic_send_reason(send_insn_id, SingletonClassSeen); + if !fun.assume_no_singleton_class_method_shadowing(block, recv_class, state) { + fun.set_dynamic_send_reason(send_insn_id, SingletonClassWithShadowingMethodSeen); return Err(()); } @@ -4584,6 +4600,12 @@ impl Function { _ => None, }) } + Insn::FixnumAnd { left, right, .. } => { + self.fold_fixnum_bop(insn_id, left, right, |l, r| match (l, r) { + (Some(l), Some(r)) => Some(l & r), + _ => None, + }) + } Insn::FixnumEq { left, right, .. } => { self.fold_fixnum_pred(insn_id, left, right, |l, r| match (l, r) { (Some(l), Some(r)) => Some(l == r), @@ -4680,7 +4702,6 @@ impl Function { | &Insn::GetLEP | &Insn::LoadSelf | &Insn::GetLocal { .. } - | &Insn::IsBlockParamModified { .. } | &Insn::PutSpecialObject { .. } | &Insn::IncrCounter(_) | &Insn::IncrCounterPtr { .. } => @@ -4688,6 +4709,9 @@ impl Function { | &Insn::IsBlockGiven { lep } => { worklist.push_back(lep); } + &Insn::IsBlockParamModified { ep } => { + worklist.push_back(ep); + } &Insn::PatchPoint { state, .. } | &Insn::CheckInterrupts { state } | &Insn::GetBlockParam { state, .. } @@ -5031,6 +5055,29 @@ impl Function { } } + /// Remove duplicate PatchPoint instructions within each basic block. + /// Two PatchPoints are redundant if they assert the same Invariant and no + /// intervening instruction could invalidate it (i.e., writes to PatchPoint). + fn remove_redundant_patch_points(&mut self) { + for block_id in self.rpo() { + let mut seen = HashSet::new(); + let insns = std::mem::take(&mut self.blocks[block_id.0].insns); + let mut new_insns = Vec::with_capacity(insns.len()); + for insn_id in insns { + let insn = self.find(insn_id); + if let Insn::PatchPoint { invariant, .. } = insn { + if !seen.insert(invariant) { + continue; + } + } else if insn.effects_of().write_bits().overlaps(abstract_heaps::PatchPoint) { + seen.clear(); + } + new_insns.push(insn_id); + } + self.blocks[block_id.0].insns = new_insns; + } + } + /// Return a list that has entry_block and then jit_entry_blocks fn entry_blocks(&self) -> Vec { let mut entry_blocks = self.jit_entry_blocks.clone(); @@ -5254,6 +5301,8 @@ impl Function { Counter::compile_hir_fold_constants_time_ns } else if ident_equal!($name, clean_cfg) { Counter::compile_hir_clean_cfg_time_ns + } else if ident_equal!($name, remove_redundant_patch_points) { + Counter::compile_hir_remove_redundant_patch_points_time_ns } else if ident_equal!($name, eliminate_dead_code) { Counter::compile_hir_eliminate_dead_code_time_ns } else { @@ -5280,6 +5329,7 @@ impl Function { run_pass!(optimize_c_calls); run_pass!(fold_constants); run_pass!(clean_cfg); + run_pass!(remove_redundant_patch_points); run_pass!(eliminate_dead_code); if should_dump { @@ -5512,6 +5562,7 @@ impl Function { | Insn::LoadField { .. } | Insn::GetConstantPath { .. } | Insn::IsBlockGiven { .. } + | Insn::IsBlockParamModified { .. } | Insn::GetGlobal { .. } | Insn::LoadPC | Insn::LoadEC @@ -5532,7 +5583,6 @@ impl Function { | Insn::GetSpecialSymbol { .. } | Insn::GetLocal { .. } | Insn::GetBlockParam { .. } - | Insn::IsBlockParamModified { .. } | Insn::StoreField { .. } => { Ok(()) } @@ -6708,9 +6758,7 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { } YARVINSN_opt_getconstant_path => { let ic = get_arg(pc, 0).as_ptr(); - // TODO: Remove this extra Snapshot and pass `exit_id` to `GetConstantPath` instead. - let snapshot = fun.push_insn(block, Insn::Snapshot { state: exit_state }); - state.stack_push(fun.push_insn(block, Insn::GetConstantPath { ic, state: snapshot })); + state.stack_push(fun.push_insn(block, Insn::GetConstantPath { ic, state: exit_id })); } YARVINSN_branchunless | YARVINSN_branchunless_without_ints => { if opcode == YARVINSN_branchunless { @@ -6965,7 +7013,8 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result { // If the block param is already a Proc (modified), read it from EP. // Otherwise, convert it to a Proc and store it to EP. - let is_modified = fun.push_insn(block, Insn::IsBlockParamModified { level }); + let ep = fun.push_insn(block, Insn::GetEP { level }); + let is_modified = fun.push_insn(block, Insn::IsBlockParamModified { ep }); let locals_count = state.locals.len(); let stack_count = state.stack.len(); @@ -8469,14 +8518,11 @@ mod graphviz_tests { bb3 [label=<
- - -
bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject) 
PatchPoint NoTracePoint 
PatchPoint NoTracePoint 
PatchPoint NoTracePoint 
PatchPoint MethodRedefined(Integer@0x1000, |@0x1008, cme:0x1010) 
v27:Fixnum = GuardType v11, Fixnum 
v28:Fixnum = GuardType v12, Fixnum 
v29:Fixnum = FixnumOr v27, v28 
IncrCounter inline_cfunc_optimized_send_count 
PatchPoint NoTracePoint 
CheckInterrupts 
Return v29 
>]; @@ -8532,7 +8578,6 @@ mod graphviz_tests { v18:Truthy = RefineType v9, Truthy  PatchPoint NoTracePoint  v21:Fixnum[3] = Const Value(3)  - PatchPoint NoTracePoint  CheckInterrupts  Return v21  >]; @@ -8541,7 +8586,6 @@ mod graphviz_tests { bb4(v26:BasicObject, v27:Falsy)  PatchPoint NoTracePoint  v31:Fixnum[4] = Const Value(4)  - PatchPoint NoTracePoint  CheckInterrupts  Return v31  >]; diff --git a/zjit/src/hir/opt_tests.rs b/zjit/src/hir/opt_tests.rs index 5d3f8513278833..1779025fe701c0 100644 --- a/zjit/src/hir/opt_tests.rs +++ b/zjit/src/hir/opt_tests.rs @@ -115,10 +115,7 @@ mod hir_opt_tests { v10:Fixnum[1] = Const Value(1) v12:Fixnum[2] = Const Value(2) PatchPoint MethodRedefined(Integer@0x1000, +@0x1008, cme:0x1010) - v34:Fixnum[3] = Const Value(3) IncrCounter inline_cfunc_optimized_send_count - v17:Fixnum[3] = Const Value(3) - PatchPoint MethodRedefined(Integer@0x1000, +@0x1008, cme:0x1010) v35:Fixnum[6] = Const Value(6) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -147,10 +144,7 @@ mod hir_opt_tests { v10:Fixnum[5] = Const Value(5) v12:Fixnum[3] = Const Value(3) PatchPoint MethodRedefined(Integer@0x1000, -@0x1008, cme:0x1010) - v34:Fixnum[2] = Const Value(2) IncrCounter inline_cfunc_optimized_send_count - v17:Fixnum[1] = Const Value(1) - PatchPoint MethodRedefined(Integer@0x1000, -@0x1008, cme:0x1010) v35:Fixnum[1] = Const Value(1) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -241,7 +235,6 @@ mod hir_opt_tests { v46:Fixnum[0] = Const Value(0) IncrCounter inline_cfunc_optimized_send_count v20:Fixnum[0] = Const Value(0) - PatchPoint MethodRedefined(Integer@0x1000, *@0x1008, cme:0x1010) v39:Fixnum = GuardType v9, Fixnum v47:Fixnum[0] = Const Value(0) IncrCounter inline_cfunc_optimized_send_count @@ -508,6 +501,66 @@ mod hir_opt_tests { "); } + #[test] + fn test_fold_fixnum_and() { + eval(" + def test + 4 & -7 + end + "); + + assert_snapshot!(inspect("test"), @"0"); + assert_snapshot!(hir_string("test"), @" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + v10:Fixnum[4] = Const Value(4) + v12:Fixnum[-7] = Const Value(-7) + PatchPoint MethodRedefined(Integer@0x1000, &@0x1008, cme:0x1010) + v25:Fixnum[0] = Const Value(0) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + + #[test] + fn test_fold_fixnum_and_with_negative_self() { + eval(" + def test + -4 & 7 + end + "); + + assert_snapshot!(inspect("test"), @"4"); + assert_snapshot!(hir_string("test"), @" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + v10:Fixnum[-4] = Const Value(-4) + v12:Fixnum[7] = Const Value(7) + PatchPoint MethodRedefined(Integer@0x1000, &@0x1008, cme:0x1010) + v25:Fixnum[4] = Const Value(4) + IncrCounter inline_cfunc_optimized_send_count + CheckInterrupts + Return v25 + "); + } + #[test] fn test_fold_fixnum_less() { eval(" @@ -570,9 +623,6 @@ mod hir_opt_tests { v60:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - v23:Fixnum[2] = Const Value(2) - v25:Fixnum[2] = Const Value(2) - PatchPoint MethodRedefined(Integer@0x1000, <=@0x1008, cme:0x1010) v62:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -644,9 +694,6 @@ mod hir_opt_tests { v60:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - v23:Fixnum[2] = Const Value(2) - v25:Fixnum[2] = Const Value(2) - PatchPoint MethodRedefined(Integer@0x1000, >=@0x1008, cme:0x1010) v62:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -832,7 +879,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :object@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(CustomEq@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(CustomEq@0x1000) PatchPoint MethodRedefined(CustomEq@0x1000, !=@0x1008, cme:0x1010) v29:HeapObject[class_exact:CustomEq] = GuardType v9, HeapObject[class_exact:CustomEq] v30:BoolExact = CCallWithFrame v29, :BasicObject#!=@0x1038, v9 @@ -985,7 +1032,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v20:BasicObject = SendDirect v19, 0x1038, :foo (0x1048) @@ -1014,7 +1061,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -1043,7 +1090,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, baz@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_cfunc_optimized_send_count @@ -1082,7 +1129,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v11:Fixnum[1] = Const Value(1) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v22:BasicObject = SendDirect v21, 0x1038, :foo (0x1048), v11 @@ -1113,7 +1160,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, fun_new_map@0x1008, cme:0x1010) v23:ArraySubclass[class_exact:C] = GuardType v9, ArraySubclass[class_exact:C] v24:BasicObject = SendDirect v23, 0x1038, :fun_new_map (0x1048) @@ -1147,7 +1194,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, bar@0x1008, cme:0x1010) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v25:BasicObject = CCallWithFrame v24, :Enumerable#bar@0x1038, block=0x1040 @@ -1181,7 +1228,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :a@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) v23:ArrayExact = GuardType v9, ArrayExact v24:BasicObject = CCallWithFrame v23, :Array#length@0x1038 @@ -1239,7 +1286,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v20:BasicObject = SendDirect v19, 0x1038, :foo (0x1048) @@ -1268,7 +1315,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v11:Fixnum[3] = Const Value(3) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, Integer@0x1008, cme:0x1010) v21:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v22:BasicObject = SendDirect v21, 0x1038, :Integer (0x1048), v11 @@ -1299,7 +1346,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v24:BasicObject = SendDirect v23, 0x1038, :foo (0x1048), v11, v13 @@ -1330,11 +1377,11 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v24:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v25:BasicObject = SendDirect v24, 0x1038, :foo (0x1048) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, bar@0x1050, cme:0x1058) v28:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v29:BasicObject = SendDirect v28, 0x1038, :bar (0x1048) @@ -1361,7 +1408,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v20:BasicObject = SendDirect v19, 0x1038, :foo (0x1048) @@ -1389,7 +1436,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v11:Fixnum[3] = Const Value(3) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v22:BasicObject = SendDirect v21, 0x1038, :foo (0x1048), v11 @@ -1418,7 +1465,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[3] = Const Value(3) v13:Fixnum[4] = Const Value(4) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v24:BasicObject = SendDirect v23, 0x1038, :foo (0x1048), v11, v13 @@ -1446,14 +1493,14 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, target@0x1008, cme:0x1010) v45:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v46:BasicObject = SendDirect v45, 0x1038, :target (0x1048) v14:Fixnum[10] = Const Value(10) v16:Fixnum[20] = Const Value(20) v18:Fixnum[30] = Const Value(30) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, target@0x1008, cme:0x1010) v49:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v50:BasicObject = SendDirect v49, 0x1038, :target (0x1048), v14, v16, v18 @@ -1490,7 +1537,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v12:StringExact = StringCopy v11 - PatchPoint NoSingletonClass(Object@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1008) PatchPoint MethodRedefined(Object@0x1008, puts@0x1010, cme:0x1018) v23:HeapObject[class_exact*:Object@VALUE(0x1008)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1008)] v24:BasicObject = CCallVariadic v23, :Kernel#puts@0x1040, v12 @@ -2051,7 +2098,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:Fixnum[0] = Const Value(0) - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v26:ArrayExact = GuardType v9, ArrayExact v27:CInt64[0] = UnboxFixnum v14 @@ -2088,7 +2135,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:Fixnum[0] = Const Value(0) - PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Hash@0x1000) PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) v26:HashExact = GuardType v9, HashExact v27:BasicObject = HashAref v26, v14 @@ -2770,10 +2817,10 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 - v15:Fixnum[5] = Const Value(5) + v10:BasicObject = GetConstantPath 0x1000 + v14:Fixnum[5] = Const Value(5) CheckInterrupts - Return v15 + Return v14 "); } @@ -2851,7 +2898,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -2881,7 +2928,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:NilClass): v13:ArrayExact = NewArray - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, itself@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count PatchPoint NoEPEscape(test) @@ -2916,15 +2963,15 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:NilClass): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) - v30:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(Module@0x1010) + v29:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, name@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count - v35:StringExact|NilClass = CCall v30, :Module#name@0x1048 + v34:StringExact|NilClass = CCall v29, :Module#name@0x1048 PatchPoint NoEPEscape(test) - v22:Fixnum[1] = Const Value(1) + v21:Fixnum[1] = Const Value(1) CheckInterrupts - Return v22 + Return v21 "); } @@ -2948,7 +2995,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count v17:Fixnum[5] = Const Value(5) @@ -2977,9 +3024,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v19:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v18:Class[C@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts - Return v19 + Return v18 "); } @@ -3002,19 +3049,16 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v30:Class[String@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint SingleRactorMode + v26:Class[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint StableConstantNames(0x1010, Class) - v33:Class[Class@0x1018] = Const Value(VALUE(0x1018)) - PatchPoint SingleRactorMode + v29:Class[Class@0x1018] = Const Value(VALUE(0x1018)) PatchPoint StableConstantNames(0x1020, Module) - v36:Class[Module@0x1028] = Const Value(VALUE(0x1028)) - PatchPoint SingleRactorMode + v32:Class[Module@0x1028] = Const Value(VALUE(0x1028)) PatchPoint StableConstantNames(0x1030, BasicObject) - v39:Class[BasicObject@0x1038] = Const Value(VALUE(0x1038)) - v22:ArrayExact = NewArray v30, v33, v36, v39 + v35:Class[BasicObject@0x1038] = Const Value(VALUE(0x1038)) + v18:ArrayExact = NewArray v26, v29, v32, v35 CheckInterrupts - Return v22 + Return v18 "); } @@ -3037,13 +3081,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Enumerable) - v24:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint SingleRactorMode + v22:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint StableConstantNames(0x1010, Kernel) - v27:ModuleExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) - v16:ArrayExact = NewArray v24, v27 + v25:ModuleExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) + v14:ArrayExact = NewArray v22, v25 CheckInterrupts - Return v16 + Return v14 "); } @@ -3068,9 +3111,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, MY_MODULE) - v19:ModuleSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:ModuleSubclass[VALUE(0x1008)] = Const Value(VALUE(0x1008)) CheckInterrupts - Return v19 + Return v18 "); } @@ -3094,7 +3137,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count v17:Fixnum[5] = Const Value(5) @@ -3147,7 +3190,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v21:CPtr = GetLEP @@ -3175,7 +3218,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) v20:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v21:FalseClass = Const Value(false) @@ -3205,7 +3248,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, block_given?@0x1008, cme:0x1010) v24:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_cfunc_optimized_send_count @@ -3267,7 +3310,7 @@ mod hir_opt_tests { bb3(v10:BasicObject, v11:BasicObject, v12:NilClass): v16:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v17:ArrayExact = ArrayDup v16 - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, first@0x1010, cme:0x1018) IncrCounter inline_iseq_optimized_send_count v32:BasicObject = InvokeBuiltin leaf , v17 @@ -3297,14 +3340,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, M) - v21:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(Module@0x1010) + v20:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, class@0x1018, cme:0x1020) IncrCounter inline_iseq_optimized_send_count - v27:Class[Module@0x1010] = Const Value(VALUE(0x1010)) + v26:Class[Module@0x1010] = Const Value(VALUE(0x1010)) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v26 "); } @@ -3334,7 +3377,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :c@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v23:BasicObject = SendDirect v22, 0x1038, :foo (0x1048) @@ -3365,7 +3408,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v24:BasicObject = SendDirect v23, 0x1038, :foo (0x1048), v11, v13 @@ -3400,7 +3443,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:NilClass): v13:Fixnum[1] = Const Value(1) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v32:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v8, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -3485,7 +3528,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v24:BasicObject = SendDirect v23, 0x1038, :foo (0x1048), v11, v13 @@ -3516,7 +3559,7 @@ mod hir_opt_tests { v11:Fixnum[3] = Const Value(3) v13:Fixnum[1] = Const Value(1) v15:Fixnum[2] = Const Value(2) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v25:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v27:BasicObject = SendDirect v25, 0x1038, :foo (0x1048), v13, v15, v11 @@ -3547,7 +3590,7 @@ mod hir_opt_tests { v11:Fixnum[0] = Const Value(0) v13:Fixnum[2] = Const Value(2) v15:Fixnum[1] = Const Value(1) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v25:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v27:BasicObject = SendDirect v25, 0x1038, :foo (0x1048), v11, v15, v13 @@ -3577,7 +3620,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[0] = Const Value(0) v13:Fixnum[2] = Const Value(2) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v24:BasicObject = SendDirect v23, 0x1038, :foo (0x1048), v11, v13 @@ -3608,7 +3651,7 @@ mod hir_opt_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[3] = Const Value(3) v15:Fixnum[4] = Const Value(4) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v38:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v39:BasicObject = SendDirect v38, 0x1038, :foo (0x1048), v11, v13, v15 @@ -3616,7 +3659,7 @@ mod hir_opt_tests { v22:Fixnum[2] = Const Value(2) v24:Fixnum[4] = Const Value(4) v26:Fixnum[3] = Const Value(3) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v42:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v44:BasicObject = SendDirect v42, 0x1038, :foo (0x1048), v20, v22, v26, v24 @@ -3647,7 +3690,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v11:Fixnum[1] = Const Value(1) v13:Fixnum[3] = Const Value(3) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v36:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v37:Fixnum[4] = Const Value(4) @@ -3656,7 +3699,7 @@ mod hir_opt_tests { v20:Fixnum[2] = Const Value(2) v22:Fixnum[40] = Const Value(40) v24:Fixnum[30] = Const Value(30) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v42:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v44:BasicObject = SendDirect v42, 0x1038, :foo (0x1048), v18, v20, v24, v22 @@ -3686,7 +3729,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v11:Fixnum[6] = Const Value(6) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, target@0x1008, cme:0x1010) v49:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v50:BasicObject = SendDirect v49, 0x1038, :target (0x1048), v11 @@ -3694,7 +3737,7 @@ mod hir_opt_tests { v18:Fixnum[20] = Const Value(20) v20:Fixnum[30] = Const Value(30) v22:Fixnum[6] = Const Value(6) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, target@0x1008, cme:0x1010) v53:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v54:BasicObject = SendDirect v53, 0x1038, :target (0x1048), v16, v18, v20, v22 @@ -3731,7 +3774,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v11:Fixnum[2] = Const Value(2) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v21:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v22:BasicObject = SendDirect v21, 0x1038, :foo (0x1048), v11 @@ -3786,7 +3829,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v20:Fixnum[1] = Const Value(1) @@ -3932,9 +3975,9 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 + v10:BasicObject = GetConstantPath 0x1000 CheckInterrupts - Return v11 + Return v10 "); } @@ -3956,9 +3999,9 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 + v10:BasicObject = GetConstantPath 0x1000 CheckInterrupts - Return v11 + Return v10 "); } @@ -3981,9 +4024,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v19:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) CheckInterrupts - Return v19 + Return v18 "); } @@ -4012,9 +4055,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Foo::Bar::C) - v19:Class[Foo::Bar::C@0x1008] = Const Value(VALUE(0x1008)) + v18:Class[Foo::Bar::C@0x1008] = Const Value(VALUE(0x1008)) CheckInterrupts - Return v19 + Return v18 "); } @@ -4038,17 +4081,17 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v44:Class[C@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(C@0x1008, new@0x1009, cme:0x1010) - v47:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) - PatchPoint NoSingletonClass(C@0x1008) + v46:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) - v51:NilClass = Const Value(nil) + v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts CheckInterrupts - Return v47 + Return v46 "); } @@ -4076,17 +4119,17 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, C) - v47:Class[C@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) - v16:Fixnum[1] = Const Value(1) + v46:Class[C@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(C@0x1008, new@0x1009, cme:0x1010) - v50:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) - PatchPoint NoSingletonClass(C@0x1008) + v49:HeapObject[class_exact:C] = ObjectAllocClass C:VALUE(0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, initialize@0x1038, cme:0x1040) - v53:BasicObject = SendDirect v50, 0x1068, :initialize (0x1078), v16 + v52:BasicObject = SendDirect v49, 0x1068, :initialize (0x1078), v15 CheckInterrupts CheckInterrupts - Return v50 + Return v49 "); } @@ -4109,17 +4152,17 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Object) - v44:Class[Object@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[Object@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(Object@0x1008, new@0x1009, cme:0x1010) - v47:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) - PatchPoint NoSingletonClass(Object@0x1008) + v46:ObjectExact = ObjectAllocClass Object:VALUE(0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1008) PatchPoint MethodRedefined(Object@0x1008, initialize@0x1038, cme:0x1040) - v51:NilClass = Const Value(nil) + v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts CheckInterrupts - Return v47 + Return v46 "); } @@ -4142,17 +4185,17 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, BasicObject) - v44:Class[BasicObject@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[BasicObject@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(BasicObject@0x1008, new@0x1009, cme:0x1010) - v47:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) - PatchPoint NoSingletonClass(BasicObject@0x1008) + v46:BasicObjectExact = ObjectAllocClass BasicObject:VALUE(0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(BasicObject@0x1008) PatchPoint MethodRedefined(BasicObject@0x1008, initialize@0x1038, cme:0x1040) - v51:NilClass = Const Value(nil) + v50:NilClass = Const Value(nil) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts CheckInterrupts - Return v47 + Return v46 "); } @@ -4175,15 +4218,15 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Hash) - v44:Class[Hash@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[Hash@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(Hash@0x1008, new@0x1009, cme:0x1010) - v47:HashExact = ObjectAllocClass Hash:VALUE(0x1008) + v46:HashExact = ObjectAllocClass Hash:VALUE(0x1008) IncrCounter complex_arg_pass_param_block - v20:BasicObject = Send v47, :initialize # SendFallbackReason: Complex argument passing + v19:BasicObject = Send v46, :initialize # SendFallbackReason: Complex argument passing CheckInterrupts CheckInterrupts - Return v47 + Return v46 "); assert_snapshot!(inspect("test"), @"{}"); } @@ -4207,15 +4250,15 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Array) - v47:Class[Array@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) - v16:Fixnum[1] = Const Value(1) + v46:Class[Array@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + v15:Fixnum[1] = Const Value(1) PatchPoint MethodRedefined(Array@0x1008, new@0x1009, cme:0x1010) - PatchPoint NoSingletonClass(Class@0x1038) + PatchPoint NoSingletonClassWithShadowingMethod(Class@0x1038) PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) - v58:BasicObject = CCallVariadic v47, :Array.new@0x1040, v16 + v57:BasicObject = CCallVariadic v46, :Array.new@0x1040, v15 CheckInterrupts - Return v58 + Return v57 "); } @@ -4238,17 +4281,17 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Set) - v44:Class[Set@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[Set@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(Set@0x1008, new@0x1009, cme:0x1010) - v18:HeapBasicObject = ObjectAlloc v44 - PatchPoint NoSingletonClass(Set@0x1008) + v17:HeapBasicObject = ObjectAlloc v43 + PatchPoint NoSingletonClassWithShadowingMethod(Set@0x1008) PatchPoint MethodRedefined(Set@0x1008, initialize@0x1038, cme:0x1040) - v50:SetExact = GuardType v18, SetExact - v51:BasicObject = CCallVariadic v50, :Set#initialize@0x1068 + v49:SetExact = GuardType v17, SetExact + v50:BasicObject = CCallVariadic v49, :Set#initialize@0x1068 CheckInterrupts CheckInterrupts - Return v18 + Return v17 "); } @@ -4271,14 +4314,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v44:Class[String@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) + v43:Class[String@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) PatchPoint MethodRedefined(String@0x1008, new@0x1009, cme:0x1010) - PatchPoint NoSingletonClass(Class@0x1038) + PatchPoint NoSingletonClassWithShadowingMethod(Class@0x1038) PatchPoint MethodRedefined(Class@0x1038, new@0x1009, cme:0x1010) - v55:BasicObject = CCallVariadic v44, :String.new@0x1040 + v54:BasicObject = CCallVariadic v43, :String.new@0x1040 CheckInterrupts - Return v55 + Return v54 "); } @@ -4301,18 +4344,18 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Regexp) - v48:Class[Regexp@0x1008] = Const Value(VALUE(0x1008)) - v13:NilClass = Const Value(nil) - v16:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) - v17:StringExact = StringCopy v16 + v47:Class[Regexp@0x1008] = Const Value(VALUE(0x1008)) + v12:NilClass = Const Value(nil) + v15:StringExact[VALUE(0x1010)] = Const Value(VALUE(0x1010)) + v16:StringExact = StringCopy v15 PatchPoint MethodRedefined(Regexp@0x1008, new@0x1018, cme:0x1020) - v51:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) - PatchPoint NoSingletonClass(Regexp@0x1008) + v50:RegexpExact = ObjectAllocClass Regexp:VALUE(0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Regexp@0x1008) PatchPoint MethodRedefined(Regexp@0x1008, initialize@0x1048, cme:0x1050) - v55:BasicObject = CCallVariadic v51, :Regexp#initialize@0x1078, v17 + v54:BasicObject = CCallVariadic v50, :Regexp#initialize@0x1078, v16 CheckInterrupts CheckInterrupts - Return v51 + Return v50 "); } @@ -4337,7 +4380,7 @@ mod hir_opt_tests { Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): v18:ArrayExact = NewArray v11, v12 - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) v30:CInt64 = ArrayLength v18 v31:Fixnum = BoxFixnum v30 @@ -4368,7 +4411,7 @@ mod hir_opt_tests { Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): v18:ArrayExact = NewArray v11, v12 - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) v30:CInt64 = ArrayLength v18 v31:Fixnum = BoxFixnum v30 @@ -4426,16 +4469,17 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :block@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - v13:CBool = IsBlockParamModified l0 - IfTrue v13, bb4(v8, v9) - v24:BasicObject = GetBlockParam :block, l0, EP@3 - Jump bb6(v8, v24, v24) - bb4(v14:BasicObject, v15:BasicObject): - v22:BasicObject = GetLocal :block, l0, EP@3 - Jump bb6(v14, v22, v22) - bb6(v26:BasicObject, v27:BasicObject, v28:BasicObject): + v13:CPtr = GetEP 0 + v14:CBool = IsBlockParamModified v13 + IfTrue v14, bb4(v8, v9) + v25:BasicObject = GetBlockParam :block, l0, EP@3 + Jump bb6(v8, v25, v25) + bb4(v15:BasicObject, v16:BasicObject): + v23:BasicObject = GetLocal :block, l0, EP@3 + Jump bb6(v15, v23, v23) + bb6(v27:BasicObject, v28:BasicObject, v29:BasicObject): CheckInterrupts - Return v28 + Return v29 "); } @@ -4459,17 +4503,18 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v10:CBool = IsBlockParamModified l1 - IfTrue v10, bb4(v6) - v20:BasicObject = GetBlockParam :block, l1, EP@3 - Jump bb6(v6, v20) - bb4(v11:BasicObject): - v17:CPtr = GetEP 1 - v18:BasicObject = LoadField v17, :block@0x1000 - Jump bb6(v11, v18) - bb6(v22:BasicObject, v23:BasicObject): + v10:CPtr = GetEP 1 + v11:CBool = IsBlockParamModified v10 + IfTrue v11, bb4(v6) + v21:BasicObject = GetBlockParam :block, l1, EP@3 + Jump bb6(v6, v21) + bb4(v12:BasicObject): + v18:CPtr = GetEP 1 + v19:BasicObject = LoadField v18, :block@0x1000 + Jump bb6(v12, v19) + bb6(v23:BasicObject, v24:BasicObject): CheckInterrupts - Return v23 + Return v24 "); } @@ -4598,7 +4643,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) - PatchPoint NoSingletonClass(Proc@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Proc@0x1000) PatchPoint MethodRedefined(Proc@0x1000, call@0x1008, cme:0x1010) v24:HeapObject[class_exact:Proc] = GuardType v9, HeapObject[class_exact:Proc] v25:BasicObject = InvokeProc v24, v14 @@ -4630,7 +4675,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:Fixnum[2] = Const Value(2) - PatchPoint NoSingletonClass(Proc@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Proc@0x1000) PatchPoint MethodRedefined(Proc@0x1000, []@0x1008, cme:0x1010) v25:HeapObject[class_exact:Proc] = GuardType v9, HeapObject[class_exact:Proc] v26:BasicObject = InvokeProc v25, v14 @@ -4662,7 +4707,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:Fixnum[3] = Const Value(3) - PatchPoint NoSingletonClass(Proc@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Proc@0x1000) PatchPoint MethodRedefined(Proc@0x1000, yield@0x1008, cme:0x1010) v24:HeapObject[class_exact:Proc] = GuardType v9, HeapObject[class_exact:Proc] v25:BasicObject = InvokeProc v24, v14 @@ -4694,7 +4739,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) - PatchPoint NoSingletonClass(Proc@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Proc@0x1000) PatchPoint MethodRedefined(Proc@0x1000, ===@0x1008, cme:0x1010) v24:HeapObject[class_exact:Proc] = GuardType v9, HeapObject[class_exact:Proc] v25:BasicObject = InvokeProc v24, v14 @@ -5166,7 +5211,6 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) v11:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint BOPRedefined(HASH_REDEFINED_OP_FLAG, BOP_FREEZE) CheckInterrupts Return v11 "); @@ -5189,7 +5233,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v10:HashExact = NewHash - PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Hash@0x1000) PatchPoint MethodRedefined(Hash@0x1000, dup@0x1008, cme:0x1010) v23:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 v14:BasicObject = Send v23, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) @@ -5263,7 +5307,6 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) CheckInterrupts Return v11 "); @@ -5286,7 +5329,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, dup@0x1008, cme:0x1010) v23:BasicObject = CCallWithFrame v10, :Kernel#dup@0x1038 v14:BasicObject = Send v23, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) @@ -5360,7 +5403,6 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) v11:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint BOPRedefined(STRING_REDEFINED_OP_FLAG, BOP_FREEZE) CheckInterrupts Return v11 "); @@ -5384,7 +5426,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint NoSingletonClass(String@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1008) PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) v24:BasicObject = CCallWithFrame v11, :String#dup@0x1040 v15:BasicObject = Send v24, :freeze # SendFallbackReason: Uncategorized(opt_send_without_block) @@ -5483,7 +5525,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint NoSingletonClass(String@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1008) PatchPoint MethodRedefined(String@0x1008, dup@0x1010, cme:0x1018) v24:BasicObject = CCallWithFrame v11, :String#dup@0x1040 v15:BasicObject = Send v24, :-@ # SendFallbackReason: Uncategorized(opt_send_without_block) @@ -5566,7 +5608,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint NoSingletonClass(String@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1008) v28:String = GuardType v9, String v21:StringExact = StringConcat v13, v28 CheckInterrupts @@ -5600,7 +5642,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint NoSingletonClass(MyString@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(MyString@0x1008) v28:String = GuardType v9, String v21:StringExact = StringConcat v13, v28 CheckInterrupts @@ -5632,7 +5674,7 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): v13:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v27:ArrayExact = GuardType v9, ArrayExact - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, to_s@0x1010, cme:0x1018) v32:BasicObject = CCallWithFrame v27, :Array#to_s@0x1040 v19:String = AnyToString v9, str: v32 @@ -5724,19 +5766,19 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, S) - v24:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:Fixnum[0] = Const Value(0) - PatchPoint NoSingletonClass(Array@0x1010) + v23:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:Fixnum[0] = Const Value(0) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1010) PatchPoint MethodRedefined(Array@0x1010, []@0x1018, cme:0x1020) - v28:CInt64[0] = UnboxFixnum v13 - v29:CInt64 = ArrayLength v24 - v30:CInt64[0] = GuardLess v28, v29 - v31:CInt64[0] = Const CInt64(0) - v32:CInt64[0] = GuardGreaterEq v30, v31 - v33:BasicObject = ArrayAref v24, v32 + v27:CInt64[0] = UnboxFixnum v12 + v28:CInt64 = ArrayLength v23 + v29:CInt64[0] = GuardLess v27, v28 + v30:CInt64[0] = Const CInt64(0) + v31:CInt64[0] = GuardGreaterEq v29, v30 + v32:BasicObject = ArrayAref v23, v31 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v33 + Return v32 "); // TODO(max): Check the result of `S[0] = 5; test` using `inspect` to make sure that we // actually do the load at run-time. @@ -5761,7 +5803,7 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[1] = Const Value(1) - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v25:CInt64[1] = UnboxFixnum v13 v26:CInt64 = ArrayLength v11 @@ -5794,7 +5836,7 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[-3] = Const Value(-3) - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v25:CInt64[-3] = UnboxFixnum v13 v26:CInt64 = ArrayLength v11 @@ -5827,7 +5869,7 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[-10] = Const Value(-10) - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v25:CInt64[-10] = UnboxFixnum v13 v26:CInt64 = ArrayLength v11 @@ -5860,7 +5902,7 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[10] = Const Value(10) - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v25:CInt64[10] = UnboxFixnum v13 v26:CInt64 = ArrayLength v11 @@ -5896,7 +5938,7 @@ mod hir_opt_tests { PatchPoint BOPRedefined(ARRAY_REDEFINED_OP_FLAG, BOP_FREEZE) v11:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:Fixnum[10] = Const Value(10) - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v24:BasicObject = SendDirect v11, 0x1040, :[] (0x1050), v13 CheckInterrupts @@ -5957,7 +5999,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:ArrayExact = ArrayDup v10 - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, max@0x1010, cme:0x1018) v21:BasicObject = SendDirect v11, 0x1040, :max (0x1050) CheckInterrupts @@ -5988,9 +6030,9 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, MY_SET) - v19:SetExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v18:SetExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) CheckInterrupts - Return v19 + Return v18 "); } @@ -6037,13 +6079,11 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): PatchPoint SingleRactorMode - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, zero@0x1008, cme:0x1010) v23:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count v31:StaticSymbol[:b] = Const Value(VALUE(0x1038)) - PatchPoint SingleRactorMode - PatchPoint NoSingletonClass(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, one@0x1040, cme:0x1048) v28:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -6125,13 +6165,13 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Foo) - v23:Class[Foo@0x1008] = Const Value(VALUE(0x1008)) - v13:Fixnum[100] = Const Value(100) - PatchPoint NoSingletonClass(Class@0x1010) + v22:Class[Foo@0x1008] = Const Value(VALUE(0x1008)) + v12:Fixnum[100] = Const Value(100) + PatchPoint NoSingletonClassWithShadowingMethod(Class@0x1010) PatchPoint MethodRedefined(Class@0x1010, identity@0x1018, cme:0x1020) IncrCounter inline_iseq_optimized_send_count CheckInterrupts - Return v13 + Return v12 "); } @@ -6435,7 +6475,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :val@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, nil?@0x1008, cme:0x1010) v24:StringExact = GuardType v9, StringExact v25:FalseClass = Const Value(false) @@ -6465,7 +6505,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :a@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, !@0x1008, cme:0x1010) v24:ArrayExact = GuardType v9, ArrayExact v25:FalseClass = Const Value(false) @@ -6597,7 +6637,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :a@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, empty?@0x1008, cme:0x1010) v24:ArrayExact = GuardType v9, ArrayExact v25:CInt64 = ArrayLength v24 @@ -6630,7 +6670,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :a@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Hash@0x1000) PatchPoint MethodRedefined(Hash@0x1000, empty?@0x1008, cme:0x1010) v24:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count @@ -6663,7 +6703,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :b@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) v28:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] v29:CBool = IsBitEqual v28, v12 @@ -6758,7 +6798,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -6796,7 +6836,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v25:CShape = LoadField v22, :_shape_id@0x1038 @@ -6838,7 +6878,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v25:CShape = LoadField v22, :_shape_id@0x1038 @@ -7047,14 +7087,14 @@ mod hir_opt_tests { Jump bb4(v8, v9, v32) bb5(v15:BasicObject, v16:BasicObject, v17:BasicObject): v19:HeapObject[class_exact:C] = RefineType v17, HeapObject[class_exact:C] - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) IncrCounter getivar_fallback_not_monomorphic v45:BasicObject = GetIvar v19, :@foo Jump bb4(v15, v16, v45) bb6(v24:BasicObject, v25:BasicObject, v26:BasicObject): v28:HeapObject[class_exact:C] = RefineType v26, HeapObject[class_exact:C] - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) IncrCounter getivar_fallback_not_monomorphic v48:BasicObject = GetIvar v28, :@foo @@ -7094,7 +7134,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] IncrCounter getivar_fallback_too_complex @@ -7123,7 +7163,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v10:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:ArrayExact = ArrayDup v10 - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, map@0x1010, cme:0x1018) v21:BasicObject = SendDirect v11, 0x1040, :map (0x1050) CheckInterrupts @@ -7161,17 +7201,16 @@ mod hir_opt_tests { v13:ArrayExact = NewArray PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, A) - v37:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint SingleRactorMode + v35:ArrayExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint StableConstantNames(0x1010, B) - v40:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) - PatchPoint NoSingletonClass(Array@0x1020) + v38:ArrayExact[VALUE(0x1018)] = Const Value(VALUE(0x1018)) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1020) PatchPoint MethodRedefined(Array@0x1020, zip@0x1028, cme:0x1030) - v44:BasicObject = CCallVariadic v37, :zip@0x1058, v40 - v24:BasicObject = GetLocal :result, l0, EP@3 + v42:BasicObject = CCallVariadic v35, :zip@0x1058, v38 + v22:BasicObject = GetLocal :result, l0, EP@3 PatchPoint NoEPEscape(test) CheckInterrupts - Return v24 + Return v22 "); } @@ -7298,7 +7337,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, foo@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] v20:BasicObject = SendDirect v19, 0x1038, :foo (0x1048) @@ -7332,14 +7371,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, O) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(C@0x1010) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1010) PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) - v26:CShape = LoadField v21, :_shape_id@0x1048 - v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) - v28:NilClass = Const Value(nil) + v25:CShape = LoadField v20, :_shape_id@0x1048 + v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) + v27:NilClass = Const Value(nil) CheckInterrupts - Return v28 + Return v27 "); } @@ -7368,14 +7407,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, O) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(C@0x1010) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1010) PatchPoint MethodRedefined(C@0x1010, foo@0x1018, cme:0x1020) - v26:CShape = LoadField v21, :_shape_id@0x1048 - v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) - v28:NilClass = Const Value(nil) + v25:CShape = LoadField v20, :_shape_id@0x1048 + v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) + v27:NilClass = Const Value(nil) CheckInterrupts - Return v28 + Return v27 "); } @@ -7403,7 +7442,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v25:CShape = LoadField v22, :_shape_id@0x1038 @@ -7438,7 +7477,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v25:CShape = LoadField v22, :_shape_id@0x1038 @@ -7546,7 +7585,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v23:BasicObject = LoadField v22, :foo@0x1038 @@ -7576,7 +7615,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v23:CPtr = LoadField v22, :_as_heap@0x1038 @@ -7610,7 +7649,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) v26:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v18:Fixnum[5] = Const Value(5) @@ -7643,7 +7682,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :v@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) v30:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] v31:CUInt64 = LoadField v30, :_rbasic_flags@0x1038 @@ -7679,7 +7718,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :v@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo=@0x1008, cme:0x1010) v30:HeapObject[class_exact:C] = GuardType v11, HeapObject[class_exact:C] v31:CUInt64 = LoadField v30, :_rbasic_flags@0x1038 @@ -7709,7 +7748,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) v21:ArrayExact = CCallWithFrame v10, :Array#reverse@0x1038 CheckInterrupts @@ -7737,7 +7776,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v10:ArrayExact = NewArray - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, reverse@0x1008, cme:0x1010) v16:Fixnum[5] = Const Value(5) CheckInterrupts @@ -7764,7 +7803,7 @@ mod hir_opt_tests { v10:ArrayExact = NewArray v12:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v13:StringExact = StringCopy v12 - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, join@0x1010, cme:0x1018) v24:StringExact = CCallVariadic v10, :Array#join@0x1040, v13 CheckInterrupts @@ -7790,7 +7829,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint NoSingletonClass(String@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1008) PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -7816,7 +7855,7 @@ mod hir_opt_tests { bb3(v6:BasicObject): v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 - PatchPoint NoSingletonClass(String@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1008) PatchPoint MethodRedefined(String@0x1008, to_s@0x1010, cme:0x1018) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts @@ -7843,7 +7882,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, to_s@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count @@ -7959,7 +7998,7 @@ mod hir_opt_tests { v13:ArrayExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v14:ArrayExact = ArrayDup v13 v19:Fixnum[0] = Const Value(0) - PatchPoint NoSingletonClass(Array@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1008) PatchPoint MethodRedefined(Array@0x1008, []@0x1010, cme:0x1018) v31:CInt64[0] = UnboxFixnum v19 v32:CInt64 = ArrayLength v14 @@ -7996,7 +8035,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :idx@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v28:ArrayExact = GuardType v11, ArrayExact v29:Fixnum = GuardType v12, Fixnum @@ -8036,7 +8075,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :idx@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) v28:ArraySubclass[class_exact:C] = GuardType v11, ArraySubclass[class_exact:C] v29:Fixnum = GuardType v12, Fixnum @@ -8076,7 +8115,7 @@ mod hir_opt_tests { v13:HashExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v14:HashExact = HashDup v13 v19:Fixnum[1] = Const Value(1) - PatchPoint NoSingletonClass(Hash@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(Hash@0x1008) PatchPoint MethodRedefined(Hash@0x1008, []@0x1010, cme:0x1018) v31:BasicObject = HashAref v14, v19 IncrCounter inline_cfunc_optimized_send_count @@ -8108,7 +8147,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :key@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Hash@0x1000) PatchPoint MethodRedefined(Hash@0x1000, []@0x1008, cme:0x1010) v28:HashExact = GuardType v11, HashExact v29:BasicObject = HashAref v28, v12 @@ -8142,7 +8181,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :key@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, []@0x1008, cme:0x1010) v28:HashSubclass[class_exact:C] = GuardType v11, HashSubclass[class_exact:C] v29:BasicObject = CCallWithFrame v28, :Hash#[]@0x1038, v12 @@ -8171,14 +8210,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, H) - v24:HashExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:StaticSymbol[:a] = Const Value(VALUE(0x1010)) - PatchPoint NoSingletonClass(Hash@0x1018) + v23:HashExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:StaticSymbol[:a] = Const Value(VALUE(0x1010)) + PatchPoint NoSingletonClassWithShadowingMethod(Hash@0x1018) PatchPoint MethodRedefined(Hash@0x1018, []@0x1020, cme:0x1028) - v28:BasicObject = HashAref v24, v13 + v27:BasicObject = HashAref v23, v12 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v28 + Return v27 "); } @@ -8207,7 +8246,7 @@ mod hir_opt_tests { PatchPoint NoEPEscape(test) v22:Fixnum[1] = Const Value(1) v24:Fixnum[3] = Const Value(3) - PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Hash@0x1000) PatchPoint MethodRedefined(Hash@0x1000, []=@0x1008, cme:0x1010) HashAset v13, v22, v24 IncrCounter inline_cfunc_optimized_send_count @@ -8241,7 +8280,7 @@ mod hir_opt_tests { v10:BasicObject = LoadArg :val@3 Jump bb3(v7, v8, v9, v10) bb3(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Hash@0x1000) PatchPoint MethodRedefined(Hash@0x1000, []=@0x1008, cme:0x1010) v36:HashExact = GuardType v13, HashExact HashAset v36, v14, v15 @@ -8277,7 +8316,7 @@ mod hir_opt_tests { v10:BasicObject = LoadArg :val@3 Jump bb3(v7, v8, v9, v10) bb3(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, []=@0x1008, cme:0x1010) v36:HashSubclass[class_exact:C] = GuardType v13, HashSubclass[class_exact:C] v37:BasicObject = CCallWithFrame v36, :Hash#[]=@0x1038, v14, v15 @@ -8305,15 +8344,15 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Thread) - v21:Class[Thread@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(Class@0x1010) + v20:Class[Thread@0x1008] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(Class@0x1010) PatchPoint MethodRedefined(Class@0x1010, current@0x1018, cme:0x1020) - v25:CPtr = LoadEC - v26:CPtr = LoadField v25, :thread_ptr@0x1048 - v27:BasicObject = LoadField v26, :self@0x1049 + v24:CPtr = LoadEC + v25:CPtr = LoadField v24, :thread_ptr@0x1048 + v26:BasicObject = LoadField v25, :self@0x1049 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v27 + Return v26 "); } @@ -8340,7 +8379,7 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): v16:Fixnum[1] = Const Value(1) v18:Fixnum[10] = Const Value(10) - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) v32:ArrayExact = GuardType v9, ArrayExact v33:CUInt64 = LoadField v32, :_rbasic_flags@0x1038 @@ -8385,7 +8424,7 @@ mod hir_opt_tests { v10:BasicObject = LoadArg :val@3 Jump bb3(v7, v8, v9, v10) bb3(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, []=@0x1008, cme:0x1010) v36:ArrayExact = GuardType v13, ArrayExact v37:Fixnum = GuardType v14, Fixnum @@ -8433,7 +8472,7 @@ mod hir_opt_tests { v10:BasicObject = LoadArg :val@3 Jump bb3(v7, v8, v9, v10) bb3(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint NoSingletonClass(MyArray@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(MyArray@0x1000) PatchPoint MethodRedefined(MyArray@0x1000, []=@0x1008, cme:0x1010) v36:ArraySubclass[class_exact:MyArray] = GuardType v13, ArraySubclass[class_exact:MyArray] v37:BasicObject = CCallVariadic v36, :Array#[]=@0x1038, v14, v15 @@ -8464,7 +8503,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, <<@0x1008, cme:0x1010) v26:ArrayExact = GuardType v9, ArrayExact ArrayPush v26, v14 @@ -8496,7 +8535,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:Fixnum[1] = Const Value(1) - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) v25:ArrayExact = GuardType v9, ArrayExact ArrayPush v25, v14 @@ -8530,7 +8569,7 @@ mod hir_opt_tests { v14:Fixnum[1] = Const Value(1) v16:Fixnum[2] = Const Value(2) v18:Fixnum[3] = Const Value(3) - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, push@0x1008, cme:0x1010) v29:ArrayExact = GuardType v9, ArrayExact v30:BasicObject = CCallVariadic v29, :Array#push@0x1038, v14, v16, v18 @@ -8601,7 +8640,6 @@ mod hir_opt_tests { v20:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v19, Value(VALUE(0x1040)) v21:RubyValue = LoadField v18, :_ep_specval@0x1048 v22:FalseClass = GuardBitEquals v21, Value(false) - PatchPoint MethodRedefined(Array@0x1000, pop@0x1008, cme:0x1010) v28:CPtr = GetLEP v29:RubyValue = LoadField v28, :_ep_method_entry@0x1038 v30:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v29, Value(VALUE(0x1040)) @@ -8645,7 +8683,6 @@ mod hir_opt_tests { v24:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v23, Value(VALUE(0x1040)) v25:RubyValue = LoadField v22, :_ep_specval@0x1048 v26:FalseClass = GuardBitEquals v25, Value(false) - PatchPoint MethodRedefined(Array@0x1000, []@0x1008, cme:0x1010) v36:CPtr = GetLEP v37:RubyValue = LoadField v36, :_ep_method_entry@0x1038 v38:CallableMethodEntry[VALUE(0x1040)] = GuardBitEquals v37, Value(VALUE(0x1040)) @@ -8719,7 +8756,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :arr@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, length@0x1008, cme:0x1010) v24:ArrayExact = GuardType v9, ArrayExact v25:CInt64 = ArrayLength v24 @@ -8750,7 +8787,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :arr@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(Array@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Array@0x1000) PatchPoint MethodRedefined(Array@0x1000, size@0x1008, cme:0x1010) v24:ArrayExact = GuardType v9, ArrayExact v25:CInt64 = ArrayLength v24 @@ -8782,7 +8819,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:RegexpExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) - PatchPoint NoSingletonClass(String@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1008) PatchPoint MethodRedefined(String@0x1008, =~@0x1010, cme:0x1018) v26:StringExact = GuardType v9, StringExact v27:BasicObject = CCallWithFrame v26, :String#=~@0x1040, v14 @@ -8812,7 +8849,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :i@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:Fixnum = GuardType v12, Fixnum @@ -8852,7 +8889,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :i@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, getbyte@0x1008, cme:0x1010) v31:StringExact = GuardType v11, StringExact v32:Fixnum = GuardType v12, Fixnum @@ -8893,7 +8930,7 @@ mod hir_opt_tests { v10:BasicObject = LoadArg :val@3 Jump bb3(v7, v8, v9, v10) bb3(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) v31:StringExact = GuardType v13, StringExact v32:Fixnum = GuardType v14, Fixnum @@ -8939,7 +8976,7 @@ mod hir_opt_tests { v10:BasicObject = LoadArg :val@3 Jump bb3(v7, v8, v9, v10) bb3(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint NoSingletonClass(MyString@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(MyString@0x1000) PatchPoint MethodRedefined(MyString@0x1000, setbyte@0x1008, cme:0x1010) v31:StringSubclass[class_exact:MyString] = GuardType v13, StringSubclass[class_exact:MyString] v32:Fixnum = GuardType v14, Fixnum @@ -8983,7 +9020,7 @@ mod hir_opt_tests { v10:BasicObject = LoadArg :val@3 Jump bb3(v7, v8, v9, v10) bb3(v12:BasicObject, v13:BasicObject, v14:BasicObject, v15:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, setbyte@0x1008, cme:0x1010) v31:StringExact = GuardType v13, StringExact v32:BasicObject = CCallWithFrame v31, :String#setbyte@0x1038, v14, v15 @@ -9013,7 +9050,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :s@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) v24:StringExact = GuardType v9, StringExact v25:CInt64 = LoadField v24, :len@0x1038 @@ -9048,7 +9085,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :s@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, empty?@0x1008, cme:0x1010) v28:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count @@ -9369,7 +9406,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :y@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) v28:StringExact = GuardType v11, StringExact v29:String = GuardType v12, String @@ -9401,7 +9438,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :y@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) v28:StringExact = GuardType v11, StringExact v29:Fixnum = GuardType v12, Fixnum @@ -9435,7 +9472,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :y@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, <<@0x1008, cme:0x1010) v28:StringExact = GuardType v11, StringExact v29:String = GuardType v12, String @@ -9469,7 +9506,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :y@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(MyString@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(MyString@0x1000) PatchPoint MethodRedefined(MyString@0x1000, <<@0x1008, cme:0x1010) v28:StringSubclass[class_exact:MyString] = GuardType v11, StringSubclass[class_exact:MyString] v29:BasicObject = CCallWithFrame v28, :String#<<@0x1038, v12 @@ -9497,7 +9534,7 @@ mod hir_opt_tests { v10:StringExact[VALUE(0x1000)] = Const Value(VALUE(0x1000)) v11:StringExact = StringCopy v10 v13:StaticSymbol[:a] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(String@0x1010) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1010) PatchPoint MethodRedefined(String@0x1010, <<@0x1018, cme:0x1020) v25:BasicObject = CCallWithFrame v11, :String#<<@0x1048, v13 CheckInterrupts @@ -9551,7 +9588,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :x@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, ascii_only?@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count @@ -9800,7 +9837,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :hash@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Hash@0x1000) PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) v24:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count @@ -9832,7 +9869,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :hash@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(Hash@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Hash@0x1000) PatchPoint MethodRedefined(Hash@0x1000, size@0x1008, cme:0x1010) v28:HashExact = GuardType v9, HashExact IncrCounter inline_cfunc_optimized_send_count @@ -9865,10 +9902,9 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint NoSingletonClass(C@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v29:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count @@ -9899,11 +9935,10 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint NoSingletonClass(C@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] PatchPoint MethodRedefined(C@0x1008, respond_to_missing?@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) v31:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count @@ -9936,10 +9971,9 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint NoSingletonClass(C@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v29:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count @@ -9973,10 +10007,9 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:FalseClass = Const Value(false) - PatchPoint NoSingletonClass(C@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v27:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v31:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count @@ -10010,10 +10043,9 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:NilClass = Const Value(nil) - PatchPoint NoSingletonClass(C@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v27:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v31:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count @@ -10047,10 +10079,9 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:TrueClass = Const Value(true) - PatchPoint NoSingletonClass(C@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v27:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v31:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count @@ -10083,10 +10114,9 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:Fixnum[4] = Const Value(4) - PatchPoint NoSingletonClass(C@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v27:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v31:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count @@ -10119,10 +10149,9 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) v16:NilClass = Const Value(nil) - PatchPoint NoSingletonClass(C@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v27:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] - PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1040, cme:0x1048) v31:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count @@ -10153,11 +10182,10 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint NoSingletonClass(C@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] PatchPoint MethodRedefined(C@0x1008, respond_to_missing?@0x1040, cme:0x1048) - PatchPoint NoSingletonClass(C@0x1008) PatchPoint MethodRedefined(C@0x1008, foo@0x1070, cme:0x1078) v31:FalseClass = Const Value(false) IncrCounter inline_cfunc_optimized_send_count @@ -10191,7 +10219,7 @@ mod hir_opt_tests { Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): v14:StaticSymbol[:foo] = Const Value(VALUE(0x1000)) - PatchPoint NoSingletonClass(C@0x1008) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1008) PatchPoint MethodRedefined(C@0x1008, respond_to?@0x1010, cme:0x1018) v25:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] v26:BasicObject = CCallVariadic v25, :Kernel#respond_to?@0x1040, v14 @@ -10218,7 +10246,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10246,7 +10274,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10274,7 +10302,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10302,7 +10330,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10330,7 +10358,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10358,7 +10386,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10386,7 +10414,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10415,7 +10443,7 @@ mod hir_opt_tests { Jump bb3(v4) bb3(v6:BasicObject): v11:Fixnum[3] = Const Value(3) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v21:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10445,7 +10473,7 @@ mod hir_opt_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) v15:Fixnum[3] = Const Value(3) - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v25:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10575,7 +10603,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10606,7 +10634,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10637,7 +10665,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, callee@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -10696,7 +10724,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :r@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) v28:StringExact = GuardType v11, StringExact v29:String = GuardType v12, String @@ -10730,7 +10758,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :r@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, ==@0x1008, cme:0x1010) v28:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] v29:String = GuardType v12, String @@ -10764,7 +10792,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :r@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, ==@0x1008, cme:0x1010) v28:StringExact = GuardType v11, StringExact v29:String = GuardType v12, String @@ -10796,7 +10824,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :r@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String @@ -10830,7 +10858,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :r@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, ===@0x1008, cme:0x1010) v27:StringSubclass[class_exact:C] = GuardType v11, StringSubclass[class_exact:C] v28:String = GuardType v12, String @@ -10864,7 +10892,7 @@ mod hir_opt_tests { v8:BasicObject = LoadArg :r@2 Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, ===@0x1008, cme:0x1010) v27:StringExact = GuardType v11, StringExact v28:String = GuardType v12, String @@ -10896,7 +10924,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :s@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) v24:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count @@ -10928,7 +10956,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :s@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, size@0x1008, cme:0x1010) v28:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count @@ -10959,7 +10987,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :s@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) v23:StringExact = GuardType v9, StringExact v24:CInt64 = LoadField v23, :len@0x1038 @@ -10992,7 +11020,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :s@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, bytesize@0x1008, cme:0x1010) v27:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count @@ -11023,7 +11051,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :s@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) v24:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count @@ -11054,14 +11082,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v27:Class[String@0x1008] = Const Value(VALUE(0x1008)) + v26:Class[String@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) - PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint NoSingletonClassWithShadowingMethod(Class@0x1010) PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) - v31:BoolExact = IsA v9, v27 + v30:BoolExact = IsA v9, v26 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v31 + Return v30 "); } @@ -11086,14 +11114,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v27:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v26:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) - PatchPoint NoSingletonClass(Module@0x1010) + PatchPoint NoSingletonClassWithShadowingMethod(Module@0x1010) PatchPoint MethodRedefined(Module@0x1010, ===@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count - v32:BoolExact = CCall v27, :Module#===@0x1048, v9 + v31:BoolExact = CCall v26, :Module#===@0x1048, v9 CheckInterrupts - Return v32 + Return v31 "); } @@ -11118,14 +11146,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v25:Class[String@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(String@0x1008) + v24:Class[String@0x1008] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1008) PatchPoint MethodRedefined(String@0x1008, is_a?@0x1009, cme:0x1010) - v29:StringExact = GuardType v9, StringExact - v30:BoolExact = IsA v29, v25 + v28:StringExact = GuardType v9, StringExact + v29:BoolExact = IsA v28, v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v30 + Return v29 "); } @@ -11150,13 +11178,13 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v25:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(String@0x1010) + v24:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1010) PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) - v29:StringExact = GuardType v9, StringExact - v30:BasicObject = CCallWithFrame v29, :Kernel#is_a?@0x1048, v25 + v28:StringExact = GuardType v9, StringExact + v29:BasicObject = CCallWithFrame v28, :Kernel#is_a?@0x1048, v24 CheckInterrupts - Return v30 + Return v29 "); } @@ -11184,14 +11212,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) - v29:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(String@0x1010) + v28:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1010) PatchPoint MethodRedefined(String@0x1010, is_a?@0x1018, cme:0x1020) - v33:StringExact = GuardType v9, StringExact + v32:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count - v21:Fixnum[5] = Const Value(5) + v20:Fixnum[5] = Const Value(5) CheckInterrupts - Return v21 + Return v20 "); } @@ -11219,14 +11247,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) - v31:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) + v30:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) PatchPoint NoEPEscape(test) - PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint NoSingletonClassWithShadowingMethod(Class@0x1010) PatchPoint MethodRedefined(Class@0x1010, ===@0x1018, cme:0x1020) IncrCounter inline_cfunc_optimized_send_count - v23:Fixnum[5] = Const Value(5) + v22:Fixnum[5] = Const Value(5) CheckInterrupts - Return v23 + Return v22 "); } @@ -11251,14 +11279,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, String) - v25:Class[String@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(String@0x1008) + v24:Class[String@0x1008] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1008) PatchPoint MethodRedefined(String@0x1008, kind_of?@0x1009, cme:0x1010) - v29:StringExact = GuardType v9, StringExact - v30:BoolExact = IsA v29, v25 + v28:StringExact = GuardType v9, StringExact + v29:BoolExact = IsA v28, v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v30 + Return v29 "); } @@ -11283,13 +11311,13 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Kernel) - v25:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(String@0x1010) + v24:ModuleExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1010) PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) - v29:StringExact = GuardType v9, StringExact - v30:BasicObject = CCallWithFrame v29, :Kernel#kind_of?@0x1048, v25 + v28:StringExact = GuardType v9, StringExact + v29:BasicObject = CCallWithFrame v28, :Kernel#kind_of?@0x1048, v24 CheckInterrupts - Return v30 + Return v29 "); } @@ -11317,14 +11345,14 @@ mod hir_opt_tests { bb3(v8:BasicObject, v9:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Integer) - v29:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(String@0x1010) + v28:Class[Integer@0x1008] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1010) PatchPoint MethodRedefined(String@0x1010, kind_of?@0x1018, cme:0x1020) - v33:StringExact = GuardType v9, StringExact + v32:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count - v21:Fixnum[5] = Const Value(5) + v20:Fixnum[5] = Const Value(5) CheckInterrupts - Return v21 + Return v20 "); } @@ -11403,7 +11431,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :s@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(String@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) v28:StringExact = GuardType v9, StringExact IncrCounter inline_cfunc_optimized_send_count @@ -11444,7 +11472,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) v43:HeapObject[class_exact:C] = GuardType v6, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count @@ -11452,9 +11480,8 @@ mod hir_opt_tests { IncrCounter inline_cfunc_optimized_send_count v13:StaticSymbol[:_lex_actions] = Const Value(VALUE(0x1038)) v15:TrueClass = Const Value(true) - PatchPoint NoSingletonClass(Class@0x1040) + PatchPoint NoSingletonClassWithShadowingMethod(Class@0x1040) PatchPoint MethodRedefined(Class@0x1040, respond_to?@0x1048, cme:0x1050) - PatchPoint NoSingletonClass(Class@0x1040) PatchPoint MethodRedefined(Class@0x1040, _lex_actions@0x1078, cme:0x1080) v55:TrueClass = Const Value(true) IncrCounter inline_cfunc_optimized_send_count @@ -11485,13 +11512,13 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) v24:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count v28:Class[C@0x1000] = Const Value(VALUE(0x1000)) IncrCounter inline_cfunc_optimized_send_count - PatchPoint NoSingletonClass(Class@0x1038) + PatchPoint NoSingletonClassWithShadowingMethod(Class@0x1038) PatchPoint MethodRedefined(Class@0x1038, name@0x1040, cme:0x1048) IncrCounter inline_cfunc_optimized_send_count v34:StringExact|NilClass = CCall v28, :Module#name@0x1070 @@ -11520,7 +11547,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :o@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, class@0x1008, cme:0x1010) v22:HeapObject[class_exact:C] = GuardType v9, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count @@ -11575,7 +11602,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(Object@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1000) PatchPoint MethodRedefined(Object@0x1000, class@0x1008, cme:0x1010) v19:HeapObject[class_exact*:Object@VALUE(0x1000)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1000)] IncrCounter inline_iseq_optimized_send_count @@ -11635,7 +11662,7 @@ mod hir_opt_tests { v63:CShape[0x1003] = Const CShape(0x1003) StoreField v58, :_shape_id@0x1000, v63 v46:Class[VMFrozenCore] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(Class@0x1010) + PatchPoint NoSingletonClassWithShadowingMethod(Class@0x1010) PatchPoint MethodRedefined(Class@0x1010, lambda@0x1018, cme:0x1020) v68:BasicObject = CCallWithFrame v46, :RubyVM::FrozenCore.lambda@0x1048, block=0x1050 v49:BasicObject = GetLocal :a, l0, EP@6 @@ -11677,12 +11704,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_OBJ) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestFrozen@0x1010) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(TestFrozen@0x1010) PatchPoint MethodRedefined(TestFrozen@0x1010, a@0x1018, cme:0x1020) - v30:Fixnum[1] = Const Value(1) + v29:Fixnum[1] = Const Value(1) CheckInterrupts - Return v30 + Return v29 "); } @@ -11718,12 +11745,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, MULTI_FROZEN) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestMultiIvars@0x1010) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(TestMultiIvars@0x1010) PatchPoint MethodRedefined(TestMultiIvars@0x1010, b@0x1018, cme:0x1020) - v30:Fixnum[20] = Const Value(20) + v29:Fixnum[20] = Const Value(20) CheckInterrupts - Return v30 + Return v29 "); } @@ -11757,12 +11784,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_STR) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestFrozenStr@0x1010) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(TestFrozenStr@0x1010) PatchPoint MethodRedefined(TestFrozenStr@0x1010, name@0x1018, cme:0x1020) - v30:StringExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) + v29:StringExact[VALUE(0x1048)] = Const Value(VALUE(0x1048)) CheckInterrupts - Return v30 + Return v29 "); } @@ -11796,12 +11823,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_NIL) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestFrozenNil@0x1010) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(TestFrozenNil@0x1010) PatchPoint MethodRedefined(TestFrozenNil@0x1010, value@0x1018, cme:0x1020) - v30:NilClass = Const Value(nil) + v29:NilClass = Const Value(nil) CheckInterrupts - Return v30 + Return v29 "); } @@ -11835,14 +11862,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, UNFROZEN_OBJ) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestUnfrozen@0x1010) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(TestUnfrozen@0x1010) PatchPoint MethodRedefined(TestUnfrozen@0x1010, a@0x1018, cme:0x1020) - v26:CShape = LoadField v21, :_shape_id@0x1048 - v27:CShape[0x1049] = GuardBitEquals v26, CShape(0x1049) - v28:BasicObject = LoadField v21, :@a@0x104a + v25:CShape = LoadField v20, :_shape_id@0x1048 + v26:CShape[0x1049] = GuardBitEquals v25, CShape(0x1049) + v27:BasicObject = LoadField v20, :@a@0x104a CheckInterrupts - Return v28 + Return v27 "); } @@ -11876,12 +11903,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_READER) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestAttrReader@0x1010) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(TestAttrReader@0x1010) PatchPoint MethodRedefined(TestAttrReader@0x1010, value@0x1018, cme:0x1020) - v30:Fixnum[42] = Const Value(42) + v29:Fixnum[42] = Const Value(42) CheckInterrupts - Return v30 + Return v29 "); } @@ -11915,12 +11942,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_SYM) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestFrozenSym@0x1010) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(TestFrozenSym@0x1010) PatchPoint MethodRedefined(TestFrozenSym@0x1010, sym@0x1018, cme:0x1020) - v30:StaticSymbol[:hello] = Const Value(VALUE(0x1048)) + v29:StaticSymbol[:hello] = Const Value(VALUE(0x1048)) CheckInterrupts - Return v30 + Return v29 "); } @@ -11954,12 +11981,12 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, FROZEN_TRUE) - v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestFrozenBool@0x1010) + v20:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(TestFrozenBool@0x1010) PatchPoint MethodRedefined(TestFrozenBool@0x1010, flag@0x1018, cme:0x1020) - v30:TrueClass = Const Value(true) + v29:TrueClass = Const Value(true) CheckInterrupts - Return v30 + Return v29 "); } @@ -11992,7 +12019,7 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :obj@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - PatchPoint NoSingletonClass(TestDynamic@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(TestDynamic@0x1000) PatchPoint MethodRedefined(TestDynamic@0x1000, val@0x1008, cme:0x1010) v22:HeapObject[class_exact:TestDynamic] = GuardType v9, HeapObject[class_exact:TestDynamic] v25:CShape = LoadField v22, :_shape_id@0x1038 @@ -12034,21 +12061,19 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, NESTED_FROZEN) - v29:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestNestedAccess@0x1010) + v27:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(TestNestedAccess@0x1010) PatchPoint MethodRedefined(TestNestedAccess@0x1010, x@0x1018, cme:0x1020) - v54:Fixnum[100] = Const Value(100) - PatchPoint SingleRactorMode + v52:Fixnum[100] = Const Value(100) PatchPoint StableConstantNames(0x1048, NESTED_FROZEN) - v35:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(TestNestedAccess@0x1010) + v33:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) PatchPoint MethodRedefined(TestNestedAccess@0x1010, y@0x1050, cme:0x1058) - v56:Fixnum[200] = Const Value(200) + v54:Fixnum[200] = Const Value(200) PatchPoint MethodRedefined(Integer@0x1080, +@0x1088, cme:0x1090) - v57:Fixnum[300] = Const Value(300) + v55:Fixnum[300] = Const Value(300) IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v57 + Return v55 "); } @@ -12072,14 +12097,14 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, S) - v21:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - PatchPoint NoSingletonClass(String@0x1010) + v20:StringExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1010) PatchPoint MethodRedefined(String@0x1010, bytesize@0x1018, cme:0x1020) - v25:CInt64 = LoadField v21, :len@0x1048 - v26:Fixnum = BoxFixnum v25 + v24:CInt64 = LoadField v20, :len@0x1048 + v25:Fixnum = BoxFixnum v24 IncrCounter inline_cfunc_optimized_send_count CheckInterrupts - Return v26 + Return v25 "); } @@ -12103,7 +12128,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) v19:HeapObject[class_exact:C] = GuardType v6, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count @@ -12136,10 +12161,10 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) - v22:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = Send v22, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:BasicObject = Send v21, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts - Return v13 + Return v12 "); } @@ -12162,7 +12187,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(BasicObject@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(BasicObject@0x1000) PatchPoint MethodRedefined(BasicObject@0x1000, initialize@0x1008, cme:0x1010) v21:BasicObjectExact = GuardType v6, BasicObjectExact v22:NilClass = Const Value(nil) @@ -12192,10 +12217,10 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) - v22:BasicObjectExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = Send v22, :initialize # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v21:BasicObjectExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:BasicObject = Send v21, :initialize # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts - Return v13 + Return v12 "); } @@ -12220,10 +12245,10 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) - v22:ObjectExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = Send v22, :toplevel_method # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v21:ObjectExact[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:BasicObject = Send v21, :toplevel_method # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts - Return v13 + Return v12 "); } @@ -12247,7 +12272,7 @@ mod hir_opt_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, secret@0x1008, cme:0x1010) v19:HeapObject[class_exact:C] = GuardType v6, HeapObject[class_exact:C] IncrCounter inline_iseq_optimized_send_count @@ -12280,26 +12305,69 @@ mod hir_opt_tests { bb3(v6:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1000, Obj) - v22:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) - v13:BasicObject = Send v22, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL + v21:HeapObject[VALUE(0x1008)] = Const Value(VALUE(0x1008)) + v12:BasicObject = Send v21, :secret # SendFallbackReason: SendWithoutBlock: method private or protected and no FCALL CheckInterrupts - Return v13 + Return v12 "); } // Test that when a singleton class has been seen for a class, we skip the - // NoSingletonClass optimization to avoid an invalidation loop. + // Creating a singleton class alone should NOT disable optimization; only defining + // a method on the singleton class that shadows a method on the original class should. #[test] - fn test_skip_optimization_after_singleton_class_seen() { - // First, trigger the singleton class callback for String by creating a singleton class. - // This should mark String as having had a singleton class seen. + fn test_singleton_class_creation_alone_does_not_prevent_optimization() { + // Create a singleton class for a String instance, but don't define any methods on it. eval(r#" "hello".singleton_class "#); - // Now define and compile a method that would normally be optimized with NoSingletonClass. - // Since String has had a singleton class, the optimization should be skipped and we - // should fall back to SendWithoutBlock. + // Define and compile a method that calls String#length. + // Since no shadowing method was defined, the optimization should still apply. + eval(r#" + def test(s) + s.length + end + test("asdf") + "#); + + // The output should still have the optimized CCall path with the patchpoint, + // because merely creating a singleton class doesn't shadow any methods. + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :s, l0, SP@4 + Jump bb3(v1, v2) + bb2(): + EntryPoint JIT(0) + v5:BasicObject = LoadArg :self@0 + v6:BasicObject = LoadArg :s@1 + Jump bb3(v5, v6) + bb3(v8:BasicObject, v9:BasicObject): + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) + v24:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v26:Fixnum = CCall v24, :String#length@0x1038 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_skip_optimization_after_singleton_class_shadowing_method() { + // Define a method on a singleton class that shadows String#length. + // This should mark String as having had a shadowing singleton method seen. + eval(r#" + s = "hello" + def s.length; 42; end + "#); + + // Now define and compile a method that would normally be optimized. + // Since a shadowing singleton method was defined, the optimization should be + // skipped and we should fall back to SendWithoutBlock. eval(r#" def test(s) s.length @@ -12307,7 +12375,7 @@ mod hir_opt_tests { test("asdf") "#); - // The output should NOT have NoSingletonClass patchpoint for String, and should + // The output should NOT have the patchpoint for String, and should // fall back to SendWithoutBlock instead of the optimized CCall path. assert_snapshot!(hir_string("test"), @r" fn test@:3: @@ -12322,12 +12390,133 @@ mod hir_opt_tests { v6:BasicObject = LoadArg :s@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - v15:BasicObject = Send v9, :length # SendFallbackReason: Singleton class previously created for receiver class + v15:BasicObject = Send v9, :length # SendFallbackReason: Singleton class with shadowing method previously seen for receiver class CheckInterrupts Return v15 "); } + #[test] + fn test_nested_singleton_class_does_not_prevent_optimization() { + // Access nested singleton classes without defining any shadowing methods. + eval(r#" + s = "hello" + s.singleton_class.singleton_class + "#); + + eval(r#" + def test(s) + s.length + end + test("asdf") + "#); + + // Nested singleton class access without shadowing methods should not + // prevent optimization. + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :s, l0, SP@4 + Jump bb3(v1, v2) + bb2(): + EntryPoint JIT(0) + v5:BasicObject = LoadArg :self@0 + v6:BasicObject = LoadArg :s@1 + Jump bb3(v5, v6) + bb3(v8:BasicObject, v9:BasicObject): + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) + v24:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v26:Fixnum = CCall v24, :String#length@0x1038 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_nested_singleton_class_method_does_not_shadow_instance_method() { + // Define :length on the doubly-nested singleton (metaclass of s's singleton class). + // This is in the metaclass chain, NOT the instance method chain, so it doesn't + // shadow String#length for instance method calls. + eval(r#" + s = "hello" + s.singleton_class.singleton_class.define_method(:length) { 99 } + "#); + + eval(r#" + def test(s) + s.length + end + test("asdf") + "#); + + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :s, l0, SP@4 + Jump bb3(v1, v2) + bb2(): + EntryPoint JIT(0) + v5:BasicObject = LoadArg :self@0 + v6:BasicObject = LoadArg :s@1 + Jump bb3(v5, v6) + bb3(v8:BasicObject, v9:BasicObject): + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) + v24:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v26:Fixnum = CCall v24, :String#length@0x1038 + CheckInterrupts + Return v26 + "); + } + + #[test] + fn test_singleton_class_non_shadowing_method_does_not_prevent_optimization() { + // Define a method on a singleton class that does NOT shadow any method on String. + eval(r#" + s = "hello" + def s.my_custom_method; 42; end + "#); + + // Define and compile a method that calls String#length. + // Since the singleton method doesn't shadow #length, optimization should still apply. + eval(r#" + def test(s) + s.length + end + test("asdf") + "#); + + // The output should still have the optimized CCall path with the patchpoint. + assert_snapshot!(hir_string("test"), @r" + fn test@:3: + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + v2:BasicObject = GetLocal :s, l0, SP@4 + Jump bb3(v1, v2) + bb2(): + EntryPoint JIT(0) + v5:BasicObject = LoadArg :self@0 + v6:BasicObject = LoadArg :s@1 + Jump bb3(v5, v6) + bb3(v8:BasicObject, v9:BasicObject): + PatchPoint NoSingletonClassWithShadowingMethod(String@0x1000) + PatchPoint MethodRedefined(String@0x1000, length@0x1008, cme:0x1010) + v24:StringExact = GuardType v9, StringExact + IncrCounter inline_cfunc_optimized_send_count + v26:Fixnum = CCall v24, :String#length@0x1038 + CheckInterrupts + Return v26 + "); + } + #[test] fn test_invokesuper_to_iseq_optimizes_to_direct() { eval(" @@ -12700,7 +12889,7 @@ mod hir_opt_tests { v8:NilClass = Const Value(nil) Jump bb3(v6, v7, v8) bb3(v10:BasicObject, v11:BasicObject, v12:NilClass): - PatchPoint NoSingletonClass(B@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(B@0x1000) PatchPoint MethodRedefined(B@0x1000, proc@0x1008, cme:0x1010) v36:HeapObject[class_exact:B] = GuardType v10, HeapObject[class_exact:B] v37:BasicObject = CCallWithFrame v36, :Kernel#proc@0x1038, block=0x1040 @@ -12897,13 +13086,13 @@ mod hir_opt_tests { v32:BasicObject = Send v9, :foo # SendFallbackReason: SendWithoutBlock: polymorphic fallback Jump bb4(v8, v9, v32) bb5(v15:BasicObject, v16:BasicObject, v17:BasicObject): - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, foo@0x1008, cme:0x1010) IncrCounter inline_iseq_optimized_send_count v55:Fixnum[3] = Const Value(3) Jump bb4(v15, v16, v55) bb6(v24:BasicObject, v25:BasicObject, v26:BasicObject): - PatchPoint NoSingletonClass(D@0x1038) + PatchPoint NoSingletonClassWithShadowingMethod(D@0x1038) PatchPoint MethodRedefined(D@0x1038, foo@0x1008, cme:0x1040) IncrCounter inline_iseq_optimized_send_count v57:Fixnum[4] = Const Value(4) @@ -12952,7 +13141,7 @@ mod hir_opt_tests { Jump bb4(v8, v9, v32) bb5(v15:BasicObject, v16:BasicObject, v17:BasicObject): v19:HeapObject[class_exact:C] = RefineType v17, HeapObject[class_exact:C] - PatchPoint NoSingletonClass(C@0x1000) + PatchPoint NoSingletonClassWithShadowingMethod(C@0x1000) PatchPoint MethodRedefined(C@0x1000, itself@0x1008, cme:0x1010) IncrCounter inline_cfunc_optimized_send_count Jump bb4(v15, v16, v19) @@ -13102,29 +13291,29 @@ mod hir_opt_tests { v68:Truthy = RefineType v59, Truthy IfTrue v67, bb8(v57, v58, v68, v60, v61) v70:Falsy = RefineType v59, Falsy - PatchPoint NoSingletonClass(Object@0x1018) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1018) PatchPoint MethodRedefined(Object@0x1018, lambda@0x1020, cme:0x1028) - v115:HeapObject[class_exact*:Object@VALUE(0x1018)] = GuardType v57, HeapObject[class_exact*:Object@VALUE(0x1018)] - v116:BasicObject = CCallWithFrame v115, :Kernel#lambda@0x1050, block=0x1058 + v114:HeapObject[class_exact*:Object@VALUE(0x1018)] = GuardType v57, HeapObject[class_exact*:Object@VALUE(0x1018)] + v115:BasicObject = CCallWithFrame v114, :Kernel#lambda@0x1050, block=0x1058 v74:BasicObject = GetLocal :list, l0, EP@6 v76:BasicObject = GetLocal :iter_method, l0, EP@4 v77:BasicObject = GetLocal :kwsplat, l0, EP@3 - SetLocal :sep, l0, EP@5, v116 - Jump bb8(v57, v74, v116, v76, v77) + SetLocal :sep, l0, EP@5, v115 + Jump bb8(v57, v74, v115, v76, v77) bb8(v81:BasicObject, v82:BasicObject, v83:BasicObject, v84:BasicObject, v85:BasicObject): PatchPoint SingleRactorMode PatchPoint StableConstantNames(0x1060, CONST) - v111:HashExact[VALUE(0x1068)] = Const Value(VALUE(0x1068)) - SetLocal :kwsplat, l0, EP@3, v111 - v95:BasicObject = GetLocal :list, l0, EP@6 - v97:BasicObject = GetLocal :iter_method, l0, EP@4 - v99:BasicObject = Send v95, 0x1070, :__send__, v97 # SendFallbackReason: Send: unsupported method type Optimized - v100:BasicObject = GetLocal :list, l0, EP@6 - v101:BasicObject = GetLocal :sep, l0, EP@5 - v102:BasicObject = GetLocal :iter_method, l0, EP@4 - v103:BasicObject = GetLocal :kwsplat, l0, EP@3 - CheckInterrupts - Return v99 + v110:HashExact[VALUE(0x1068)] = Const Value(VALUE(0x1068)) + SetLocal :kwsplat, l0, EP@3, v110 + v94:BasicObject = GetLocal :list, l0, EP@6 + v96:BasicObject = GetLocal :iter_method, l0, EP@4 + v98:BasicObject = Send v94, 0x1070, :__send__, v96 # SendFallbackReason: Send: unsupported method type Optimized + v99:BasicObject = GetLocal :list, l0, EP@6 + v100:BasicObject = GetLocal :sep, l0, EP@5 + v101:BasicObject = GetLocal :iter_method, l0, EP@4 + v102:BasicObject = GetLocal :kwsplat, l0, EP@3 + CheckInterrupts + Return v98 "); } diff --git a/zjit/src/hir/tests.rs b/zjit/src/hir/tests.rs index 7cdb907fa825cf..5e423e37c542b7 100644 --- a/zjit/src/hir/tests.rs +++ b/zjit/src/hir/tests.rs @@ -24,6 +24,41 @@ mod snapshot_tests { format!("{}", FunctionPrinter::with_snapshot(&function)) } + #[test] + fn test_remove_redundant_patch_points() { + eval(" + def test = 1 + 2 + 3 + test + test + "); + assert_snapshot!(optimized_hir_string("test"), @r" + fn test@:2: + bb0(): + Entries bb1, bb2 + bb1(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb3(v1) + bb2(): + EntryPoint JIT(0) + v4:BasicObject = LoadArg :self@0 + Jump bb3(v4) + bb3(v6:BasicObject): + v8:Any = Snapshot FrameState { pc: 0x1000, stack: [], locals: [] } + PatchPoint NoTracePoint + v10:Fixnum[1] = Const Value(1) + v12:Fixnum[2] = Const Value(2) + v13:Any = Snapshot FrameState { pc: 0x1008, stack: [v10, v12], locals: [] } + PatchPoint MethodRedefined(Integer@0x1010, +@0x1018, cme:0x1020) + IncrCounter inline_cfunc_optimized_send_count + v35:Fixnum[6] = Const Value(6) + IncrCounter inline_cfunc_optimized_send_count + v21:Any = Snapshot FrameState { pc: 0x1048, stack: [v35], locals: [] } + CheckInterrupts + Return v35 + "); + } + #[test] fn test_new_array_with_elements() { eval("def test(a, b) = [a, b]"); @@ -84,7 +119,7 @@ mod snapshot_tests { v13:Fixnum[1] = Const Value(1) v15:Fixnum[2] = Const Value(2) v16:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13, v15], locals: [] } - PatchPoint NoSingletonClass(Object@0x1010) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1010) PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) v25:HeapObject[class_exact*:Object@VALUE(0x1010)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1010)] v26:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v13, v15, v11], locals: [] } @@ -122,7 +157,7 @@ mod snapshot_tests { v11:Fixnum[1] = Const Value(1) v13:Fixnum[2] = Const Value(2) v14:Any = Snapshot FrameState { pc: 0x1008, stack: [v6, v11, v13], locals: [] } - PatchPoint NoSingletonClass(Object@0x1010) + PatchPoint NoSingletonClassWithShadowingMethod(Object@0x1010) PatchPoint MethodRedefined(Object@0x1010, foo@0x1018, cme:0x1020) v23:HeapObject[class_exact*:Object@VALUE(0x1010)] = GuardType v6, HeapObject[class_exact*:Object@VALUE(0x1010)] v24:BasicObject = SendDirect v23, 0x1048, :foo (0x1058), v11, v13 @@ -2284,20 +2319,20 @@ pub mod hir_build_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v11:BasicObject = GetConstantPath 0x1000 - v13:NilClass = Const Value(nil) - v16:CBool = IsMethodCFunc v11, :new - IfFalse v16, bb4(v6, v13, v11) - v18:HeapBasicObject = ObjectAlloc v11 - v20:BasicObject = Send v18, :initialize # SendFallbackReason: Uncategorized(opt_send_without_block) + v10:BasicObject = GetConstantPath 0x1000 + v12:NilClass = Const Value(nil) + v15:CBool = IsMethodCFunc v10, :new + IfFalse v15, bb4(v6, v12, v10) + v17:HeapBasicObject = ObjectAlloc v10 + v19:BasicObject = Send v17, :initialize # SendFallbackReason: Uncategorized(opt_send_without_block) CheckInterrupts - Jump bb5(v6, v18, v20) - bb4(v24:BasicObject, v25:NilClass, v26:BasicObject): - v29:BasicObject = Send v26, :new # SendFallbackReason: Uncategorized(opt_send_without_block) - Jump bb5(v24, v29, v25) - bb5(v32:BasicObject, v33:BasicObject, v34:BasicObject): + Jump bb5(v6, v17, v19) + bb4(v23:BasicObject, v24:NilClass, v25:BasicObject): + v28:BasicObject = Send v25, :new # SendFallbackReason: Uncategorized(opt_send_without_block) + Jump bb5(v23, v28, v24) + bb5(v31:BasicObject, v32:BasicObject, v33:BasicObject): CheckInterrupts - Return v33 + Return v32 "); } @@ -3054,18 +3089,19 @@ pub mod hir_build_tests { v6:BasicObject = LoadArg :block@1 Jump bb3(v5, v6) bb3(v8:BasicObject, v9:BasicObject): - v13:CBool = IsBlockParamModified l0 - IfTrue v13, bb4(v8, v9) + v13:CPtr = GetEP 0 + v14:CBool = IsBlockParamModified v13 + IfTrue v14, bb4(v8, v9) Jump bb5(v8, v9) - bb4(v14:BasicObject, v15:BasicObject): - v22:BasicObject = GetLocal :block, l0, EP@3 - Jump bb6(v14, v22, v22) - bb5(v17:BasicObject, v18:BasicObject): - v24:BasicObject = GetBlockParam :block, l0, EP@3 - Jump bb6(v17, v24, v24) - bb6(v26:BasicObject, v27:BasicObject, v28:BasicObject): + bb4(v15:BasicObject, v16:BasicObject): + v23:BasicObject = GetLocal :block, l0, EP@3 + Jump bb6(v15, v23, v23) + bb5(v18:BasicObject, v19:BasicObject): + v25:BasicObject = GetBlockParam :block, l0, EP@3 + Jump bb6(v18, v25, v25) + bb6(v27:BasicObject, v28:BasicObject, v29:BasicObject): CheckInterrupts - Return v28 + Return v29 "); } @@ -3089,19 +3125,20 @@ pub mod hir_build_tests { v4:BasicObject = LoadArg :self@0 Jump bb3(v4) bb3(v6:BasicObject): - v10:CBool = IsBlockParamModified l1 - IfTrue v10, bb4(v6) + v10:CPtr = GetEP 1 + v11:CBool = IsBlockParamModified v10 + IfTrue v11, bb4(v6) Jump bb5(v6) - bb4(v11:BasicObject): - v17:CPtr = GetEP 1 - v18:BasicObject = LoadField v17, :block@0x1000 - Jump bb6(v11, v18) - bb5(v13:BasicObject): - v20:BasicObject = GetBlockParam :block, l1, EP@3 - Jump bb6(v13, v20) - bb6(v22:BasicObject, v23:BasicObject): + bb4(v12:BasicObject): + v18:CPtr = GetEP 1 + v19:BasicObject = LoadField v18, :block@0x1000 + Jump bb6(v12, v19) + bb5(v14:BasicObject): + v21:BasicObject = GetBlockParam :block, l1, EP@3 + Jump bb6(v14, v21) + bb6(v23:BasicObject, v24:BasicObject): CheckInterrupts - Return v23 + Return v24 "); } diff --git a/zjit/src/hir_effect/gen_hir_effect.rb b/zjit/src/hir_effect/gen_hir_effect.rb index 5d13ebafa2e185..1cc552d1e6ea7d 100644 --- a/zjit/src/hir_effect/gen_hir_effect.rb +++ b/zjit/src/hir_effect/gen_hir_effect.rb @@ -48,6 +48,8 @@ def to_graphviz effect, f allocator = any.subeffect 'Allocator' control = any.subeffect 'Control' memory = any.subeffect 'Memory' +patch_point = any.subeffect 'PatchPoint' +interrupt_flag = memory.subeffect 'InterruptFlag' other = memory.subeffect 'Other' frame = memory.subeffect 'Frame' pc = frame.subeffect 'PC' diff --git a/zjit/src/hir_effect/hir_effect.inc.rs b/zjit/src/hir_effect/hir_effect.inc.rs index d9566b3eaa5574..75e3447da1f217 100644 --- a/zjit/src/hir_effect/hir_effect.inc.rs +++ b/zjit/src/hir_effect/hir_effect.inc.rs @@ -1,28 +1,32 @@ // This file is @generated by src/hir/gen_hir_effect.rb. mod bits { pub const Allocator: u8 = 1u8 << 0; - pub const Any: u8 = Allocator | Control | Memory; + pub const Any: u8 = Allocator | Control | Memory | PatchPoint; pub const Control: u8 = 1u8 << 1; pub const Empty: u8 = 0u8; pub const Frame: u8 = Locals | PC | Stack; - pub const Locals: u8 = 1u8 << 2; - pub const Memory: u8 = Frame | Other; - pub const Other: u8 = 1u8 << 3; - pub const PC: u8 = 1u8 << 4; - pub const Stack: u8 = 1u8 << 5; - pub const AllBitPatterns: [(&str, u8); 10] = [ + pub const InterruptFlag: u8 = 1u8 << 2; + pub const Locals: u8 = 1u8 << 3; + pub const Memory: u8 = Frame | InterruptFlag | Other; + pub const Other: u8 = 1u8 << 4; + pub const PC: u8 = 1u8 << 5; + pub const PatchPoint: u8 = 1u8 << 6; + pub const Stack: u8 = 1u8 << 7; + pub const AllBitPatterns: [(&str, u8); 12] = [ ("Any", Any), ("Memory", Memory), ("Frame", Frame), ("Stack", Stack), + ("PatchPoint", PatchPoint), ("PC", PC), ("Other", Other), ("Locals", Locals), + ("InterruptFlag", InterruptFlag), ("Control", Control), ("Allocator", Allocator), ("Empty", Empty), ]; - pub const NumEffectBits: u8 = 6; + pub const NumEffectBits: u8 = 8; } pub mod effect_types { pub type EffectBits = u8; @@ -34,10 +38,12 @@ pub mod abstract_heaps { pub const Control: AbstractHeap = AbstractHeap::from_bits(bits::Control); pub const Empty: AbstractHeap = AbstractHeap::from_bits(bits::Empty); pub const Frame: AbstractHeap = AbstractHeap::from_bits(bits::Frame); + pub const InterruptFlag: AbstractHeap = AbstractHeap::from_bits(bits::InterruptFlag); pub const Locals: AbstractHeap = AbstractHeap::from_bits(bits::Locals); pub const Memory: AbstractHeap = AbstractHeap::from_bits(bits::Memory); pub const Other: AbstractHeap = AbstractHeap::from_bits(bits::Other); pub const PC: AbstractHeap = AbstractHeap::from_bits(bits::PC); + pub const PatchPoint: AbstractHeap = AbstractHeap::from_bits(bits::PatchPoint); pub const Stack: AbstractHeap = AbstractHeap::from_bits(bits::Stack); } pub mod effects { @@ -47,9 +53,11 @@ pub mod effects { pub const Control: Effect = Effect::promote(abstract_heaps::Control); pub const Empty: Effect = Effect::promote(abstract_heaps::Empty); pub const Frame: Effect = Effect::promote(abstract_heaps::Frame); + pub const InterruptFlag: Effect = Effect::promote(abstract_heaps::InterruptFlag); pub const Locals: Effect = Effect::promote(abstract_heaps::Locals); pub const Memory: Effect = Effect::promote(abstract_heaps::Memory); pub const Other: Effect = Effect::promote(abstract_heaps::Other); pub const PC: Effect = Effect::promote(abstract_heaps::PC); + pub const PatchPoint: Effect = Effect::promote(abstract_heaps::PatchPoint); pub const Stack: Effect = Effect::promote(abstract_heaps::Stack); } diff --git a/zjit/src/invariants.rs b/zjit/src/invariants.rs index 7aa13cbfcb4bdd..a5dd893d978648 100644 --- a/zjit/src/invariants.rs +++ b/zjit/src/invariants.rs @@ -93,9 +93,9 @@ pub struct Invariants { /// Set of patch points that assume that the interpreter is running with only one ractor single_ractor_patch_points: HashSet, - /// Map from a class to a set of patch points that assume objects of the class - /// will have no singleton class. - no_singleton_class_patch_points: HashMap>, + /// Map from a class to a set of patch points that assume no singleton class of an + /// instance of the class has a method that shadows a method on the class. + no_singleton_class_shadowing_patch_points: HashMap>, } impl Invariants { @@ -104,7 +104,7 @@ impl Invariants { self.update_ep_escape_iseqs(); self.update_no_ep_escape_iseq_patch_points(); self.update_cme_patch_points(); - self.update_no_singleton_class_patch_points(); + self.update_no_singleton_class_shadowing_patch_points(); } /// Forget an ISEQ when freeing it. We need to because a) if the address is reused, we'd be @@ -125,7 +125,7 @@ impl Invariants { /// Forget a class when freeing it. See [Self::forget_iseq] for reasoning. pub fn forget_klass(&mut self, klass: VALUE) { - self.no_singleton_class_patch_points.remove(&klass); + self.no_singleton_class_shadowing_patch_points.remove(&klass); } /// Update ISEQ references in Invariants::ep_escape_iseqs @@ -160,15 +160,15 @@ impl Invariants { self.cme_patch_points = updated_cme_patch_points; } - fn update_no_singleton_class_patch_points(&mut self) { - let updated_no_singleton_class_patch_points = std::mem::take(&mut self.no_singleton_class_patch_points) + fn update_no_singleton_class_shadowing_patch_points(&mut self) { + let updated_no_singleton_class_shadowing_patch_points = std::mem::take(&mut self.no_singleton_class_shadowing_patch_points) .into_iter() .map(|(klass, patch_points)| { let new_klass = unsafe { rb_gc_location(klass) }; (new_klass, patch_points) }) .collect(); - self.no_singleton_class_patch_points = updated_no_singleton_class_patch_points; + self.no_singleton_class_shadowing_patch_points = updated_no_singleton_class_shadowing_patch_points; } } @@ -301,15 +301,16 @@ pub fn track_stable_constant_names_assumption( } } -/// Track a patch point for objects of a given class will have no singleton class. -pub fn track_no_singleton_class_assumption( +/// Track a patch point assuming no singleton class of an instance of a given class +/// has a method that shadows a method on the class. +pub fn track_no_singleton_class_shadowing_assumption( klass: VALUE, patch_point_ptr: CodePtr, side_exit_ptr: CodePtr, version: IseqVersionRef, ) { let invariants = ZJITState::get_invariants(); - invariants.no_singleton_class_patch_points.entry(klass).or_default().insert(PatchPoint::new( + invariants.no_singleton_class_shadowing_patch_points.entry(klass).or_default().insert(PatchPoint::new( patch_point_ptr, side_exit_ptr, version, @@ -439,39 +440,39 @@ pub extern "C" fn rb_zjit_tracing_invalidate_all() { }); } -/// Returns true if we've seen a singleton class of a given class since boot. -/// This is used to avoid an invalidation loop where we repeatedly compile code -/// that assumes no singleton class, only to have it invalidated. -pub fn has_singleton_class_of(klass: VALUE) -> bool { +/// Returns true if we've seen a singleton class with a shadowing method for a given class +/// since boot. This is used to avoid an invalidation loop where we repeatedly compile code +/// that assumes no shadowing, only to have it invalidated. +pub fn has_singleton_class_method_shadowing(klass: VALUE) -> bool { ZJITState::get_invariants() - .no_singleton_class_patch_points + .no_singleton_class_shadowing_patch_points .get(&klass) .map_or(false, |patch_points| patch_points.is_empty()) } #[unsafe(no_mangle)] -pub extern "C" fn rb_zjit_invalidate_no_singleton_class(klass: VALUE) { +pub extern "C" fn rb_zjit_invalidate_singleton_class_has_shadowing_method(klass: VALUE) { if !zjit_enabled_p() { return; } with_vm_lock(src_loc!(), || { let invariants = ZJITState::get_invariants(); - match invariants.no_singleton_class_patch_points.get_mut(&klass) { + match invariants.no_singleton_class_shadowing_patch_points.get_mut(&klass) { Some(patch_points) => { - // Invalidate existing patch points and let has_singleton_class_of() + // Invalidate existing patch points and let has_singleton_class_method_shadowing() // return true when they are compiled again let patch_points = mem::take(patch_points); if !patch_points.is_empty() { let cb = ZJITState::get_code_block(); - debug!("Singleton class created for {:?}", klass); - compile_patch_points!(cb, patch_points, "Singleton class created for {:?}", klass); + debug!("Singleton class with shadowing method for {:?}", klass); + compile_patch_points!(cb, patch_points, "Singleton class with shadowing method for {:?}", klass); cb.mark_all_executable(); } } None => { - // Let has_singleton_class_of() return true for this class - invariants.no_singleton_class_patch_points.insert(klass, HashSet::new()); + // Let has_singleton_class_method_shadowing() return true for this class + invariants.no_singleton_class_shadowing_patch_points.insert(klass, HashSet::new()); } } }); diff --git a/zjit/src/stats.rs b/zjit/src/stats.rs index 23ff9b16cf77e1..7ffe8930d680bc 100644 --- a/zjit/src/stats.rs +++ b/zjit/src/stats.rs @@ -169,6 +169,7 @@ make_counters! { compile_hir_strength_reduce_time_ns, compile_hir_fold_constants_time_ns, compile_hir_clean_cfg_time_ns, + compile_hir_remove_redundant_patch_points_time_ns, compile_hir_eliminate_dead_code_time_ns, compile_lir_time_ns, } @@ -214,7 +215,7 @@ make_counters! { exit_patchpoint_no_tracepoint, exit_patchpoint_no_ep_escape, exit_patchpoint_single_ractor_mode, - exit_patchpoint_no_singleton_class, + exit_patchpoint_no_singleton_class_shadowing, exit_callee_side_exit, exit_obj_to_string_fallback, exit_interrupt, @@ -258,7 +259,7 @@ make_counters! { // Caller has keyword arguments but callee doesn't expect them. send_fallback_unexpected_keyword_args, // Singleton class previously created for receiver class. - send_fallback_singleton_class_seen, + send_fallback_singleton_class_shadowing_seen, send_fallback_bmethod_non_iseq_proc, send_fallback_obj_to_string_not_string, send_fallback_send_cfunc_variadic, @@ -596,8 +597,8 @@ pub fn side_exit_counter(reason: crate::hir::SideExitReason) -> Counter { => exit_patchpoint_no_ep_escape, PatchPoint(Invariant::SingleRactorMode) => exit_patchpoint_single_ractor_mode, - PatchPoint(Invariant::NoSingletonClass { .. }) - => exit_patchpoint_no_singleton_class, + PatchPoint(Invariant::NoSingletonClassWithShadowingMethod { .. }) + => exit_patchpoint_no_singleton_class_shadowing, } } @@ -635,7 +636,7 @@ pub fn send_fallback_counter(reason: crate::hir::SendFallbackReason) -> Counter SendCfuncArrayVariadic => send_fallback_send_cfunc_array_variadic, ComplexArgPass => send_fallback_one_or_more_complex_arg_pass, UnexpectedKeywordArgs => send_fallback_unexpected_keyword_args, - SingletonClassSeen => send_fallback_singleton_class_seen, + SingletonClassWithShadowingMethodSeen => send_fallback_singleton_class_shadowing_seen, ArgcParamMismatch => send_fallback_argc_param_mismatch, BmethodNonIseqProc => send_fallback_bmethod_non_iseq_proc, SendNotOptimizedMethodType(_) => send_fallback_send_not_optimized_method_type,