Skip to content

Using a string that has been mutated seems to result in a variable containing an unexpected value #8861

@azimux

Description

@azimux

Environment Information

  • jruby 10.0.0.1 (3.4.2) 2025-05-07 79cf1e4 Java HotSpot(TM) 64-Bit Server VM 24.0.1+9-30 on 24.0.1+9-30 +indy +jit [x86_64-linux]
  • This problem ONLY occurs if I run: JRUBY_OPTS="--debug" rspec
  • Linux6.12.27-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.27-1 (2025-05-06) x86_64 GNU/Linux

Expected Behavior

This one is hard to explain, but I'll try!

I have a piece of code that looks like this:

module_symbol = Util.classify(type_symbol).to_sym

and if I use JRUBY_OPTS="--debug" it winds up with a surprising value.

If I binding.pry at the right spot, the problem can be demonstrated like this:

[1] pry(Foobara::BuiltinTypes)> type_symbol
=> :attributes
[2] pry(Foobara::BuiltinTypes)> Util.classify(type_symbol)
/home/miles/.rbenv/versions/jruby-10.0.0.1/lib/ruby/gems/shared/gems/foobara-util-0.0.12/lib/foobara/util/string.rb:22: warning: literal string will be frozen in the future, the string was created here: /home/miles/.rbenv/versions/jruby-10.0.0.1/lib/ruby/gems/shared/gems/foobara-util-0.0.12/lib/foobara/util/string.rb:16
=> "stringintegerstringno_cast_needed_if_is_a_big_decimalintegerstringintegerstringno_cast_needed_if_is_a_stringnumericsymboldowncasematchesmax_lengthnumericsymbolno_cast_needed_if_is_a_datestringhashstringhashno_cast_needed_if_is_a_timeseconds_since_epochstringhashdateseconds_since_epochstringhashdateno_cast_needed_if_is_a_false_class_or_true_classnumericstring_or_symbolnumericstring_or_symboldowncasemust_have_an_at_signfirst_part_has_bad_characterssecond_part_has_bad_charactersfirst_part_cannot_start_with_or_end_with_a_dot_or_have_two_dots_in_a_rowsecond_part_cannot_be_emptydomain_cannot_start_with_or_end_with_a_hyphencannot_exceed64_characterscannot_have_multiple_at_signsfirst_part_cannot_be_emptydowncasemust_have_an_at_signfirst_part_has_bad_characterssecond_part_has_bad_charactersfirst_part_cannot_start_with_or_end_with_a_dot_or_have_two_dots_in_a_rowsecond_part_cannot_be_emptydomain_cannot_start_with_or_end_with_a_hyphencannot_exceed64_characterscannot_have_multiple_at_signsfirst_part_cannot_be_emptyno_cast_needed_if_is_a_arrayarrayablesizeelement_type_declarationarrayableelement_type_declarationsno_cast_needed_if_is_a_hasharraykey_type_declarationvalue_type_declarationarrayAttributesAttributes"                                            

So type_symbol is :attributes and so the expected output of this utility function is "Attributes" but instead it looks like some unexpected value based on some object somewhere else in memory.

Note that warning about frozen strings. That's a clue of how to work around this problem.

Here's the .classify method, which works fine without JRUBY_OPTS="--debug":

    def classify(string)
      camelize(string, true)
    end

    def camelize(string, upcase_first = false)
      return nil if string.nil?

      if string.is_a?(::Symbol)
        string = string.to_s
      end

      retval = ""

      string.each_char do |char|
        if ["_", "-"].include?(char)
          upcase_first = true
        elsif upcase_first
          retval << char.upcase
          upcase_first = false
        else
          retval << char.downcase
        end
      end

      retval
    end

Notice that line retval = "". If I change this to: retval = +"" then the problem goes away.

If really needing to reproduce this, it's in the jruby branch of https://github.com/foobara/foobara It's kind of a lot of steps but here's what I would try to do to reproduce it from scratch on a different machine:

git clone git@github.com:foobara/foobara
cd foobara
git checkout jruby
bundle
JRUBY_OPTS="--debug" rspec

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions