diff --git a/NEWS.md b/NEWS.md index 318a7e37ad6475..12a8cb540b8092 100644 --- a/NEWS.md +++ b/NEWS.md @@ -222,7 +222,7 @@ They are still available on rubygems.org and can be installed with * 3.7.5 to [3.7.6][test-unit-3.7.6], [3.7.7][test-unit-3.7.7], [3.7.8][test-unit-3.7.8] * rss 0.3.3 * 0.3.2 to [0.3.3][rss-0.3.3] -* net-imap 0.6.6 +* net-imap 0.6.7 * 0.6.2 to [v0.6.3][net-imap-v0.6.3], [v0.6.4][net-imap-v0.6.4], [v0.6.4.1][net-imap-v0.6.4.1], [v0.6.5][net-imap-v0.6.5], [v0.6.6][net-imap-v0.6.6] * rbs 4.2.0 * 3.10.0 to [v3.10.1][rbs-v3.10.1], [v3.10.2][rbs-v3.10.2], [v3.10.3][rbs-v3.10.3], [v3.10.4][rbs-v3.10.4], [v4.0.0.dev.1][rbs-v4.0.0.dev.1], [v4.0.0.dev.2][rbs-v4.0.0.dev.2], [v4.0.0.dev.3][rbs-v4.0.0.dev.3], [v4.0.0.dev.4][rbs-v4.0.0.dev.4], [v4.0.0.dev.5][rbs-v4.0.0.dev.5], [v4.0.0][rbs-v4.0.0], [v4.0.1.dev.1][rbs-v4.0.1.dev.1], [v4.0.1.dev.2][rbs-v4.0.1.dev.2], [v4.0.1][rbs-v4.0.1], [v4.0.2][rbs-v4.0.2], [v4.0.3][rbs-v4.0.3], [v4.1.0.pre.1][rbs-v4.1.0.pre.1], [v4.1.0.pre.2][rbs-v4.1.0.pre.2], [v4.1.0][rbs-v4.1.0], [v4.1.1.pre.1][rbs-v4.1.1.pre.1], [v4.1.1][rbs-v4.1.1], [v4.1.2][rbs-v4.1.2], [v4.1.3][rbs-v4.1.3], [v4.2.0.pre.1][rbs-v4.2.0.pre.1], [v4.2.0][rbs-v4.2.0] diff --git a/gc/mmtk/mmtk.c b/gc/mmtk/mmtk.c index 8e0c7a72e88e69..6245dffd9c10b7 100644 --- a/gc/mmtk/mmtk.c +++ b/gc/mmtk/mmtk.c @@ -1054,6 +1054,10 @@ rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags alloc_obj[0] = flags; alloc_obj[1] = klass; + if (RB_UNLIKELY(!wb_protected)) { + mmtk_register_wb_unprotected_object((MMTk_ObjectReference)alloc_obj); + } + if (semantics == MMTK_ALLOCATION_SEMANTICS_LOS || ractor_cache->bump_pointer == NULL) { mmtk_post_alloc(ractor_cache->mutator, (void*)alloc_obj, total_size, semantics); } @@ -1741,16 +1745,18 @@ rb_gc_impl_stat_heap(void *objspace_ptr, VALUE heap_name, VALUE hash_or_sym) // Miscellaneous -#define RB_GC_OBJECT_METADATA_ENTRY_COUNT 1 +#define RB_GC_OBJECT_METADATA_ENTRY_COUNT 2 static struct rb_gc_object_metadata_entry object_metadata_entries[RB_GC_OBJECT_METADATA_ENTRY_COUNT + 1]; struct rb_gc_object_metadata_entry * rb_gc_impl_object_metadata(void *objspace_ptr, VALUE obj) { + static ID ID_wb_protected; static ID ID_object_id; if (!ID_object_id) { #define I(s) ID_##s = rb_intern(#s); + I(wb_protected); I(object_id); #undef I } @@ -1764,6 +1770,7 @@ rb_gc_impl_object_metadata(void *objspace_ptr, VALUE obj) n++; \ } while (0) + if (!mmtk_object_wb_unprotected_p((MMTk_ObjectReference)obj)) SET_ENTRY(wb_protected, Qtrue); if (rb_obj_id_p(obj)) SET_ENTRY(object_id, rb_obj_id(obj)); object_metadata_entries[n].name = 0; diff --git a/gems/bundled_gems b/gems/bundled_gems index c514ab06ec0d22..7079bff6ff273f 100644 --- a/gems/bundled_gems +++ b/gems/bundled_gems @@ -12,7 +12,7 @@ rake 13.4.2 https://github.com/ruby/rake test-unit 3.7.8 https://github.com/test-unit/test-unit rexml 3.4.4 https://github.com/ruby/rexml rss 0.3.3 https://github.com/ruby/rss -net-imap 0.6.6 https://github.com/ruby/net-imap +net-imap 0.6.7 https://github.com/ruby/net-imap net-smtp 0.5.1 https://github.com/ruby/net-smtp matrix 0.4.3 https://github.com/ruby/matrix prime 0.1.4 https://github.com/ruby/prime diff --git a/io_buffer.c b/io_buffer.c index 4acf76ac1fa266..da5491ed172df6 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -1158,6 +1158,8 @@ io_buffer_validate_for_writing(struct rb_io_buffer *buffer) static struct rb_io_buffer * get_io_buffer_for_writing(VALUE self) { + rb_check_frozen(self); + struct rb_io_buffer *buffer = get_io_buffer(self); io_buffer_validate_for_writing(buffer); return buffer; @@ -3302,7 +3304,7 @@ io_buffer_set_string(int argc, VALUE *argv, VALUE self) { rb_check_arity(argc, 1, 4); - struct rb_io_buffer *buffer = get_io_buffer(self); + struct rb_io_buffer *buffer = get_io_buffer_for_writing(self); VALUE string = rb_str_to_str(argv[0]); @@ -3317,7 +3319,7 @@ io_buffer_set_string(int argc, VALUE *argv, VALUE self) void rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length) { - struct rb_io_buffer *buffer = get_io_buffer(self); + struct rb_io_buffer *buffer = get_io_buffer_for_writing(self); void *base; size_t size; @@ -3472,10 +3474,10 @@ io_buffer_read_internal(void *_argument) VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t offset, size_t length) { + struct rb_io_buffer *buffer = get_io_buffer_for_writing(self); + io = rb_io_get_io(io); - struct rb_io_buffer *buffer = get_io_buffer(self); - io_buffer_validate_for_writing(buffer); io_buffer_validate_range(buffer, offset, length); if (length == 0) return SIZET2NUM(0); @@ -3563,10 +3565,10 @@ io_buffer_pread_internal(void *_argument) VALUE rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t offset, size_t length) { + struct rb_io_buffer *buffer = get_io_buffer_for_writing(self); + io = rb_io_get_io(io); - struct rb_io_buffer *buffer = get_io_buffer(self); - io_buffer_validate_for_writing(buffer); io_buffer_validate_range(buffer, offset, length); if (length == 0) return SIZET2NUM(0); @@ -4032,7 +4034,7 @@ memory_and_inplace(unsigned char * restrict base, size_t size, unsigned char * r static VALUE io_buffer_and_inplace(VALUE self, VALUE mask) { - struct rb_io_buffer *buffer = get_io_buffer(self); + struct rb_io_buffer *buffer = get_io_buffer_for_writing(self); struct rb_io_buffer *mask_buffer = get_io_buffer(mask); @@ -4080,7 +4082,7 @@ memory_or_inplace(unsigned char * restrict base, size_t size, unsigned char * re static VALUE io_buffer_or_inplace(VALUE self, VALUE mask) { - struct rb_io_buffer *buffer = get_io_buffer(self); + struct rb_io_buffer *buffer = get_io_buffer_for_writing(self); struct rb_io_buffer *mask_buffer = get_io_buffer(mask); @@ -4128,7 +4130,7 @@ memory_xor_inplace(unsigned char * restrict base, size_t size, unsigned char * r static VALUE io_buffer_xor_inplace(VALUE self, VALUE mask) { - struct rb_io_buffer *buffer = get_io_buffer(self); + struct rb_io_buffer *buffer = get_io_buffer_for_writing(self); struct rb_io_buffer *mask_buffer = get_io_buffer(mask); @@ -4176,7 +4178,7 @@ memory_not_inplace(unsigned char * restrict base, size_t size) static VALUE io_buffer_not_inplace(VALUE self) { - struct rb_io_buffer *buffer = get_io_buffer(self); + struct rb_io_buffer *buffer = get_io_buffer_for_writing(self); void *base; size_t size; diff --git a/spec/ruby/core/kernel/require_spec.rb b/spec/ruby/core/kernel/require_spec.rb index 90b2e15a8b60fc..1c223c6aa3d8ae 100644 --- a/spec/ruby/core/kernel/require_spec.rb +++ b/spec/ruby/core/kernel/require_spec.rb @@ -33,15 +33,16 @@ when "jruby" features -= %w[java.rb jruby/util.rb] when "ruby" - so = RbConfig::CONFIG['DLEXT'] - features.reject! { |feature| feature.end_with?("windows_1252.#{so}", "windows_31j.#{so}") } - features.reject! { |feature| feature.end_with? "encdb.#{so}" } - features.reject! { |feature| feature.end_with? "transdb.#{so}" } - features.reject! { |feature| feature.include?('-fake') } + # remove all external libraries first + features.reject! { |feature| File.absolute_path?(feature) } + + # for statically-linked ruby + features -= [ "encdb.so", "trans/transdb.so" ] # the suffixes are always ".so" + features.reject! { |feature| feature.start_with?("enc/") } # and "enc/trans/" end - features_no_ext = features.map { |path| File.basename(path, '.*') } - features_no_ext.sort.should == provided.sort + features_no_ext = features.map { |path| path.sub(/\.(?:rb|so)\z/, '') } + features_no_ext.sort.uniq.should == provided.sort requires = features code = requires.map { |f| "puts require #{f.inspect}\n" }.join diff --git a/spec/ruby/core/string/tr_spec.rb b/spec/ruby/core/string/tr_spec.rb index 91e0d2afa1c7d1..d806898b50f701 100644 --- a/spec/ruby/core/string/tr_spec.rb +++ b/spec/ruby/core/string/tr_spec.rb @@ -32,6 +32,11 @@ "hello".tr("a-z", "A-H.").should == "HE..." end + it "raises an ArgumentError when the receiver has broken encoding" do + -> { "\xFF".tr("€", "b") }.should.raise(ArgumentError) + -> { "\xFF".tr("", "") }.should.raise(ArgumentError) + end + it "raises an ArgumentError when given wrong number of arguments" do -> { "hello".tr }.should.raise(ArgumentError) ruby_version_is ""..."4.1" do @@ -141,6 +146,10 @@ "h€llø".tr("€" => "e", "ø" => "o").should == "hello" end + it "returns a copy of self if the translation hash is empty" do + "hello".tr({}).should == "hello" + end + it "returns a string in the combined encoding" do str = "hello".encode(Encoding::US_ASCII).tr("e" => "é") str.should == "héllo" @@ -160,6 +169,11 @@ ).should == "hello".encode(Encoding::UTF_16LE) end + it "raises ArgumentError the string encoding is broken" do + -> { "\xFF".tr("€" => "b") }.should.raise(ArgumentError) + -> { "\xFF".tr({}) }.should.raise(ArgumentError) + end + it "raises ArgumentError if a key is more than one codepoint" do -> { "hello".tr("hel" => "") }.should.raise(ArgumentError) -> { "🤦🏼‍♂️".tr("🤦🏼‍♂️" => "") }.should.raise(ArgumentError) @@ -231,7 +245,18 @@ end it "returns nil if the string wasn't modified" do - "hello".tr!("€" => "", "Ø" => "").should == nil + s = "hello" + s.tr!({}).should == nil + s.should == "hello" + + s = "hello" + s.tr!("€" => "", "Ø" => "").should == nil + s.should == "hello" + end + + it "raises ArgumentError the string encoding is broken" do + -> { "\xFF".tr!("€" => "b") }.should.raise(ArgumentError) + -> { "\xFF".tr!({}) }.should.raise(ArgumentError) end it "raises ArgumentError if a key is more than one codepoint" do diff --git a/string.c b/string.c index 61a18b62b2fa96..77cae1b4ccdd51 100644 --- a/string.c +++ b/string.c @@ -9362,7 +9362,7 @@ tr_trans_pairs_search_sse2(struct tr_trans_pairs_search *search) matches[i] = _mm_cmpeq_epi8(bytes, masks[i]); } - for (i = i; i < search->needles_count; i++) { + for (i = 1; i < search->needles_count; i++) { matches[0] = _mm_or_si128(matches[0], matches[i]); } @@ -9427,7 +9427,7 @@ tr_trans_pairs_search_neon(struct tr_trans_pairs_search *search) matches[i] = vceqq_u8(bytes, masks[i]); } - for (i = i; i < search->needles_count; i++) { + for (i = 1; i < search->needles_count; i++) { matches[0] = vorrq_u8(matches[0], matches[i]); } @@ -9457,6 +9457,7 @@ tr_trans_pairs(VALUE str, VALUE pairs_val) { Check_Type(pairs_val, T_HASH); size_t pairs_count = RHASH_SIZE(pairs_val); + mustnot_broken(str); rb_str_modify(str); if (RSTRING_LEN(str) == 0 || !RSTRING_PTR(str) || pairs_count == 0) return Qnil; @@ -9486,6 +9487,7 @@ tr_trans_pairs(VALUE str, VALUE pairs_val) bool modify = false; if (RB_LIKELY(rb_str_encindex_fastpath(rb_enc_to_index(e1)))) { + struct tr_trans_pairs_search search = { .s = sstart, .send = sstart + str_len, @@ -9609,6 +9611,10 @@ tr_trans_pairs(VALUE str, VALUE pairs_val) } } + if (!modify) { + return Qnil; + } + if (!STR_EMBED_P(str)) { SIZED_FREE_N(STR_HEAP_PTR(str), STR_HEAP_SIZE(str)); } @@ -9621,13 +9627,10 @@ tr_trans_pairs(VALUE str, VALUE pairs_val) RB_GC_GUARD(hash); - if (modify) { - if (cr != ENC_CODERANGE_BROKEN) - ENC_CODERANGE_SET(str, cr); - rb_enc_associate(str, e1); - return str; - } - return Qnil; + if (cr != ENC_CODERANGE_BROKEN) + ENC_CODERANGE_SET(str, cr); + rb_enc_associate(str, e1); + return str; } /* @@ -9661,9 +9664,24 @@ rb_str_tr_bang(int argc, VALUE *argv, VALUE str) /* * call-seq: * tr(selector, replacements) -> new_string + * tr(pairs) -> new_string + * + * Accepts either a +selector+ and a +replacements+ string, + * or a single +pairs+ Hash. + * + * When a +pairs+ Hash is provided the keys, returns a copy of +self+ with + * the keys of the hash replaced by the values. + * + * - They keys must be strings containing a single codepoints. + * - The values can be of any length. * - * Returns a copy of +self+ with each character specified by string +selector+ - * translated to the corresponding character in string +replacements+. + * Example: + * + * 'hello'.tr('e' => 'er', 'l' => '', 'o' => 'o !') #=> "hero !" + * + * When +selector+ and +replacements+are provided, returns a copy of +self+ + * with each character specified by string +selector+ translated to the + * corresponding character in string +replacements+. * The correspondence is _positional_: * * - Each occurrence of the first character specified by +selector+ @@ -9704,7 +9722,9 @@ rb_str_tr(int argc, VALUE *argv, VALUE str) if (argc == 1) { VALUE pairs = argv[0]; - return tr_trans_pairs(str, pairs); + VALUE result = tr_trans_pairs(str, pairs); + if (NIL_P(result)) result = str; + return str; } VALUE src = argv[0], repl = argv[1]; diff --git a/test/.excludes-mmtk/TestObjSpace.rb b/test/.excludes-mmtk/TestObjSpace.rb index 8fc0a43349b21f..feb05063df63bf 100644 --- a/test/.excludes-mmtk/TestObjSpace.rb +++ b/test/.excludes-mmtk/TestObjSpace.rb @@ -1,6 +1,5 @@ exclude(:test_dump_all_full, "testing behaviour specific to default GC") exclude(:test_dump_flag_age, "testing behaviour specific to default GC") exclude(:test_dump_flags, "testing behaviour specific to default GC") -exclude(:test_dump_flags_wb_protected, "testing behaviour specific to default GC") exclude(:test_dump_objects_dumps_page_slot_sizes, "testing behaviour specific to default GC") exclude(:test_trace_object_allocations_does_not_reuse_freed_allocation_info, "hang up") diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 9eacc3927157a4..e29b2499b3834a 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -838,6 +838,30 @@ def test_transfer_frozen_external assert_equal string, buffer.get_string end + def test_write_frozen + buffer = IO::Buffer.new(8) + buffer.freeze + + mask = IO::Buffer.new(8) + + assert_raise(FrozenError) {buffer.set_string("x")} + assert_raise(FrozenError) {buffer.copy(IO::Buffer.new(8))} + assert_raise(FrozenError) {buffer.clear} + assert_raise(FrozenError) {buffer.set_value(:U8, 0, 1)} + assert_raise(FrozenError) {buffer.set_values([:U8], 0, [1])} + assert_raise(FrozenError) {buffer.and!(mask)} + assert_raise(FrozenError) {buffer.or!(mask)} + assert_raise(FrozenError) {buffer.xor!(mask)} + assert_raise(FrozenError) {buffer.not!} + + File.open(__FILE__) do |file| + assert_raise(FrozenError) {buffer.read(file)} + assert_raise(FrozenError) {buffer.pread(file, 0)} + end + + assert_equal "\0" * 8, buffer.get_string + end + def test_counted_locking buffer = IO::Buffer.new(128) diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 2367d6bfd48a1b..34a05eba1bce2a 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -2687,6 +2687,8 @@ def test_tr_hash assert_equal S(("A"*16 + "x" + "B"*3)), S(("A"*16 + "<" + "B"*3)).tr({"<"=>"x"}) assert_equal S(("x"+"A"*16 + "B"*3)), S(("<"+"A"*16 + "B"*3)).tr({"<"=>"x"}) assert_equal S(("."*15 + "<" * 17)), S(("."*15 + "x"*17)).tr({"x"=>"<"}) + + assert_equal(S("01@3456789abcdefgHij"), S("0123456789abcdefghij").tr("h" => "H", "2" => "@")) end def test_tr!