-
-
Notifications
You must be signed in to change notification settings - Fork 942
Description
Environment Information
Provide at least:
- JRuby version (
jruby -v) and command line (flags, JRUBY_OPTS, etc)
jruby 9.3.1.0 (2.6.8) 2021-10-13 2e01e7199d OpenJDK 64-Bit Server VM 25.302-b08 on 1.8.0_302-b08 +jit [linux-x86_64]
- Operating system and platform (e.g.
uname -a)
Linux [hostname removed] 5.4.134-73.228.amzn2int.x86_64 #1 SMP Wed Jul 21 17:54:58 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
Expected Behavior
Reproduction:
class CoolString < String
def initialize(foo)
@foo = foo
super(foo)
end
end
obj1 = CoolString.new('hello!')
evil_variable_eating_hash = {}
evil_variable_eating_hash[obj1] = 'oh no!'
obj2 = evil_variable_eating_hash.keys[0]
puts "Obj1: #{obj1.instance_variable_get(:@foo)}"
puts "Obj2: #{obj2.instance_variable_get(:@foo)}"
obj3 = JRuby.runtime.freezeAndDedupString(obj1)
puts "Obj1: #{obj1.instance_variable_get(:@foo)}"
puts "Obj3: #{obj3.instance_variable_get(:@foo)}"
Trying to upgrade an app from JRuby 9.2.16.0 to 9.3.0.0/9.3.0.1 and am seeing this bug when inserting a subclass of String into a Hash as a key. The instance variables on the String subclass are removed. This logic works fine in MRI Ruby 2.1, 2.3, 2.4, 2.5, and 2.7.
Working Example from MRI Ruby 2.7.4
irb(main):001:0> RUBY_VERSION
=> "2.7.4"
irb(main):002:0> RUBY_PLATFORM
=> "x86_64-linux"
irb(main):003:1* class CoolString < String
irb(main):004:2* def initialize(foo)
irb(main):005:2* @foo = foo
irb(main):006:2* super(foo)
irb(main):007:1* end
irb(main):008:0> end
=> :initialize
irb(main):009:0> obj1 = CoolString.new('hello!')
=> "hello!"
irb(main):010:0> evil_variable_eating_hash = {}
=> {}
irb(main):011:0> evil_variable_eating_hash[obj1] = 'oh no!'
=> "oh no!"
irb(main):012:0> obj2 = evil_variable_eating_hash.keys[0]
=> "hello!"
irb(main):013:0> puts "Obj1: #{obj1.instance_variable_get(:@foo)}"
Obj1: hello!
=> nil
irb(main):014:0> puts "Obj2: #{obj2.instance_variable_get(:@foo)}"
Obj2: hello!
You can see on line 13 and 14 that both obj1 and obj2 still have the @foo instance variable set.
Actual Behavior
The CoolString object, after being retrieved from Hash.keys, no longer has the @foo instance variable set.
irb(main):001:0> class CoolString < String
irb(main):002:1> def initialize(foo)
irb(main):003:2> @foo = foo
irb(main):004:2> super(foo)
irb(main):005:2> end
irb(main):006:1> end
=> :initialize
irb(main):007:0> obj1 = CoolString.new('hello!')
=> "hello!"
irb(main):008:0> evil_variable_eating_hash = {}
=> {}
irb(main):009:0> evil_variable_eating_hash[obj1] = 'oh no!'
=> "oh no!"
irb(main):010:0> obj2 = evil_variable_eating_hash.keys[0]
=> "hello!"
irb(main):011:0> puts "Obj1: #{obj1.instance_variable_get(:@foo)}"
Obj1: hello!
=> nil
irb(main):012:0> puts "Obj2: #{obj2.instance_variable_get(:@foo)}"
Obj2:
=> nil
irb(main):013:0> require 'jruby'
=> true
irb(main):014:0> obj3 = JRuby.runtime.freezeAndDedupString(obj1)
=> "hello!"
irb(main):015:0> puts "Obj1: #{obj1.instance_variable_get(:@foo)}"
Obj1: hello!
=> nil
irb(main):016:0> puts "Obj3: #{obj3.instance_variable_get(:@foo)}"
Obj3:
=> nil
You can see on line 11 and 12 that obj2 no longer has the @foo instance variable set. I traced the issue to JRuby.runtime.freezeAndDedupString which can be seen eating the instance variable in lines 14-16.