Skip to content

Verify to_s is string or fall back on anyToString#8940

Merged
headius merged 2 commits intojruby:masterfrom
headius:as_string_type_guarding
Aug 12, 2025
Merged

Verify to_s is string or fall back on anyToString#8940
headius merged 2 commits intojruby:masterfrom
headius:as_string_type_guarding

Conversation

@headius
Copy link
Member

@headius headius commented Aug 5, 2025

Logic for "AsString" in a dynamic string must verify the result of calling to_s is a String, or else use anyToString on the original object. Previous logic here only called to_s and did not verify the result, proceeding to append non-string values like Integer or Float rather than falling back. The patch here adds an additional guard around the to_s call to perform this check and fallback.

This only affects the rare case where someone has implemented a to_s method that does not return a String, and is only observable once the dstring has been optimized and invoked twice.

As this is a bit invasive, I'm marking it for 10.0.3.0 so we can create more specs and do more verification that it works correctly before release.

@headius headius added this to the JRuby 10.0.3.0 milestone Aug 5, 2025
@headius
Copy link
Member Author

headius commented Aug 5, 2025

There's no spec specifically for this case, but without the patch it fails a hand-written example designed to lazily JIT:

$ jruby -Xjit.threshold=0 -e 'eval "1"; obj = Object.new; class << obj; def to_s; 42; end; private :to_s; end; 3.times { puts "#{obj}" }'
#<Object:0x640c216b>
42
42

The closest relevant spec is here, but it only ensures the result is a string:

it "uses an internal representation when #to_s doesn't return a String" do
obj = mock('to_s')
obj.stub!(:to_s).and_return(42)
# See rubyspec commit 787c132d by yugui. There is value in
# ensuring that this behavior works. So rather than removing
# this spec completely, the only thing that can be asserted
# is that if you interpolate an object that fails to return
# a String, you will still get a String and not raise an
# exception.
"#{obj}".should be_an_instance_of(String)
end

I will add a more specific spec that ensures non-string to_s results are not included in the dynamic string.

@headius
Copy link
Member Author

headius commented Aug 5, 2025

@eregon The existing spec for this hardly verified anything, so I replaced it with one that confirms a non-String to_s result is discarded when building a dynamic string. Let me know if you have thoughts.

@headius
Copy link
Member Author

headius commented Aug 5, 2025

@eregon A thought: perhaps better to !~ /42/ rather than match the hashy inspect output? I am generally reluctant to specify "not match" behaviors because they can be easily defeated by other bugs.

@eregon
Copy link
Member

eregon commented Aug 5, 2025

The changes to spec/ruby/language/string_spec.rb look fine. I agree, it's clearer and safer to test that it looks like a usual inspect rather than check something is not there (which would pass e.g. if empty).

headius added 2 commits August 7, 2025 11:54
Logic for "AsString" in a dynamic string must verify the result of
calling to_s is a String, or else use anyToString on the original
object. Previous logic here only called to_s and did not verify
the result, proceeding to append non-string values like Integer or
Float rather than falling back. The patch here adds an additional
guard around the to_s call to perform this check and fallback.

This only affects the rare case where someone has implemented a
to_s method that does not return a String, and is only observable
once the dstring has been optimized and invoked twice.
The old spec only verified that a dynamic string would be
successfully created when an interpolated object returned a
non-string value from to_s. This enhances the spec to also verify
that the non-string value will be discarded in favor of the generic
"hashy" inspect string.
@headius headius force-pushed the as_string_type_guarding branch from 74a1904 to 341b9f4 Compare August 7, 2025 08:55
@headius headius merged commit bb7e143 into jruby:master Aug 12, 2025
73 checks passed
@headius headius deleted the as_string_type_guarding branch August 12, 2025 16:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants