Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion error.c
Original file line number Diff line number Diff line change
Expand Up @@ -4163,7 +4163,7 @@ rb_error_frozen_object(VALUE frozen_obj)
rb_yjit_lazy_push_frame(GET_EC()->cfp->pc);

VALUE mesg = rb_sprintf("can't modify frozen %"PRIsVALUE": ",
CLASS_OF(frozen_obj));
rb_obj_class(frozen_obj));
VALUE exc = rb_exc_new_str(rb_eFrozenError, mesg);

rb_ivar_set(exc, id_recv, frozen_obj);
Expand Down
32 changes: 1 addition & 31 deletions eval.c
Original file line number Diff line number Diff line change
Expand Up @@ -428,40 +428,10 @@ rb_class_modify_check(VALUE klass)
rb_class_set_initialized(klass);
}
if (OBJ_FROZEN(klass)) {
const char *desc;

if (RCLASS_SINGLETON_P(klass)) {
desc = "object";
klass = RCLASS_ATTACHED_OBJECT(klass);
if (!SPECIAL_CONST_P(klass)) {
switch (BUILTIN_TYPE(klass)) {
case T_MODULE:
case T_ICLASS:
desc = "Module";
break;
case T_CLASS:
desc = "Class";
break;
default:
break;
}
}
}
else {
switch (BUILTIN_TYPE(klass)) {
case T_MODULE:
case T_ICLASS:
desc = "module";
break;
case T_CLASS:
desc = "class";
break;
default:
Check_Type(klass, T_CLASS);
UNREACHABLE;
}
}
rb_frozen_error_raise(klass, "can't modify frozen %s: %"PRIsVALUE, desc, klass);
rb_error_frozen_object(klass);
}
}

Expand Down
4 changes: 3 additions & 1 deletion spec/ruby/core/exception/frozen_error_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ def o.x; end
object = Object.new
object.freeze

msg_class = ruby_version_is("4.0") ? "Object" : "object"

-> {
def object.x; end
}.should raise_error(FrozenError, "can't modify frozen object: #{object}")
}.should raise_error(FrozenError, "can't modify frozen #{msg_class}: #{object}")

object = [].freeze
-> { object << nil }.should raise_error(FrozenError, "can't modify frozen Array: []")
Expand Down
9 changes: 6 additions & 3 deletions spec/ruby/language/def_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ def foo(a); end
def foo; end
end
}.should raise_error(FrozenError) { |e|
e.message.should == "can't modify frozen module: #{e.receiver}"
msg_class = ruby_version_is("4.0") ? "Module" : "module"
e.message.should == "can't modify frozen #{msg_class}: #{e.receiver}"
}

-> {
Expand All @@ -106,7 +107,8 @@ def foo; end
def foo; end
end
}.should raise_error(FrozenError){ |e|
e.message.should == "can't modify frozen class: #{e.receiver}"
msg_class = ruby_version_is("4.0") ? "Class" : "class"
e.message.should == "can't modify frozen #{msg_class}: #{e.receiver}"
}
end
end
Expand Down Expand Up @@ -283,7 +285,8 @@ def obj.==(other)
it "raises FrozenError with the correct class name" do
obj = Object.new
obj.freeze
-> { def obj.foo; end }.should raise_error(FrozenError, "can't modify frozen object: #{obj}")
msg_class = ruby_version_is("4.0") ? "Object" : "object"
-> { def obj.foo; end }.should raise_error(FrozenError, "can't modify frozen #{msg_class}: #{obj}")

obj = Object.new
c = obj.singleton_class
Expand Down
53 changes: 53 additions & 0 deletions test/ruby/test_allocation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,59 @@ def self.splat_and_keyword_splat(*b, **kw#{block}); end
RUBY
end

def test_anonymous_splat_parameter
only_block = block.empty? ? block : block[2..]
check_allocations(<<~RUBY)
def self.anon_splat(*#{block}); end

check_allocations(1, 1, "anon_splat(1, a: 2#{block})")
check_allocations(1, 1, "anon_splat(1, *empty_array, a: 2#{block})")
check_allocations(1, 1, "anon_splat(1, a:2, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat(1, **empty_hash, a: 2#{block})")

check_allocations(1, 0, "anon_splat(1, **nil#{block})")
check_allocations(1, 0, "anon_splat(1, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat(1, **hash1#{block})")
check_allocations(1, 1, "anon_splat(1, *empty_array, **hash1#{block})")
check_allocations(1, 1, "anon_splat(1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat(1, **empty_hash, **hash1#{block})")

check_allocations(1, 0, "anon_splat(1, *empty_array#{block})")
check_allocations(1, 0, "anon_splat(1, *empty_array, *empty_array, **empty_hash#{block})")

check_allocations(1, 1, "anon_splat(*array1, a: 2#{block})")

check_allocations(0, 0, "anon_splat(*nil, **nill#{block})")
check_allocations(0, 0, "anon_splat(*array1, **nill#{block})")
check_allocations(0, 0, "anon_splat(*array1, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat(*array1, **hash1#{block})")
check_allocations(1, 1, "anon_splat(*array1, *empty_array, **hash1#{block})")

check_allocations(1, 0, "anon_splat(*array1, *empty_array#{block})")
check_allocations(1, 0, "anon_splat(*array1, *empty_array, **empty_hash#{block})")

check_allocations(1, 1, "anon_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")

check_allocations(0, 0, "anon_splat(#{only_block})")
check_allocations(1, 1, "anon_splat(a: 2#{block})")
check_allocations(0, 0, "anon_splat(**empty_hash#{block})")

check_allocations(1, 1, "anon_splat(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat(1, *empty_array, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "anon_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "anon_splat(*array1, **hash1, **empty_hash#{block})")

unless defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled?
check_allocations(0, 0, "anon_splat(*array1, **nil#{block})")
check_allocations(1, 0, "anon_splat(*r2k_empty_array#{block})")
check_allocations(1, 1, "anon_splat(*r2k_array#{block})")
check_allocations(1, 0, "anon_splat(*r2k_empty_array1#{block})")
check_allocations(1, 1, "anon_splat(*r2k_array1#{block})")
end
RUBY
end

def test_anonymous_splat_and_anonymous_keyword_splat_parameters
only_block = block.empty? ? block : block[2..]
check_allocations(<<~RUBY)
Expand Down
29 changes: 29 additions & 0 deletions test/ruby/test_call.rb
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,35 @@ def self.s(*, kw: 0, **kws) [*->(*a){a}.call(*), kw, kws] end
assert_equal([1, 2, {}], s(*r2ka))
end

def test_anon_splat_mutated_bug_21757
args = [1, 2]
kw = {bug: true}

def self.m(*); end
m(*args, bug: true)
assert_equal(2, args.length)

proc = ->(*) { }
proc.(*args, bug: true)
assert_equal(2, args.length)

def self.m2(*); end
m2(*args, **kw)
assert_equal(2, args.length)

proc = ->(*) { }
proc.(*args, **kw)
assert_equal(2, args.length)

def self.m3(*, **nil); end
assert_raise(ArgumentError) { m3(*args, bug: true) }
assert_equal(2, args.length)

proc = ->(*, **nil) { }
assert_raise(ArgumentError) { proc.(*args, bug: true) }
assert_equal(2, args.length)
end

def test_kwsplat_block_eval_order
def self.t(**kw, &b) [kw, b] end

Expand Down
2 changes: 1 addition & 1 deletion test/ruby/test_class.rb
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ def test_singleton_class_of_frozen_object
obj = Object.new
c = obj.singleton_class
obj.freeze
assert_raise_with_message(FrozenError, /frozen object/) {
assert_raise_with_message(FrozenError, /frozen Object/) {
c.class_eval {def f; end}
}
end
Expand Down
16 changes: 16 additions & 0 deletions test/ruby/test_frozen.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,20 @@ def test_setting_ivar_on_frozen_string_with_ivars
str.freeze
assert_raise(FrozenError) { str.instance_variable_set(:@b, 1) }
end

def test_setting_ivar_on_frozen_string_with_singleton_class
str = "str"
str.singleton_class
str.freeze
assert_raise_with_message(FrozenError, "can't modify frozen String: \"str\"") { str.instance_variable_set(:@a, 1) }
end

class A
freeze
end

def test_setting_ivar_on_frozen_class
assert_raise_with_message(FrozenError, "can't modify frozen Class: TestFrozen::A") { A.instance_variable_set(:@a, 1) }
assert_raise_with_message(FrozenError, "can't modify frozen Class: #<Class:TestFrozen::A>") { A.singleton_class.instance_variable_set(:@a, 1) }
end
end
6 changes: 3 additions & 3 deletions test/ruby/test_module.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3016,17 +3016,17 @@ def test_frozen_visibility
bug11532 = '[ruby-core:70828] [Bug #11532]'

c = Class.new {const_set(:A, 1)}.freeze
assert_raise_with_message(FrozenError, /frozen class/, bug11532) {
assert_raise_with_message(FrozenError, /frozen Class/, bug11532) {
c.class_eval {private_constant :A}
}

c = Class.new {const_set(:A, 1); private_constant :A}.freeze
assert_raise_with_message(FrozenError, /frozen class/, bug11532) {
assert_raise_with_message(FrozenError, /frozen Class/, bug11532) {
c.class_eval {public_constant :A}
}

c = Class.new {const_set(:A, 1)}.freeze
assert_raise_with_message(FrozenError, /frozen class/, bug11532) {
assert_raise_with_message(FrozenError, /frozen Class/, bug11532) {
c.class_eval {deprecate_constant :A}
}
end
Expand Down
23 changes: 23 additions & 0 deletions test/ruby/test_refinement.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1933,6 +1933,29 @@ module PublicCows
end;
end

def test_public_in_refine_for_method_in_superclass
assert_separately([], "#{<<-"begin;"}\n#{<<-"end;"}")
begin;
bug21446 = '[ruby-core:122558] [Bug #21446]'

class CowSuper
private
def moo() "Moo"; end
end
class Cow < CowSuper
end

module PublicCows
refine(Cow) {
public :moo
}
end

using PublicCows
assert_equal("Moo", Cow.new.moo, bug21446)
end;
end

module SuperToModule
class Parent
end
Expand Down
2 changes: 1 addition & 1 deletion test/ruby/test_zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2057,7 +2057,7 @@ def self.test = @@x = 42
end

def test_setclassvariable_raises
assert_compiles '"can\'t modify frozen #<Class:Foo>: Foo"', %q{
assert_compiles '"can\'t modify frozen Class: Foo"', %q{
class Foo
def self.test = @@x = 42
freeze
Expand Down
2 changes: 1 addition & 1 deletion thread_pthread.c
Original file line number Diff line number Diff line change
Expand Up @@ -1459,7 +1459,7 @@ rb_ractor_sched_barrier_start(rb_vm_t *vm, rb_ractor_t *cr)
vm->ractor.sync.lock_owner = cr;
}

// do not release ractor_sched_lock and threre is no newly added (resumed) thread
// do not release ractor_sched_lock and there is no newly added (resumed) thread
// thread_sched_setup_running_threads
}

Expand Down
26 changes: 20 additions & 6 deletions vm_args.c
Original file line number Diff line number Diff line change
Expand Up @@ -638,12 +638,26 @@ setup_parameters_complex(rb_execution_context_t * const ec, const rb_iseq_t * co
given_argc == ISEQ_BODY(iseq)->param.lead_num + (kw_flag ? 2 : 1) &&
!ISEQ_BODY(iseq)->param.flags.has_opt &&
!ISEQ_BODY(iseq)->param.flags.has_post &&
!ISEQ_BODY(iseq)->param.flags.ruby2_keywords &&
(!kw_flag ||
!ISEQ_BODY(iseq)->param.flags.has_kw ||
!ISEQ_BODY(iseq)->param.flags.has_kwrest ||
!ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg)) {
args->rest_dupped = true;
!ISEQ_BODY(iseq)->param.flags.ruby2_keywords) {
if (kw_flag) {
if (ISEQ_BODY(iseq)->param.flags.has_kw ||
ISEQ_BODY(iseq)->param.flags.has_kwrest) {
args->rest_dupped = true;
}
else if (kw_flag & VM_CALL_KW_SPLAT) {
VALUE kw_hash = locals[args->argc - 1];
if (kw_hash == Qnil ||
(RB_TYPE_P(kw_hash, T_HASH) && RHASH_EMPTY_P(kw_hash))) {
args->rest_dupped = true;
}
}

}
else if (!ISEQ_BODY(iseq)->param.flags.has_kw &&
!ISEQ_BODY(iseq)->param.flags.has_kwrest &&
!ISEQ_BODY(iseq)->param.flags.accepts_no_kwarg) {
args->rest_dupped = true;
}
}
}

Expand Down
6 changes: 6 additions & 0 deletions vm_method.c
Original file line number Diff line number Diff line change
Expand Up @@ -1281,6 +1281,7 @@ check_override_opt_method(VALUE klass, VALUE mid)
}
}

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)
*
Expand Down Expand Up @@ -1321,7 +1322,12 @@ 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);
me->def->body.refined.orig_me = search_method0(refined_class, mid, NULL, true);
}
}
if (type == VM_METHOD_TYPE_REFINED) {
rb_method_entry_t *old_me = lookup_method_table(RCLASS_ORIGIN(klass), mid);
Expand Down
1 change: 1 addition & 0 deletions zjit.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,7 @@ VALUE rb_zjit_stats(rb_execution_context_t *ec, VALUE self, VALUE target_key);
VALUE rb_zjit_reset_stats_bang(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_print_stats_p(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_get_stats_file_path_p(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_trace_exit_locations_enabled_p(rb_execution_context_t *ec, VALUE self);
VALUE rb_zjit_get_exit_locations(rb_execution_context_t *ec, VALUE self);

Expand Down
11 changes: 11 additions & 0 deletions zjit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ module RubyVM::ZJIT
# Blocks that are called when YJIT is enabled
@jit_hooks = []
# Avoid calling a Ruby method here to avoid interfering with compilation tests
if Primitive.rb_zjit_get_stats_file_path_p
at_exit { print_stats_file }
end
if Primitive.rb_zjit_print_stats_p
at_exit { print_stats }
end
Expand Down Expand Up @@ -333,6 +336,14 @@ def print_stats
$stderr.write stats_string
end

# Print ZJIT stats to file
def print_stats_file
filename = Primitive.rb_zjit_get_stats_file_path_p
File.open(filename, "wb") do |file|
file.write stats_string
end
end

def dump_locations # :nodoc:
return unless trace_exit_locations_enabled?

Expand Down
Loading