Skip to content

Commit 00307c2

Browse files
committed
[GR-18163] Fix recursive raising FrozenError
PullRequest: truffleruby/4118
2 parents 1f80340 + 8df94ac commit 00307c2

File tree

4 files changed

+37
-1
lines changed

4 files changed

+37
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Bug fixes:
1717
* The arguments of `Thread.new(*args, &block)` need to be marked as shared between multiple threads (#3179, @eregon).
1818
* Fix `Range#bsearch` and raise `TypeError` when range boundaries are non-numeric and block not passed (@andrykonchin).
1919
* Fix using the `--cpusampler` profiler when there are custom unblock functions for `rb_thread_call_without_gvl()` (#3013, @eregon).
20+
* Fix recursive raising `FrozenError` exception when redefined `#inspect` modifies an object (#3388, @andrykonchin).
2021

2122
Compatibility:
2223

spec/ruby/core/exception/frozen_error_spec.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,19 @@ def o.x; end
2020
end
2121
end
2222
end
23+
24+
describe "Modifying a frozen object" do
25+
context "#inspect is redefined and modifies the object" do
26+
it "returns ... instead of String representation of object" do
27+
object = Object.new
28+
def object.inspect; @a = 1 end
29+
def object.modify; @a = 2 end
30+
31+
object.freeze
32+
33+
# CRuby's message contains multiple whitespaces before '...'.
34+
# So handle both multiple and single whitespace.
35+
-> { object.modify }.should raise_error(FrozenError, /can't modify frozen .*?: \s*.../)
36+
end
37+
end
38+
end

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ public String inspectReceiver(Object receiver) {
118118
return RubyGuards.getJavaString(rubyString);
119119
}
120120

121+
@TruffleBoundary
122+
public String inspectFrozenObject(Object object) {
123+
Object rubyString = DispatchNode.getUncached().call(
124+
context.getCoreLibrary().truffleExceptionOperationsModule, "inspect_frozen_object", object);
125+
return RubyGuards.getJavaString(rubyString);
126+
}
127+
121128
// ArgumentError
122129

123130
public RubyException argumentErrorOneHashRequired(RubyBaseNode currentNode) {
@@ -295,7 +302,8 @@ public RubyException argumentErrorCantUnfreeze(Object self, Node currentNode) {
295302
@TruffleBoundary
296303
public RubyException frozenError(Object object, Node currentNode) {
297304
String className = LogicalClassNode.getUncached().execute(object).fields.getName();
298-
return frozenError(StringUtils.format("can't modify frozen %s: %s", className, inspect(object)), currentNode,
305+
String string = inspectFrozenObject(object);
306+
return frozenError(StringUtils.format("can't modify frozen %s: %s", className, string), currentNode,
299307
object);
300308
}
301309

src/main/ruby/truffleruby/core/truffle/exception_operations.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,17 @@ def self.receiver_string(receiver)
9191
end
9292
end
9393

94+
# MRI: inspect_frozen_obj
95+
def self.inspect_frozen_object(object)
96+
string = nil
97+
98+
return '...' if Truffle::ThreadOperations.detect_recursion object do
99+
string = Truffle::Type.rb_inspect(object)
100+
end
101+
102+
string
103+
end
104+
94105
# default implementation of Exception#detailed_message hook
95106
def self.detailed_message(exception, highlight)
96107
message = StringValue exception.message.to_s

0 commit comments

Comments
 (0)