diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index c7eb2865f11485..d0a8024b053b0d 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -12,4 +12,4 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1 + - uses: actions/labeler@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0 diff --git a/ext/objspace/objspace.c b/ext/objspace/objspace.c index 8a4128e1b4e11b..ffe184573592f3 100644 --- a/ext/objspace/objspace.c +++ b/ext/objspace/objspace.c @@ -230,7 +230,7 @@ type2sym(enum ruby_value_type i) * T_DATA may be wrong. * * It returns a hash as: - * {:TOTAL=>1461154, :T_CLASS=>158280, :T_MODULE=>20672, :T_STRING=>527249, ...} + * {TOTAL: 1461154, T_CLASS: 158280, T_MODULE: 20672, T_STRING: 527249, ...} * * If the optional argument, result_hash, is given, * it is overwritten and returned. diff --git a/ext/stringio/stringio.c b/ext/stringio/stringio.c index 09757a283eaf7c..41aa71f893afe7 100644 --- a/ext/stringio/stringio.c +++ b/ext/stringio/stringio.c @@ -161,6 +161,19 @@ strio_substr(struct StringIO *ptr, long pos, long len, rb_encoding *enc) return enc_subseq(str, pos, len, enc); } +static VALUE +strio_readbuf(struct StringIO *ptr, VALUE str) +{ + if (!NIL_P(str)) { + StringValue(str); + rb_str_modify(str); + if (str == ptr->string) { + rb_raise(rb_eArgError, "cannot read into the underlying string"); + } + } + return str; +} + #define StringIO(obj) get_strio(obj) #define StringIOForRead(obj) get_strio_for_read(obj) @@ -1684,11 +1697,7 @@ strio_read(int argc, VALUE *argv, VALUE self) switch (argc) { case 2: - str = argv[1]; - if (!NIL_P(str)) { - StringValue(str); - rb_str_modify(str); - } + str = strio_readbuf(ptr, argv[1]); /* fall through */ case 1: if (!NIL_P(argv[0])) { @@ -1753,6 +1762,8 @@ static VALUE strio_pread(int argc, VALUE *argv, VALUE self) { VALUE rb_len, rb_offset, rb_buf; + struct StringIO *ptr = readable(self); + rb_scan_args(argc, argv, "21", &rb_len, &rb_offset, &rb_buf); long len = NUM2LONG(rb_len); long offset = NUM2LONG(rb_offset); @@ -1761,6 +1772,12 @@ strio_pread(int argc, VALUE *argv, VALUE self) rb_raise(rb_eArgError, "negative string size (or size too big): %" PRIsVALUE, rb_len); } + if (offset < 0) { + rb_syserr_fail_str(EINVAL, rb_sprintf("pread: Invalid offset argument: %" PRIsVALUE, rb_offset)); + } + + rb_buf = strio_readbuf(ptr, rb_buf); + if (len == 0) { if (NIL_P(rb_buf)) { return rb_str_new("", 0); @@ -1768,12 +1785,6 @@ strio_pread(int argc, VALUE *argv, VALUE self) return rb_buf; } - if (offset < 0) { - rb_syserr_fail_str(EINVAL, rb_sprintf("pread: Invalid offset argument: %" PRIsVALUE, rb_offset)); - } - - struct StringIO *ptr = readable(self); - if (outside_p(ptr, offset)) { rb_eof_error(); } diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb index f4fe2fc44b5b61..daff07ecb0abff 100644 --- a/test/ruby/test_refinement.rb +++ b/test/ruby/test_refinement.rb @@ -1058,6 +1058,586 @@ def chop! end; end + { + zsuper: "public :a", + super: "def a = super" + }.each do |desc, method_def| + define_method :"test_modify_#{desc}_refinement_method_in_superclass" do + assert_separately([], <<-"end;") + class A + private def a = :a + alias a a + end + + class B < A + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:a, B.new.a) + + class A + def a = :b + end + assert_equal(:b, B.new.a) + end; + end + + define_method :"test_modify_#{desc}_refinement_method_in_module_prepended_to_superclass" do + assert_separately([], <<-"end;") + module M + private def a = :a + alias a a + end + + class A + prepend M + end + + class B < A + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:a, B.new.a) + + module M + def a = :b + end + assert_equal(:b, B.new.a) + end; + end + + define_method :"test_modify_#{desc}_refinement_method_in_module_included_in_superclass" do + assert_separately([], <<-"end;") + module M + private def a = :a + alias a a + end + + class A + include M + end + + class B < A + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:a, B.new.a) + + module M + def a = :b + end + assert_equal(:b, B.new.a) + end; + end + + define_method :"test_remove_#{desc}_refinement_method_from_superclass" do + assert_separately([], <<-"end;") + class A + private def a = :a + end + + class B < A + private def a = :b + end + + class C < B + end + + module R + refine C do + #{method_def} + end + end + using R + assert_equal(:b, C.new.a) + + class B + remove_method(:a) + end + assert_equal(:a, C.new.a) + end; + end + + define_method :"test_remove_#{desc}_refinement_method_from_module_prepended_to_superclass" do + assert_separately([], <<-"end;") + module M + private def a = :b + end + + class A + prepend M + private def a = :a + end + + class B < A + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:b, B.new.a) + + module M + remove_method(:a) + end + assert_equal(:a, B.new.a) + end; + end + + define_method :"test_remove_#{desc}_refinement_method_from_module_prepended_to_class" do + assert_separately([], <<-"end;") + module M + private def a = :b + end + + class A + prepend M + private def a = :a + end + + module R + refine A do + #{method_def} + end + end + using R + assert_equal(:b, A.new.a) + + module M + remove_method(:a) + end + assert_equal(:a, A.new.a) + end; + end + + define_method :"test_remove_#{desc}_refinement_method_from_module_included_in_superclass" do + assert_separately([], <<-"end;") + module M + private def a = :b + end + + class A + private def a = :a + end + + class B < A + include M + end + + class C < B + end + + module R + refine C do + #{method_def} + end + end + using R + assert_equal(:b, C.new.a) + + module M + remove_method(:a) + end + assert_equal(:a, C.new.a) + end; + end + + define_method :"test_remove_#{desc}_refinement_method_from_module_included_in_class" do + assert_separately([], <<-"end;") + module M + private def a = :b + end + + class A + private def a = :a + end + + class B < A + include M + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:b, B.new.a) + + module M + remove_method(:a) + end + assert_equal(:a, B.new.a) + end; + end + + define_method :"test_undef_#{desc}_refinement_method_in_superclass" do + assert_separately([], <<-"end;") + class A + private def a = :a + end + + class B < A + private def a = :b + end + + class C < B + end + + module R + refine C do + #{method_def} + end + end + using R + assert_equal(:b, C.new.a) + + class B + undef_method(:a) + end + assert_raise(NoMethodError) { C.new.a } + end; + end + + define_method :"test_undef_#{desc}_refinement_method_in_module_prepended_to_superclass" do + assert_separately([], <<-"end;") + module M + private def a = :b + end + + class A + prepend M + private def a = :a + end + + class B < A + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:b, B.new.a) + + module M + undef_method(:a) + end + assert_raise(NoMethodError) { B.new.a } + end; + end + + define_method :"test_undef_#{desc}_refinement_method_in_module_prepended_to_class" do + assert_separately([], <<-"end;") + module M + private def a = :b + end + + class A + prepend M + private def a = :a + end + + module R + refine A do + #{method_def} + end + end + using R + assert_equal(:b, A.new.a) + + module M + undef_method(:a) + end + assert_raise(NoMethodError) { A.new.a } + end; + end + + define_method :"test_undef_#{desc}_refinement_method_in_module_included_in_superclass" do + assert_separately([], <<-"end;") + module M + private def a = :b + end + + class A + private def a = :a + end + + class B < A + include M + end + + class C < B + end + + module R + refine C do + #{method_def} + end + end + using R + assert_equal(:b, C.new.a) + + module M + undef_method(:a) + end + assert_raise(NoMethodError) { C.new.a } + end; + end + + define_method :"test_undef_#{desc}_refinement_method_in_module_included_in_class" do + assert_separately([], <<-"end;") + module M + private def a = :b + end + + class A + private def a = :a + end + + class B < A + include M + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:b, B.new.a) + + module M + undef_method(:a) + end + assert_raise(NoMethodError) { B.new.a } + end; + end + + define_method :"test_override_#{desc}_refinement_method_by_prepending_to_class" do + assert_separately([], <<-"end;") + class A + private def a = :a + end + + module R + refine A do + #{method_def} + end + end + using R + assert_equal(:a, A.new.a) + + module M + def a = :b + end + A.prepend M + assert_equal(:b, A.new.a) + end; + end + + define_method :"test_override_#{desc}_refinement_method_by_prepending_to_superclass" do + assert_separately([], <<-"end;") + class A + private def a = :a + end + + class B < A + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:a, B.new.a) + + module M + def a = :b + end + A.prepend M + assert_equal(:b, B.new.a) + end; + end + + define_method :"test_override_#{desc}_refinement_method_by_including_in_class" do + assert_separately([], <<-"end;") + class A + private def a = :a + end + + class B < A + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:a, B.new.a) + + module M + def a = :b + end + B.include M + assert_equal(:b, B.new.a) + end; + end + + define_method :"test_override_#{desc}_refinement_method_by_including_in_superclass" do + assert_separately([], <<-"end;") + class A + private def a = :a + end + + class B < A + end + + class C < B + end + + module R + refine C do + #{method_def} + end + end + using R + assert_equal(:a, C.new.a) + + module M + def a = :b + end + B.include M + assert_equal(:b, C.new.a) + end; + end + + define_method :"test_override_#{desc}_refinement_method_by_prepending_undef_to_class" do + assert_separately([], <<-"end;") + class A + private def a = :a + end + + module R + refine A do + #{method_def} + end + end + using R + assert_equal(:a, A.new.a) + + module M + def a = :b + undef_method :a + end + A.prepend M + assert_raise(NoMethodError) { A.new.a } + end; + end + + define_method :"test_override_#{desc}_refinement_method_by_prepending_undef_to_superclass" do + assert_separately([], <<-"end;") + class A + private def a = :a + end + + class B < A + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:a, B.new.a) + + module M + def a = :b + undef_method :a + end + A.prepend M + assert_raise(NoMethodError) { B.new.a } + end; + end + + define_method :"test_override_#{desc}_refinement_method_by_including_undef_in_class" do + assert_separately([], <<-"end;") + class A + private def a = :a + end + + class B < A + end + + module R + refine B do + #{method_def} + end + end + using R + assert_equal(:a, B.new.a) + + module M + def a = :b + undef_method :a + end + B.include M + assert_raise(NoMethodError) { B.new.a } + end; + end + + define_method :"test_override_#{desc}_refinement_method_by_including_undef_in_superclass" do + assert_separately([], <<-"end;") + class A + private def a = :a + end + + class B < A + end + + class C < B + end + + module R + refine C do + #{method_def} + end + end + using R + assert_equal(:a, C.new.a) + + module M + def a = :b + undef_method :a + end + B.include M + assert_raise(NoMethodError) { C.new.a } + end; + end + end + def test_instance_methods bug8881 = '[ruby-core:57080] [Bug #8881]' assert_not_include(Foo.instance_methods(false), :z, bug8881) diff --git a/test/stringio/test_stringio.rb b/test/stringio/test_stringio.rb index 656c0bb91d25c5..0f61245a8a4686 100644 --- a/test/stringio/test_stringio.rb +++ b/test/stringio/test_stringio.rb @@ -765,6 +765,8 @@ def test_read s = "" f.read(nil, s) assert_equal(Encoding::ASCII_8BIT, s.encoding, bug20418) + + assert_raise(ArgumentError) {f.read(1, f.string)} end def test_readpartial @@ -830,10 +832,13 @@ def test_pread assert_raise(EOFError) { f.pread(1, 5) } assert_raise(ArgumentError) { f.pread(-1, 0) } + assert_raise(ArgumentError) { f.pread(0, 0, f.string) } assert_raise(Errno::EINVAL) { f.pread(3, -1) } + assert_raise(Errno::EINVAL) { f.pread(0, -1) } + assert_raise(IOError) { StringIO.new(nil, "w").pread(3, 0) } + assert_raise(TypeError) { f.pread(3, 0, []) } assert_equal "".b, StringIO.new("").pread(0, 0) - assert_equal "".b, StringIO.new("").pread(0, -10) buf = "stale".b assert_equal "stale".b, StringIO.new("").pread(0, 0, buf) diff --git a/vm_method.c b/vm_method.c index 9a090d1eb7eaed..e9f0d85cae4c0c 100644 --- a/vm_method.c +++ b/vm_method.c @@ -1370,6 +1370,12 @@ check_override_opt_method(VALUE klass, VALUE mid) } } +static VALUE +zsuper_to_super(int argc, VALUE *argv, VALUE self) +{ + return rb_call_super_kw(argc, argv, RB_PASS_CALLED_KEYWORDS); +} + static inline rb_method_entry_t* search_method0(VALUE klass, ID id, VALUE *defined_class_ptr, bool skip_refined); /* * klass->method_table[mid] = method_entry(defined_class, visi, def) @@ -1386,6 +1392,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil st_data_t data; int make_refined = 0; VALUE orig_klass; + bool turn_zsuper_to_super = false; if (NIL_P(klass)) { klass = rb_cObject; @@ -1411,12 +1418,10 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil if (RB_TYPE_P(klass, T_MODULE) && FL_TEST(klass, RMODULE_IS_REFINEMENT)) { VALUE refined_class = rb_refinement_module_get_refined_class(klass); - bool search_superclass = type == VM_METHOD_TYPE_ZSUPER && !lookup_method_table(refined_class, mid); - rb_add_refined_method_entry(refined_class, mid); - if (search_superclass) { - rb_method_entry_t *me = lookup_method_table(refined_class, mid); - RB_OBJ_WRITE(me, &me->def->body.refined.orig_me, search_method0(refined_class, mid, NULL, true)); + if (type == VM_METHOD_TYPE_ZSUPER) { + turn_zsuper_to_super = true; } + rb_add_refined_method_entry(refined_class, mid); } if (type == VM_METHOD_TYPE_REFINED) { rb_method_entry_t *old_me = lookup_method_table(RCLASS_ORIGIN(klass), mid); @@ -1479,6 +1484,12 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil me = rb_method_entry_create(mid, defined_class, visi, NULL); if (def == NULL) { def = rb_method_definition_create(type, original_id); + if (turn_zsuper_to_super) { + def->type = VM_METHOD_TYPE_CFUNC; + def->body.cfunc.func = (rb_cfunc_t)zsuper_to_super; + def->body.cfunc.invoker = ractor_safe_call_cfunc_m1; + def->body.cfunc.argc = -1; + } } rb_method_definition_set(me, def, opts);