summaryrefslogtreecommitdiff
path: root/jni/ruby/test/-ext-/symbol
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/test/-ext-/symbol
Fresh start
Diffstat (limited to 'jni/ruby/test/-ext-/symbol')
-rw-r--r--jni/ruby/test/-ext-/symbol/test_inadvertent_creation.rb473
-rw-r--r--jni/ruby/test/-ext-/symbol/test_type.rb124
2 files changed, 597 insertions, 0 deletions
diff --git a/jni/ruby/test/-ext-/symbol/test_inadvertent_creation.rb b/jni/ruby/test/-ext-/symbol/test_inadvertent_creation.rb
new file mode 100644
index 0000000..f772086
--- /dev/null
+++ b/jni/ruby/test/-ext-/symbol/test_inadvertent_creation.rb
@@ -0,0 +1,473 @@
+require 'test/unit'
+require "-test-/symbol"
+
+module Test_Symbol
+ class TestInadvertent < Test::Unit::TestCase
+ def noninterned_name(prefix = "")
+ prefix += "_#{Thread.current.object_id.to_s(36).tr('-', '_')}"
+ begin
+ name = "#{prefix}_#{rand(0x1000).to_s(16)}_#{Time.now.usec}"
+ end while Bug::Symbol.find(name)
+ name
+ end
+
+ def setup
+ @obj = Object.new
+ end
+
+ def assert_not_pinneddown(name, msg = nil)
+ assert_not_send([Bug::Symbol, :pinneddown?, name], msg)
+ end
+
+ def assert_not_interned(name, msg = nil)
+ assert_not_send([Bug::Symbol, :find, name], msg)
+ end
+
+ def assert_not_interned_error(obj, meth, name, msg = nil, &block)
+ e = assert_raise(NameError, msg) {obj.__send__(meth, name, &block)}
+ if Symbol === name
+ assert_not_pinneddown(name, msg)
+ else
+ assert_not_interned(name, msg)
+ end
+ e
+ end
+
+ def assert_not_interned_false(obj, meth, name, msg = nil)
+ assert_not_send([obj, meth, name], msg)
+ if Symbol === name
+ assert_not_pinneddown(name, msg)
+ else
+ assert_not_interned(name, msg)
+ end
+ end
+
+ Feature5072 = '[ruby-core:38367]'
+
+ def test_module_const_get
+ cl = Class.new
+ name = noninterned_name("A")
+
+ assert_not_interned_error(cl, :const_get, name, Feature5072)
+
+ assert_not_interned_error(cl, :const_get, name.to_sym)
+ end
+
+ def test_module_const_defined?
+ cl = Class.new
+ name = noninterned_name("A")
+
+ assert_not_interned_false(cl, :const_defined?, name, Feature5072)
+
+ name = noninterned_name
+ assert_not_interned_error(cl, :const_defined?, name.to_sym)
+ end
+
+ def test_respond_to_missing
+ feature5072 = Feature5072
+ c = Class.new do
+ def self.respond_to_missing?(*)
+ super
+ end
+ end
+ s = noninterned_name
+
+ # assert_not_interned_false(c, :respond_to?, s, feature5072)
+ assert_not_interned_false(c, :method_defined?, s, feature5072)
+ assert_not_interned_false(c, :public_method_defined?, s, feature5072)
+ assert_not_interned_false(c, :private_method_defined?, s, feature5072)
+ assert_not_interned_false(c, :protected_method_defined?, s, feature5072)
+ assert_not_interned_false(c, :const_defined?, noninterned_name("A"), feature5072)
+ assert_not_interned_false(c, :instance_variable_defined?, noninterned_name("@"), feature5072)
+ assert_not_interned_false(c, :class_variable_defined?, noninterned_name("@@"), feature5072)
+ end
+
+ def test_missing_method
+ bug10985 = '[ruby-core:68564] [Bug #10985]'
+ m = nil
+ c = Class.new do
+ def self.respond_to_missing?(*)
+ true
+ end
+ end
+
+ s = noninterned_name
+ assert_nothing_raised(NameError, bug10985) {m = c.method(s)}
+ assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
+
+ s = noninterned_name
+ assert_nothing_raised(NameError, bug10985) {m = c.public_method(s.to_sym)}
+ assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
+
+ s = noninterned_name
+ assert_nothing_raised(NameError, bug10985) {m = c.singleton_method(s.to_sym)}
+ assert_raise_with_message(NoMethodError, /#{s}/) {m.call}
+ end
+
+ Feature5079 = '[ruby-core:38404]'
+
+ def test_undefined_instance_variable
+ feature5079 = feature5079
+ c = Class.new
+ iv = noninterned_name("@")
+
+ assert_not_interned_false(c, :instance_variable_get, iv, feature5079)
+ assert_not_interned_error(c, :remove_instance_variable, iv, feature5079)
+ end
+
+ def test_undefined_class_variable
+ feature5079 = feature5079
+ c = Class.new
+ cv = noninterned_name("@@")
+
+ assert_not_interned_error(c, :class_variable_get, cv, feature5079)
+ assert_not_interned_error(c, :remove_class_variable, cv, feature5079)
+ end
+
+
+ def test_undefined_const
+ feature5079 = feature5079
+ c = Class.new
+ s = noninterned_name("A")
+
+ assert_not_interned_error(c, :remove_const, s, feature5079)
+ end
+
+ def test_undefined_method
+ feature5079 = feature5079
+ c = Class.new
+ s = noninterned_name
+
+ assert_not_interned_error(c, :method, s, feature5079)
+ assert_not_interned_error(c, :public_method, s, feature5079)
+ assert_not_interned_error(c, :instance_method, s, feature5079)
+ assert_not_interned_error(c, :public_instance_method, s, feature5079)
+ end
+
+ Feature5089 = '[ruby-core:38447]'
+ def test_const_missing
+ feature5089 = Feature5089
+ c = Class.new do
+ def self.const_missing(const_name)
+ raise NameError, const_name.to_s
+ end
+ end
+ s = noninterned_name("A")
+
+ assert_not_interned_error(c, :const_get, s.to_sym, feature5089)
+ assert_not_interned_false(c, :autoload?, s.to_sym, feature5089)
+ end
+
+ def test_aliased_method
+ feature5089 = Feature5089
+ c = Class.new do
+ def self.alias_method(str)
+ super(:puts, str)
+ end
+ end
+ s = noninterned_name
+
+ assert_not_interned_error(c, :alias_method, s, feature5089)
+ assert_not_interned_error(c, :private_class_method, s, feature5089)
+ assert_not_interned_error(c, :private_constant, s, feature5089)
+ assert_not_interned_error(c, :private, s, feature5089)
+ assert_not_interned_error(c, :protected, s, feature5089)
+ assert_not_interned_error(c, :public, s, feature5089)
+ assert_not_interned_error(c, :public_class_method, s, feature5089)
+ assert_not_interned_error(c, :public_constant, s, feature5089)
+ assert_not_interned_error(c, :remove_method, s, feature5089)
+ assert_not_interned_error(c, :undef_method, s, feature5089)
+ assert_not_interned_error(c, :untrace_var, s, feature5089)
+ end
+
+ Feature5112 = '[ruby-core:38576]'
+
+ def test_public_send
+ name = noninterned_name
+ e = assert_raise(NoMethodError) {@obj.public_send(name, Feature5112)}
+ assert_not_interned(name)
+ assert_equal(name, e.name)
+ assert_equal([Feature5112], e.args)
+ end
+
+ def test_send
+ name = noninterned_name
+ e = assert_raise(NoMethodError) {@obj.send(name, Feature5112)}
+ assert_not_interned(name)
+ assert_equal(name, e.name)
+ assert_equal([Feature5112], e.args)
+ end
+
+ def test___send__
+ name = noninterned_name
+ e = assert_raise(NoMethodError) {@obj.__send__(name, Feature5112)}
+ assert_not_interned(name)
+ assert_equal(name, e.name)
+ assert_equal([Feature5112], e.args)
+ end
+
+ def test_thread_aref
+ Thread.current[:test] = nil
+ name = noninterned_name
+ assert_nil(Thread.current[name])
+ assert_not_interned(name)
+ end
+
+ def test_thread_key?
+ Thread.current[:test] = nil
+ name = noninterned_name
+ assert_not_send([Thread.current, :key?, name])
+ assert_not_interned(name)
+ end
+
+ def test_thread_variable_get
+ Thread.current.thread_variable_set(:test, nil)
+ name = noninterned_name
+ assert_nil(Thread.current.thread_variable_get(name))
+ assert_not_pinneddown(name)
+ end
+
+ def test_thread_variable_set
+ name = noninterned_name
+ Thread.current.thread_variable_set(name, 42)
+ assert_not_pinneddown(name)
+ end
+
+ def test_thread_variable?
+ Thread.current.thread_variable_set(:test, nil)
+ name = noninterned_name
+ assert_not_send([Thread.current, :thread_variable?, name])
+ assert_not_interned(name)
+ end
+
+ def test_enumerable_inject_op
+ name = noninterned_name
+ assert_raise(NoMethodError) {[1, 2].inject(name)}
+ assert_not_interned(name)
+ end
+
+ def test_module_const_set
+ name = noninterned_name
+ mod = Module.new
+ assert_raise(NameError) {mod.const_set(name, true)}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.const_set(name.to_sym, true)}
+ assert_not_pinneddown(name)
+ end
+
+ def test_module_cvar_set
+ name = noninterned_name
+ mod = Module.new
+ assert_raise(NameError) {mod.class_variable_set(name, true)}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.class_variable_set(name.to_sym, true)}
+ assert_not_pinneddown(name)
+ end
+
+ def test_object_ivar_set
+ name = noninterned_name
+ obj = Object.new
+ assert_raise(NameError) {obj.instance_variable_set(name, true)}
+ assert_not_interned(name)
+ assert_raise(NameError) {obj.instance_variable_set(name.to_sym, true)}
+ assert_not_pinneddown(name)
+ end
+
+ def test_struct_new
+ name = noninterned_name
+ assert_raise(NameError) {Struct.new(name)}
+ assert_not_interned(name)
+ end
+
+ def test_struct_aref
+ s = Struct.new(:foo).new
+ name = noninterned_name
+ assert_raise(NameError) {s[name]}
+ assert_not_interned(name)
+ end
+
+ def test_struct_aset
+ s = Struct.new(:foo).new
+ name = noninterned_name
+ assert_raise(NameError) {s[name] = true}
+ assert_not_interned(name)
+ end
+
+ def test_invalid_attr
+ name = noninterned_name("*")
+ mod = Module.new
+ assert_raise(NameError) {mod.module_eval {attr(name)}}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.module_eval {attr(name.to_sym)}}
+ assert_not_pinneddown(name)
+ end
+
+ def test_invalid_attr_reader
+ name = noninterned_name("*")
+ mod = Module.new
+ assert_raise(NameError) {mod.module_eval {attr_reader(name)}}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.module_eval {attr_reader(name.to_sym)}}
+ assert_not_pinneddown(name)
+ end
+
+ def test_invalid_attr_writer
+ name = noninterned_name("*")
+ mod = Module.new
+ assert_raise(NameError) {mod.module_eval {attr_writer(name)}}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.module_eval {attr_writer(name.to_sym)}}
+ assert_not_pinneddown(name)
+ end
+
+ def test_invalid_attr_accessor
+ name = noninterned_name("*")
+ mod = Module.new
+ assert_raise(NameError) {mod.module_eval {attr_accessor(name)}}
+ assert_not_interned(name)
+ assert_raise(NameError) {mod.module_eval {attr_accessor(name.to_sym)}}
+ assert_not_pinneddown(name)
+ end
+
+ def test_gc_attrset
+ assert_separately(['-r-test-/symbol', '-', '[ruby-core:62226] [Bug #9787]'], <<-'end;') # begin
+ bug = ARGV.shift
+ def noninterned_name(prefix = "")
+ prefix += "_#{Thread.current.object_id.to_s(36).tr('-', '_')}"
+ begin
+ name = "#{prefix}_#{rand(0x1000).to_s(16)}_#{Time.now.usec}"
+ end while Bug::Symbol.find(name) or Bug::Symbol.find(name + "=")
+ name
+ end
+ names = Array.new(1000) {noninterned_name("gc")}
+ names.each {|n| n.to_sym}
+ GC.start(immediate_sweep: false)
+ names.each do |n|
+ eval(":#{n}=")
+ assert_nothing_raised(TypeError, bug) {eval("proc{self.#{n} = nil}")}
+ end
+ end;
+ end
+
+ def test_execopt_key
+ name = noninterned_name.intern
+ assert_raise(ArgumentError) {
+ system(".", name => nil)
+ }
+ assert_not_pinneddown(name)
+ end
+
+ def test_execopt_redirect_value
+ name = noninterned_name.intern
+ assert_raise(ArgumentError) {
+ system(".", [] => name)
+ }
+ assert_not_pinneddown(name)
+ end
+
+ def test_execopt_redirect_path
+ name = noninterned_name.intern
+ assert_raise(TypeError) {
+ system(".", [] => [name, 0])
+ }
+ assert_not_pinneddown(name)
+ end
+
+ def test_execopt_redirect_symbol
+ name = noninterned_name.intern
+ assert_raise(ArgumentError) {
+ system(".", in: name)
+ }
+ assert_not_pinneddown(name)
+ end
+
+ def assert_no_immortal_symbol_created(name)
+ name = noninterned_name(name)
+ yield(name)
+ assert_not_pinneddown(name)
+ end
+
+ def assert_no_immortal_symbol_in_method_missing(name)
+ assert_no_immortal_symbol_created("send should not leak - #{name}") do |name|
+ assert_raise(NoMethodError) {yield(name)}
+ end
+ end
+
+ def test_send_leak_string
+ assert_no_immortal_symbol_in_method_missing("str") do |name|
+ 42.send(name)
+ end
+ end
+
+ def test_send_leak_symbol
+ assert_no_immortal_symbol_in_method_missing("sym") do |name|
+ 42.send(name.to_sym)
+ end
+ end
+
+ def test_send_leak_string_custom_method_missing
+ x = Object.new
+ def x.method_missing(*); super; end
+ assert_no_immortal_symbol_in_method_missing("str mm") do |name|
+ x.send(name)
+ end
+ end
+
+ def test_send_leak_symbol_custom_method_missing
+ x = Object.new
+ def x.method_missing(*); super; end
+ assert_no_immortal_symbol_in_method_missing("sym mm") do |name|
+ x.send(name.to_sym)
+ end
+ end
+
+ def test_send_leak_string_no_optimization
+ assert_no_immortal_symbol_in_method_missing("str slow") do |name|
+ 42.method(:send).call(name)
+ end
+ end
+
+ def test_send_leak_symbol_no_optimization
+ assert_no_immortal_symbol_in_method_missing("sym slow") do |name|
+ 42.method(:send).call(name.to_sym)
+ end
+ end
+
+ def test_send_leak_string_custom_method_missing_no_optimization
+ x = Object.new
+ def x.method_missing(*); super; end
+ assert_no_immortal_symbol_in_method_missing("str mm slow") do |name|
+ x.method(:send).call(name)
+ end
+ end
+
+ def test_send_leak_symbol_custom_method_missing_no_optimization
+ x = Object.new
+ def x.method_missing(*); super; end
+ assert_no_immortal_symbol_in_method_missing("sym mm slow") do |name|
+ x.method(:send).call(name.to_sym)
+ end
+ end
+
+ def test_kwarg_symbol_leak_no_rest
+ foo = -> (arg: 42) {}
+ assert_no_immortal_symbol_created("kwarg no rest") do |name|
+ assert_raise(ArgumentError) { foo.call(name.to_sym => 42) }
+ end
+ end
+
+ def test_kwarg_symbol_leak_with_rest
+ foo = -> (arg: 2, **options) {}
+ assert_no_immortal_symbol_created("kwarg with rest") do |name|
+ foo.call(name.to_sym => 42)
+ end
+ end
+
+ def test_kwarg_symbol_leak_just_rest
+ foo = -> (**options) {}
+ assert_no_immortal_symbol_created("kwarg just rest") do |name|
+ foo.call(name.to_sym => 42)
+ end
+ end
+ end
+end
diff --git a/jni/ruby/test/-ext-/symbol/test_type.rb b/jni/ruby/test/-ext-/symbol/test_type.rb
new file mode 100644
index 0000000..f1749f5
--- /dev/null
+++ b/jni/ruby/test/-ext-/symbol/test_type.rb
@@ -0,0 +1,124 @@
+require 'test/unit'
+require "-test-/symbol"
+
+module Test_Symbol
+ class TestType < Test::Unit::TestCase
+ def test_id2str_fstring_bug9171
+ fstr = eval("# encoding: us-ascii
+ 'foobar'.freeze")
+ assert_same fstr, Bug::Symbol.id2str(:foobar)
+
+ fstr = eval("# encoding: us-ascii
+ '>'.freeze")
+ assert_same fstr, Bug::Symbol.id2str(:>)
+ end
+
+ def assert_symtype(sym, pred, msg = nil)
+ assert_send([Bug::Symbol, pred, sym], msg)
+ end
+
+ def assert_not_symtype(sym, pred, msg = nil)
+ assert_not_send([Bug::Symbol, pred, sym], msg)
+ end
+
+ def test_const
+ assert_symtype("Foo", :const?)
+ assert_not_symtype("F!", :const?)
+ assert_not_symtype("foo", :const?)
+ assert_not_symtype("@foo", :const?)
+ assert_not_symtype("@@foo", :const?)
+ assert_not_symtype("$foo", :const?)
+ assert_not_symtype("foo=", :const?)
+ assert_not_symtype("[foo]", :const?)
+ assert_not_symtype("xFoo", :const?)
+ end
+
+ def test_local
+ assert_symtype("foo", :local?)
+ assert_symtype("fooBar", :local?)
+ assert_symtype("foo_bar", :local?)
+ assert_not_symtype("foo!", :local?)
+ assert_not_symtype("foo?", :local?)
+ assert_not_symtype("Foo", :local?)
+ assert_not_symtype("@foo", :local?)
+ assert_not_symtype("@@foo", :local?)
+ assert_not_symtype("$foo", :local?)
+ assert_not_symtype("foo=", :local?)
+ assert_not_symtype("[foo]", :local?)
+ end
+
+ def test_global
+ assert_symtype("$foo", :global?)
+ assert_symtype("$$", :global?)
+ assert_not_symtype("$()", :global?)
+ assert_not_symtype("$", :global?)
+ assert_not_symtype("foo", :global?)
+ assert_not_symtype("Foo", :global?)
+ assert_not_symtype("@foo", :global?)
+ assert_not_symtype("@@foo", :global?)
+ assert_not_symtype("foo=", :global?)
+ assert_not_symtype("[foo]", :global?)
+ end
+
+ def test_instance
+ assert_symtype("@foo", :instance?)
+ assert_not_symtype("@", :instance?)
+ assert_not_symtype("@1", :instance?)
+ assert_not_symtype("@@", :instance?)
+ assert_not_symtype("foo", :instance?)
+ assert_not_symtype("Foo", :instance?)
+ assert_not_symtype("@@foo", :instance?)
+ assert_not_symtype("$foo", :instance?)
+ assert_not_symtype("foo=", :instance?)
+ assert_not_symtype("[foo]", :instance?)
+ end
+
+ def test_class
+ assert_symtype("@@foo", :class?)
+ assert_not_symtype("@@", :class?)
+ assert_not_symtype("@", :class?)
+ assert_not_symtype("@@1", :class?)
+ assert_not_symtype("foo", :class?)
+ assert_not_symtype("Foo", :class?)
+ assert_not_symtype("@foo", :class?)
+ assert_not_symtype("$foo", :class?)
+ assert_not_symtype("foo=", :class?)
+ assert_not_symtype("[foo]", :class?)
+ end
+
+ def test_attrset
+ assert_symtype("foo=", :attrset?)
+ assert_symtype("Foo=", :attrset?)
+ assert_symtype("@foo=", :attrset?)
+ assert_symtype("@@foo=", :attrset?)
+ assert_symtype("$foo=", :attrset?)
+ assert_symtype("0=", :attrset?)
+ assert_symtype("@=", :attrset?)
+ assert_symtype("@@=", :attrset?)
+ assert_not_symtype("foo", :attrset?)
+ assert_not_symtype("Foo", :attrset?)
+ assert_not_symtype("@foo", :attrset?)
+ assert_not_symtype("@@foo", :attrset?)
+ assert_not_symtype("$foo", :attrset?)
+ assert_not_symtype("[foo]", :attrset?)
+ assert_symtype("[foo]=", :attrset?)
+ assert_equal(:"foo=", Bug::Symbol.attrset("foo"))
+ assert_symtype(Bug::Symbol.attrset("foo"), :attrset?)
+ assert_equal(:"Foo=", Bug::Symbol.attrset("Foo"))
+ assert_symtype(Bug::Symbol.attrset("Foo"), :attrset?)
+ assert_equal(:"@foo=", Bug::Symbol.attrset("@foo"))
+ assert_symtype(Bug::Symbol.attrset("@foo"), :attrset?)
+ assert_equal(:"@@foo=", Bug::Symbol.attrset("@@foo"))
+ assert_symtype(Bug::Symbol.attrset("@@foo"), :attrset?)
+ assert_equal(:"$foo=", Bug::Symbol.attrset("$foo"))
+ assert_symtype(Bug::Symbol.attrset("$foo"), :attrset?)
+ assert_equal(:"[foo]=", Bug::Symbol.attrset("[foo]"))
+ assert_symtype(Bug::Symbol.attrset("[foo]"), :attrset?)
+ assert_equal(:[]=, Bug::Symbol.attrset(:[]))
+ assert_symtype(Bug::Symbol.attrset("foo?="), :attrset?)
+ assert_equal(:"foo?=", Bug::Symbol.attrset(:foo?))
+ assert_symtype(Bug::Symbol.attrset("foo!="), :attrset?)
+ assert_equal(:"foo!=", Bug::Symbol.attrset(:foo!))
+ end
+ end
+end