Skip to content

Commit 096e423

Browse files
committed
Adjust a FrozenError's message and add a receiver when a singleton method is defined on a frozen object
1 parent 0159751 commit 096e423

File tree

8 files changed

+59
-19
lines changed

8 files changed

+59
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Compatibility:
2727
* Fix Struct setters to raise `FrozenError` when a struct is frozen (#3850, @andrykonchin).
2828
* Fix `Struct#initialize` when mixed positional and keyword arguments (#3855, @andrykonchin).
2929
* Implement `rb_error_frozen_object` for the google-protobuf gem (@nirvdrum).
30+
* Adjust a `FrozenError`'s message and add a receiver when a frozen module or class is modified (e.g. by defining or undefining an instance method or by defining a nested module (@andrykonchin).
3031

3132
Performance:
3233

lib/truffle/truffle/cext.rb

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2375,7 +2375,16 @@ def rb_eval_cmd_kw(cmd, args, kw_splat)
23752375
end
23762376

23772377
def rb_error_frozen_object(object)
2378-
raise FrozenError.new("can't modify frozen #{Primitive.class(object)}", receiver: object)
2378+
string = nil
2379+
recursion = Truffle::ThreadOperations.detect_recursion object do
2380+
string = object.inspect
2381+
end
2382+
2383+
if recursion
2384+
string = ' ...'
2385+
end
2386+
2387+
raise FrozenError.new("can't modify frozen #{Primitive.class(object)}: #{string}", receiver: object)
23792388
end
23802389

23812390
def rb_tr_warn(message)

spec/ruby/core/exception/frozen_error_spec.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ def o.x; end
2121
end
2222
end
2323

24+
describe "FrozenError#message" do
25+
it "includes a receiver" do
26+
object = Object.new
27+
object.freeze
28+
29+
-> {
30+
def object.x; end
31+
}.should raise_error(FrozenError, "can't modify frozen object: #{object.to_s}")
32+
33+
object = [].freeze
34+
-> { object << nil }.should raise_error(FrozenError, "can't modify frozen Array: []")
35+
end
36+
end
37+
2438
describe "Modifying a frozen object" do
2539
context "#inspect is redefined and modifies the object" do
2640
it "returns ... instead of String representation of object" do

spec/ruby/language/def_spec.rb

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def foo(a); end
9797
def foo; end
9898
end
9999
}.should raise_error(FrozenError) { |e|
100-
e.message.should.start_with? "can't modify frozen module"
100+
e.message.should == "can't modify frozen module: #{e.receiver.to_s}"
101101
}
102102

103103
-> {
@@ -106,7 +106,7 @@ def foo; end
106106
def foo; end
107107
end
108108
}.should raise_error(FrozenError){ |e|
109-
e.message.should.start_with? "can't modify frozen class"
109+
e.message.should == "can't modify frozen class: #{e.receiver.to_s}"
110110
}
111111
end
112112
end
@@ -283,20 +283,20 @@ def obj.==(other)
283283
it "raises FrozenError with the correct class name" do
284284
obj = Object.new
285285
obj.freeze
286-
-> { def obj.foo; end }.should raise_error(FrozenError){ |e|
287-
e.message.should.start_with? "can't modify frozen object"
288-
}
286+
-> { def obj.foo; end }.should raise_error(FrozenError, "can't modify frozen object: #{obj.to_s}")
289287

288+
obj = Object.new
290289
c = obj.singleton_class
291-
-> { def c.foo; end }.should raise_error(FrozenError){ |e|
292-
e.message.should.start_with? "can't modify frozen Class"
293-
}
290+
c.singleton_class.freeze
291+
-> { def c.foo; end }.should raise_error(FrozenError, "can't modify frozen Class: #{c.to_s}")
292+
293+
c = Class.new
294+
c.freeze
295+
-> { def c.foo; end }.should raise_error(FrozenError, "can't modify frozen Class: #{c.to_s}")
294296

295297
m = Module.new
296298
m.freeze
297-
-> { def m.foo; end }.should raise_error(FrozenError){ |e|
298-
e.message.should.start_with? "can't modify frozen Module"
299-
}
299+
-> { def m.foo; end }.should raise_error(FrozenError, "can't modify frozen Module: #{m.to_s}")
300300
end
301301
end
302302

spec/ruby/optional/capi/exception_spec.rb

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,19 @@
104104
it "raises a FrozenError regardless of the object's frozen state" do
105105
# The type of the argument we supply doesn't matter. The choice here is arbitrary and we only change the type
106106
# of the argument to ensure the exception messages are set correctly.
107-
-> { @s.rb_error_frozen_object(Hash.new) }.should raise_error(FrozenError, "can't modify frozen Hash")
108-
-> { @s.rb_error_frozen_object(Array.new.freeze) }.should raise_error(FrozenError, "can't modify frozen Array")
107+
-> { @s.rb_error_frozen_object(Array.new) }.should raise_error(FrozenError, "can't modify frozen Array: []")
108+
-> { @s.rb_error_frozen_object(Array.new.freeze) }.should raise_error(FrozenError, "can't modify frozen Array: []")
109+
end
110+
111+
it "properly handles recursive rb_error_frozen_object calls" do
112+
klass = Class.new(Object)
113+
object = klass.new
114+
s = @s
115+
klass.define_method :inspect do
116+
s.rb_error_frozen_object(object)
117+
end
118+
119+
-> { @s.rb_error_frozen_object(object) }.should raise_error(FrozenError, "can't modify frozen #{klass}: ...")
109120
end
110121
end
111122

spec/tags/language/def_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/main/java/org/truffleruby/core/exception/CoreExceptions.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,8 +304,14 @@ public RubyException frozenError(Object object, Node currentNode) {
304304
object);
305305
}
306306

307+
public RubyException frozenError(Object object, String name, Node currentNode) {
308+
String string = inspectFrozenObject(object);
309+
return frozenError(StringUtils.format("can't modify frozen %s: %s", name, string), currentNode,
310+
object);
311+
}
312+
307313
@TruffleBoundary
308-
public RubyException frozenError(String message, Node currentNode, Object receiver) {
314+
private RubyException frozenError(String message, Node currentNode, Object receiver) {
309315
RubyClass exceptionClass = context.getCoreLibrary().frozenErrorClass;
310316
RubyString errorMessage = StringOperations.createUTF8String(context, language, message);
311317
final Backtrace backtrace = context.getCallStack().getBacktrace(currentNode);

src/main/java/org/truffleruby/core/module/ModuleFields.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,9 @@ public void checkFrozen(RubyContext context, Node currentNode) {
290290
throw new RaiseException(
291291
context,
292292
context.getCoreExceptions().frozenError(
293-
StringUtils.format("can't modify frozen %s", name),
294-
currentNode,
295-
receiver));
293+
receiver,
294+
name,
295+
currentNode));
296296
}
297297
}
298298

0 commit comments

Comments
 (0)