diff --git a/doc/string/hash.rdoc b/doc/string/hash.rdoc new file mode 100644 index 00000000000000..fe94770ed904b5 --- /dev/null +++ b/doc/string/hash.rdoc @@ -0,0 +1,19 @@ +Returns the integer hash value for +self+. + +Two \String objects that have identical content and compatible encodings +also have the same hash value; +see Object#hash and {Encodings}[rdoc-ref:encodings.rdoc]: + + s = 'foo' + h = s.hash # => -569050784 + h == 'foo'.hash # => true + h == 'food'.hash # => false + h == 'FOO'.hash # => false + + s0 = "äöü" + s1 = s0.encode(Encoding::ISO_8859_1) + s0.encoding # => # + s1.encoding # => # + s0.hash == s1.hash # => false + +Related: see {Querying}[rdoc-ref:String@Querying]. diff --git a/gc.c b/gc.c index c2fc681253aa36..686e727521e358 100644 --- a/gc.c +++ b/gc.c @@ -332,8 +332,6 @@ rb_gc_multi_ractor_p(void) return rb_multi_ractor_p(); } -bool rb_obj_is_main_ractor(VALUE gv); - bool rb_gc_shutdown_call_finalizer_p(VALUE obj) { diff --git a/proc.c b/proc.c index 554ec5e23712b8..ae1068e24fe9cf 100644 --- a/proc.c +++ b/proc.c @@ -4012,12 +4012,13 @@ proc_ruby2_keywords(VALUE procval) switch (proc->block.type) { case block_type_iseq: if (ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_rest && + !ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_post && !ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_kw && !ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.has_kwrest) { ISEQ_BODY(proc->block.as.captured.code.iseq)->param.flags.ruby2_keywords = 1; } else { - rb_warn("Skipping set of ruby2_keywords flag for proc (proc accepts keywords or proc does not accept argument splat)"); + rb_warn("Skipping set of ruby2_keywords flag for proc (proc accepts keywords or post arguments or proc does not accept argument splat)"); } break; default: diff --git a/ractor.c b/ractor.c index 096bda5df6dc1a..c4d748e69ce5f9 100644 --- a/ractor.c +++ b/ractor.c @@ -585,14 +585,6 @@ rb_ractor_main_p_(void) return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor; } -bool -rb_obj_is_main_ractor(VALUE gv) -{ - if (!rb_ractor_p(gv)) return false; - rb_ractor_t *r = DATA_PTR(gv); - return r == GET_VM()->ractor.main_ractor; -} - int rb_ractor_living_thread_num(const rb_ractor_t *r) { diff --git a/shape.c b/shape.c index d58f9f1d0ca568..4345654f840876 100644 --- a/shape.c +++ b/shape.c @@ -1625,7 +1625,6 @@ Init_shape(void) rb_define_const(rb_cShape, "SHAPE_IVAR", INT2NUM(SHAPE_IVAR)); rb_define_const(rb_cShape, "SHAPE_ID_NUM_BITS", INT2NUM(SHAPE_ID_NUM_BITS)); rb_define_const(rb_cShape, "SHAPE_FLAG_SHIFT", INT2NUM(SHAPE_FLAG_SHIFT)); - rb_define_const(rb_cShape, "SPECIAL_CONST_SHAPE_ID", INT2NUM(SPECIAL_CONST_SHAPE_ID)); rb_define_const(rb_cShape, "SHAPE_MAX_VARIATIONS", INT2NUM(SHAPE_MAX_VARIATIONS)); rb_define_const(rb_cShape, "SIZEOF_RB_SHAPE_T", INT2NUM(sizeof(rb_shape_t))); rb_define_const(rb_cShape, "SIZEOF_REDBLACK_NODE_T", INT2NUM(sizeof(redblack_node_t))); diff --git a/shape.h b/shape.h index 2d13c9b762b615..8eb1d2c2d6c795 100644 --- a/shape.h +++ b/shape.h @@ -72,7 +72,6 @@ typedef uint32_t redblack_id_t; #define ROOT_SHAPE_WITH_OBJ_ID 0x1 #define ROOT_TOO_COMPLEX_SHAPE_ID (ROOT_SHAPE_ID | SHAPE_ID_FL_TOO_COMPLEX) #define ROOT_TOO_COMPLEX_WITH_OBJ_ID (ROOT_SHAPE_WITH_OBJ_ID | SHAPE_ID_FL_TOO_COMPLEX | SHAPE_ID_FL_HAS_OBJECT_ID) -#define SPECIAL_CONST_SHAPE_ID (ROOT_SHAPE_ID | SHAPE_ID_FL_FROZEN) typedef struct redblack_node redblack_node_t; diff --git a/spec/ruby/core/module/ruby2_keywords_spec.rb b/spec/ruby/core/module/ruby2_keywords_spec.rb index a9afad4aee3820..73b4ba62fa1184 100644 --- a/spec/ruby/core/module/ruby2_keywords_spec.rb +++ b/spec/ruby/core/module/ruby2_keywords_spec.rb @@ -213,7 +213,7 @@ def obj.foo(a, b, c) end it "prints warning when a method accepts keywords" do obj = Object.new - def obj.foo(a:, b:) end + def obj.foo(*a, b:) end -> { obj.singleton_class.class_exec do @@ -224,7 +224,7 @@ def obj.foo(a:, b:) end it "prints warning when a method accepts keyword splat" do obj = Object.new - def obj.foo(**a) end + def obj.foo(*a, **b) end -> { obj.singleton_class.class_exec do @@ -232,4 +232,17 @@ def obj.foo(**a) end end }.should complain(/Skipping set of ruby2_keywords flag for/) end + + ruby_version_is "3.5" do + it "prints warning when a method accepts post arguments" do + obj = Object.new + def obj.foo(*a, b) end + + -> { + obj.singleton_class.class_exec do + ruby2_keywords :foo + end + }.should complain(/Skipping set of ruby2_keywords flag for/) + end + end end diff --git a/spec/ruby/core/proc/ruby2_keywords_spec.rb b/spec/ruby/core/proc/ruby2_keywords_spec.rb index ab673022317c0a..030eeeeb684640 100644 --- a/spec/ruby/core/proc/ruby2_keywords_spec.rb +++ b/spec/ruby/core/proc/ruby2_keywords_spec.rb @@ -39,7 +39,7 @@ end it "prints warning when a proc accepts keywords" do - f = -> a:, b: { } + f = -> *a, b: { } -> { f.ruby2_keywords @@ -47,10 +47,20 @@ end it "prints warning when a proc accepts keyword splat" do - f = -> **a { } + f = -> *a, **b { } -> { f.ruby2_keywords }.should complain(/Skipping set of ruby2_keywords flag for/) end + + ruby_version_is "3.5" do + it "prints warning when a proc accepts post arguments" do + f = -> *a, b { } + + -> { + f.ruby2_keywords + }.should complain(/Skipping set of ruby2_keywords flag for/) + end + end end diff --git a/string.c b/string.c index d9d8678c71a748..0329d2845aa22a 100644 --- a/string.c +++ b/string.c @@ -4133,10 +4133,8 @@ rb_str_hash_cmp(VALUE str1, VALUE str2) * call-seq: * hash -> integer * - * Returns the integer hash value for +self+. - * The value is based on the length, content and encoding of +self+. + * :include: doc/string/hash.rdoc * - * Related: Object#hash. */ static VALUE diff --git a/test/ruby/test_keyword.rb b/test/ruby/test_keyword.rb index 1e3e0e53b129a4..c836abd0c663b4 100644 --- a/test/ruby/test_keyword.rb +++ b/test/ruby/test_keyword.rb @@ -2424,6 +2424,21 @@ class << c assert_raise(ArgumentError) { m.call(42, a: 1, **h2) } end + def test_ruby2_keywords_post_arg + def self.a(*c, **kw) [c, kw] end + def self.b(*a, b) a(*a, b) end + assert_warn(/Skipping set of ruby2_keywords flag for b \(method accepts keywords or post arguments or method does not accept argument splat\)/) do + assert_nil(singleton_class.send(:ruby2_keywords, :b)) + end + assert_equal([[{foo: 1}, {bar: 1}], {}], b({foo: 1}, bar: 1)) + + b = ->(*a, b){a(*a, b)} + assert_warn(/Skipping set of ruby2_keywords flag for proc \(proc accepts keywords or post arguments or proc does not accept argument splat\)/) do + b.ruby2_keywords + end + assert_equal([[{foo: 1}, {bar: 1}], {}], b.({foo: 1}, bar: 1)) + end + def test_proc_ruby2_keywords h1 = {:a=>1} foo = ->(*args, &block){block.call(*args)} @@ -2436,8 +2451,8 @@ def test_proc_ruby2_keywords assert_raise(ArgumentError) { foo.call(:a=>1, &->(arg, **kw){[arg, kw]}) } assert_equal(h1, foo.call(:a=>1, &->(arg){arg})) - [->(){}, ->(arg){}, ->(*args, **kw){}, ->(*args, k: 1){}, ->(*args, k: ){}].each do |pr| - assert_warn(/Skipping set of ruby2_keywords flag for proc \(proc accepts keywords or proc does not accept argument splat\)/) do + [->(){}, ->(arg){}, ->(*args, x){}, ->(*args, **kw){}, ->(*args, k: 1){}, ->(*args, k: ){}].each do |pr| + assert_warn(/Skipping set of ruby2_keywords flag for proc \(proc accepts keywords or post arguments or proc does not accept argument splat\)/) do pr.ruby2_keywords end end @@ -2790,10 +2805,21 @@ def method_missing(*args) assert_equal(:opt, o.clear_last_opt(a: 1)) assert_nothing_raised(ArgumentError) { o.clear_last_empty_method(a: 1) } - assert_warn(/Skipping set of ruby2_keywords flag for bar \(method accepts keywords or method does not accept argument splat\)/) do + assert_warn(/Skipping set of ruby2_keywords flag for bar \(method accepts keywords or post arguments or method does not accept argument splat\)/) do assert_nil(c.send(:ruby2_keywords, :bar)) end + c.class_eval do + def bar_post(*a, x) = nil + define_method(:bar_post_bmethod) { |*a, x| } + end + assert_warn(/Skipping set of ruby2_keywords flag for bar_post \(method accepts keywords or post arguments or method does not accept argument splat\)/) do + assert_nil(c.send(:ruby2_keywords, :bar_post)) + end + assert_warn(/Skipping set of ruby2_keywords flag for bar_post_bmethod \(method accepts keywords or post arguments or method does not accept argument splat\)/) do + assert_nil(c.send(:ruby2_keywords, :bar_post_bmethod)) + end + utf16_sym = "abcdef".encode("UTF-16LE").to_sym c.send(:define_method, utf16_sym, c.instance_method(:itself)) assert_warn(/abcdef/) do diff --git a/test/ruby/test_ractor.rb b/test/ruby/test_ractor.rb index 2cfd56d2677652..c1f33798ba7bf3 100644 --- a/test/ruby/test_ractor.rb +++ b/test/ruby/test_ractor.rb @@ -202,6 +202,13 @@ def ractor_job(job_count, array_size) RUBY end + # [Bug #20146] + def test_max_cpu_1 + assert_ractor(<<~'RUBY', args: [{ "RUBY_MAX_CPU" => "1" }]) + assert_equal :ok, Ractor.new { :ok }.value + RUBY + end + def assert_make_shareable(obj) refute Ractor.shareable?(obj), "object was already shareable" Ractor.make_shareable(obj) diff --git a/test/ruby/test_shapes.rb b/test/ruby/test_shapes.rb index aa488e04218b28..08a841d25492b1 100644 --- a/test/ruby/test_shapes.rb +++ b/test/ruby/test_shapes.rb @@ -1051,8 +1051,10 @@ def test_raise_on_special_consts # RUBY_PLATFORM =~ /i686/ end - def test_root_shape_transition_to_special_const_on_frozen - assert_equal(RubyVM::Shape::SPECIAL_CONST_SHAPE_ID, RubyVM::Shape.of([].freeze).id) + def test_root_shape_frozen + frozen_root_shape = RubyVM::Shape.of([].freeze) + assert_predicate(frozen_root_shape, :frozen?) + assert_equal(RubyVM::Shape.root_shape.id, frozen_root_shape.raw_id) end def test_basic_shape_transition diff --git a/thread_pthread_mn.c b/thread_pthread_mn.c index 4a671cf3a172f7..0598b8d295447a 100644 --- a/thread_pthread_mn.c +++ b/thread_pthread_mn.c @@ -397,11 +397,15 @@ native_thread_check_and_create_shared(rb_vm_t *vm) rb_native_mutex_lock(&vm->ractor.sched.lock); { - unsigned int snt_cnt = vm->ractor.sched.snt_cnt; - if (!vm->ractor.main_ractor->threads.sched.enable_mn_threads) snt_cnt++; // do not need snt for main ractor + unsigned int schedulable_ractor_cnt = vm->ractor.cnt; + RUBY_ASSERT(schedulable_ractor_cnt >= 1); + + if (!vm->ractor.main_ractor->threads.sched.enable_mn_threads) + schedulable_ractor_cnt--; // do not need snt for main ractor + unsigned int snt_cnt = vm->ractor.sched.snt_cnt; if (((int)snt_cnt < MINIMUM_SNT) || - (snt_cnt < vm->ractor.cnt && + (snt_cnt < schedulable_ractor_cnt && snt_cnt < vm->ractor.sched.max_cpu)) { RUBY_DEBUG_LOG("added snt:%u dnt:%u ractor_cnt:%u grq_cnt:%u", diff --git a/vm_method.c b/vm_method.c index 241e4cacefdce4..03fb79cddd0d87 100644 --- a/vm_method.c +++ b/vm_method.c @@ -2959,13 +2959,14 @@ rb_mod_ruby2_keywords(int argc, VALUE *argv, VALUE module) switch (me->def->type) { case VM_METHOD_TYPE_ISEQ: if (ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_rest && + !ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_post && !ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_kw && !ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.has_kwrest) { ISEQ_BODY(me->def->body.iseq.iseqptr)->param.flags.ruby2_keywords = 1; rb_clear_method_cache(module, name); } else { - rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method accepts keywords or method does not accept argument splat)", QUOTE_ID(name)); + rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method accepts keywords or post arguments or method does not accept argument splat)", QUOTE_ID(name)); } break; case VM_METHOD_TYPE_BMETHOD: { @@ -2978,13 +2979,14 @@ rb_mod_ruby2_keywords(int argc, VALUE *argv, VALUE module) const struct rb_captured_block *captured = VM_BH_TO_ISEQ_BLOCK(procval); const rb_iseq_t *iseq = rb_iseq_check(captured->code.iseq); if (ISEQ_BODY(iseq)->param.flags.has_rest && + !ISEQ_BODY(iseq)->param.flags.has_post && !ISEQ_BODY(iseq)->param.flags.has_kw && !ISEQ_BODY(iseq)->param.flags.has_kwrest) { ISEQ_BODY(iseq)->param.flags.ruby2_keywords = 1; rb_clear_method_cache(module, name); } else { - rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method accepts keywords or method does not accept argument splat)", QUOTE_ID(name)); + rb_warn("Skipping set of ruby2_keywords flag for %"PRIsVALUE" (method accepts keywords or post arguments or method does not accept argument splat)", QUOTE_ID(name)); } break; } diff --git a/zjit.c b/zjit.c index 09ab128ae3f6f8..54d9f7ed86ad2f 100644 --- a/zjit.c +++ b/zjit.c @@ -347,7 +347,6 @@ rb_zjit_shape_obj_too_complex_p(VALUE obj) } enum { - RB_SPECIAL_CONST_SHAPE_ID = SPECIAL_CONST_SHAPE_ID, RB_INVALID_SHAPE_ID = INVALID_SHAPE_ID, }; diff --git a/zjit/bindgen/src/main.rs b/zjit/bindgen/src/main.rs index 59b7f9737ea233..ac1034199653dd 100644 --- a/zjit/bindgen/src/main.rs +++ b/zjit/bindgen/src/main.rs @@ -361,7 +361,6 @@ fn main() { .allowlist_function("rb_zjit_singleton_class_p") .allowlist_type("robject_offsets") .allowlist_type("rstring_offsets") - .allowlist_var("RB_SPECIAL_CONST_SHAPE_ID") .allowlist_var("RB_INVALID_SHAPE_ID") // From jit.c diff --git a/zjit/src/codegen.rs b/zjit/src/codegen.rs index db56db09275d47..f502801aff8be2 100644 --- a/zjit/src/codegen.rs +++ b/zjit/src/codegen.rs @@ -1005,35 +1005,11 @@ fn gen_new_hash( pairs.push(val); } - let n = pairs.len(); - - // Calculate the compile-time NATIVE_STACK_PTR offset from NATIVE_BASE_PTR - // At this point, frame_setup(&[], jit.c_stack_slots) has been called, - // which allocated aligned_stack_bytes(jit.c_stack_slots) on the stack - let frame_size = aligned_stack_bytes(jit.c_stack_slots); - let allocation_size = aligned_stack_bytes(n); - - asm_comment!(asm, "allocate {} bytes on C stack for {} hash elements", allocation_size, n); - asm.sub_into(NATIVE_STACK_PTR, allocation_size.into()); - - // Calculate the total offset from NATIVE_BASE_PTR to our buffer - let total_offset_from_base = (frame_size + allocation_size) as i32; - - for (idx, &pair_opnd) in pairs.iter().enumerate() { - let slot_offset = -total_offset_from_base + (idx as i32 * SIZEOF_VALUE_I32); - asm.mov( - Opnd::mem(VALUE_BITS, NATIVE_BASE_PTR, slot_offset), - pair_opnd - ); - } - - let argv = asm.lea(Opnd::mem(64, NATIVE_BASE_PTR, -total_offset_from_base)); - + let argv = gen_push_opnds(jit, asm, &pairs); let argc = (elements.len() * 2) as ::std::os::raw::c_long; asm_ccall!(asm, rb_hash_bulk_insert, lir::Opnd::Imm(argc), argv, new_hash); - asm_comment!(asm, "restore C stack pointer"); - asm.add_into(NATIVE_STACK_PTR, allocation_size.into()); + gen_pop_opnds(asm, &pairs); } new_hash diff --git a/zjit/src/cruby.rs b/zjit/src/cruby.rs index eff99daf9b93d0..3ec2954c73f6a7 100644 --- a/zjit/src/cruby.rs +++ b/zjit/src/cruby.rs @@ -269,7 +269,6 @@ pub type IseqPtr = *const rb_iseq_t; #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct ShapeId(pub u32); -pub const SPECIAL_CONST_SHAPE_ID: ShapeId = ShapeId(RB_SPECIAL_CONST_SHAPE_ID); pub const INVALID_SHAPE_ID: ShapeId = ShapeId(RB_INVALID_SHAPE_ID); // Given an ISEQ pointer, convert PC to insn_idx diff --git a/zjit/src/cruby_bindings.inc.rs b/zjit/src/cruby_bindings.inc.rs index 524b06b58047f5..ee6d4d5e0e2938 100644 --- a/zjit/src/cruby_bindings.inc.rs +++ b/zjit/src/cruby_bindings.inc.rs @@ -724,7 +724,6 @@ pub const DEFINED_REF: defined_type = 15; pub const DEFINED_FUNC: defined_type = 16; pub const DEFINED_CONST_FROM: defined_type = 17; pub type defined_type = u32; -pub const RB_SPECIAL_CONST_SHAPE_ID: _bindgen_ty_38 = 33554432; pub const RB_INVALID_SHAPE_ID: _bindgen_ty_38 = 4294967295; pub type _bindgen_ty_38 = u32; pub type rb_iseq_param_keyword_struct = rb_iseq_constant_body__bindgen_ty_1_rb_iseq_param_keyword;