From fcbf63e62c627deae76c1b8cb8c0876c536ed811 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Mon, 16 Mar 2020 18:49:26 +0900 Subject: Fresh start --- .../test/-ext-/symbol/test_inadvertent_creation.rb | 473 +++++++++++++++++++++ jni/ruby/test/-ext-/symbol/test_type.rb | 124 ++++++ 2 files changed, 597 insertions(+) create mode 100644 jni/ruby/test/-ext-/symbol/test_inadvertent_creation.rb create mode 100644 jni/ruby/test/-ext-/symbol/test_type.rb (limited to 'jni/ruby/test/-ext-/symbol') 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 -- cgit v1.2.3