diff options
Diffstat (limited to 'test/ruby/test_method.rb')
| -rw-r--r-- | test/ruby/test_method.rb | 1629 |
1 files changed, 1604 insertions, 25 deletions
diff --git a/test/ruby/test_method.rb b/test/ruby/test_method.rb index c60f7fa8a8..8561f841a8 100644 --- a/test/ruby/test_method.rb +++ b/test/ruby/test_method.rb @@ -1,10 +1,10 @@ +# -*- coding: us-ascii -*- +# frozen_string_literal: false require 'test/unit' -require_relative 'envutil' class TestMethod < Test::Unit::TestCase def setup @verbose = $VERBOSE - $VERBOSE = nil end def teardown @@ -21,7 +21,18 @@ class TestMethod < Test::Unit::TestCase def mo5(a, *b, c) end def mo6(a, *b, c, &d) end def mo7(a, b = nil, *c, d, &e) end - def ma1((a), &b) end + def mo8(a, b = nil, *, d, &e) end + def ma1((a), &b) nil && a end + def mk1(**) end + def mk2(**o) nil && o end + def mk3(a, **o) nil && o end + def mk4(a = nil, **o) nil && o end + def mk5(a, b = nil, **o) nil && o end + def mk6(a, b = nil, c, **o) nil && o end + def mk7(a, b = nil, *c, d, **o) nil && o end + def mk8(a, b = nil, *c, d, e:, f: nil, **o) nil && o end + def mnk(**nil) end + def mf(...) end class Base def foo() :base end @@ -31,12 +42,30 @@ class TestMethod < Test::Unit::TestCase end class T def initialize; end + def initialize_copy(*) super end + def initialize_clone(*) super end + def initialize_dup(*) super end + def respond_to_missing?(*) super end def normal_method; end end module M def func; end module_function :func - def meth; end + def meth; :meth end + end + + def mv1() end + def mv2() end + private :mv2 + def mv3() end + protected :mv3 + + class Visibility + def mv1() end + def mv2() end + private :mv2 + def mv3() end + protected :mv3 end def test_arity @@ -49,6 +78,13 @@ class TestMethod < Test::Unit::TestCase assert_equal(-2, method(:mo4).arity) assert_equal(-3, method(:mo5).arity) assert_equal(-3, method(:mo6).arity) + assert_equal(-1, method(:mk1).arity) + assert_equal(-1, method(:mk2).arity) + assert_equal(-2, method(:mk3).arity) + assert_equal(-1, method(:mk4).arity) + assert_equal(-2, method(:mk5).arity) + assert_equal(-3, method(:mk6).arity) + assert_equal(-3, method(:mk7).arity) end def test_arity_special @@ -67,6 +103,12 @@ class TestMethod < Test::Unit::TestCase assert_raise(TypeError) do um.bind(Base.new) end + + # cleanup + Derived.class_eval do + remove_method :foo + def foo() :derived; end + end end def test_callee @@ -76,12 +118,59 @@ class TestMethod < Test::Unit::TestCase assert_equal(:m, Class.new {define_method(:m) {__method__}}.new.m) assert_equal(:m, Class.new {define_method(:m) {tap{return __method__}}}.new.m) assert_nil(eval("class TestCallee; __method__; end")) + + assert_equal(:test_callee, __callee__) + [ + ["method", Class.new {def m; __callee__; end},], + ["block", Class.new {def m; tap{return __callee__}; end},], + ["define_method", Class.new {define_method(:m) {__callee__}}], + ["define_method block", Class.new {define_method(:m) {tap{return __callee__}}}], + ].each do |mesg, c| + c.class_eval {alias m2 m} + o = c.new + assert_equal(:m, o.m, mesg) + assert_equal(:m2, o.m2, mesg) + end + assert_nil(eval("class TestCallee; __callee__; end")) + end + + def test_orphan_callee + c = Class.new{def foo; proc{__callee__}; end; alias alias_foo foo} + assert_equal(:alias_foo, c.new.alias_foo.call, '[Bug #11046]') + end + + def test_method_in_define_method_block + bug4606 = '[ruby-core:35386]' + c = Class.new do + [:m1, :m2].each do |m| + define_method(m) do + __method__ + end + end + end + assert_equal(:m1, c.new.m1, bug4606) + assert_equal(:m2, c.new.m2, bug4606) + end + + def test_method_in_block_in_define_method_block + bug4606 = '[ruby-core:35386]' + c = Class.new do + [:m1, :m2].each do |m| + define_method(m) do + tap { return __method__ } + end + end + end + assert_equal(:m1, c.new.m1, bug4606) + assert_equal(:m2, c.new.m2, bug4606) end def test_body o = Object.new def o.foo; end assert_nothing_raised { RubyVM::InstructionSequence.disasm(o.method(:foo)) } + assert_nothing_raised { RubyVM::InstructionSequence.disasm("x".method(:upcase)) } + assert_nothing_raised { RubyVM::InstructionSequence.disasm(method(:to_s).to_proc) } end def test_new @@ -116,6 +205,49 @@ class TestMethod < Test::Unit::TestCase o = Object.new def o.foo; end assert_kind_of(Integer, o.method(:foo).hash) + assert_equal(Array.instance_method(:map).hash, Array.instance_method(:collect).hash) + assert_kind_of(String, o.method(:foo).hash.to_s) + end + + def test_hash_does_not_change_after_compaction + omit "compaction is not supported on this platform" unless GC.respond_to?(:compact) + + # iseq backed method + assert_separately([], <<~RUBY) + def a; end + + # Need this method here because otherwise the iseq may be on the C stack + # which would get pinned and not move during compaction + def get_hash + method(:a).hash + end + + hash = get_hash + + GC.verify_compaction_references(expand_heap: true, toward: :empty) + + assert_equal(hash, get_hash) + RUBY + end + + def test_owner + c = Class.new do + def foo; end + end + assert_equal(c, c.instance_method(:foo).owner) + c2 = Class.new(c) + assert_equal(c, c2.instance_method(:foo).owner) + end + + def test_owner_missing + c = Class.new do + def respond_to_missing?(name, bool) + name == :foo + end + end + c2 = Class.new(c) + assert_equal(c, c.new.method(:foo).owner) + assert_equal(c2, c2.new.method(:foo).owner) end def test_receiver_name_owner @@ -127,6 +259,12 @@ class TestMethod < Test::Unit::TestCase assert_equal(class << o; self; end, m.owner) assert_equal(:foo, m.unbind.name) assert_equal(class << o; self; end, m.unbind.owner) + class << o + alias bar foo + end + m = o.method(:bar) + assert_equal(:bar, m.name) + assert_equal(:foo, m.original_name) end def test_instance_method @@ -144,6 +282,19 @@ class TestMethod < Test::Unit::TestCase def o.bar; end m = o.method(:bar).unbind assert_raise(TypeError) { m.bind(Object.new) } + + cx = EnvUtil.labeled_class("X\u{1f431}") + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /X\u{1f431}/) do + o.method(cx) + end + end + end + + def test_bind_module_instance_method + feature4254 = '[ruby-core:34267]' + m = M.instance_method(:meth) + assert_equal(:meth, m.bind(Object.new).call, feature4254) end def test_define_method @@ -167,19 +318,155 @@ class TestMethod < Test::Unit::TestCase Class.new.class_eval { define_method(:bar, o.method(:bar)) } end + cx = EnvUtil.labeled_class("X\u{1f431}") + EnvUtil.with_default_internal(Encoding::UTF_8) do + assert_raise_with_message(TypeError, /X\u{1F431}/) do + Class.new {define_method(cx) {}} + end + end + end + + def test_define_method_no_proc o = Object.new def o.foo(c) c.class_eval { define_method(:foo) } end c = Class.new - o.foo(c) { :foo } - assert_equal(:foo, c.new.foo) + assert_raise(ArgumentError) {o.foo(c)} + bug11283 = '[ruby-core:69655] [Bug #11283]' + assert_raise(ArgumentError, bug11283) {o.foo(c) {:foo}} + end + + def test_define_singleton_method o = Object.new o.instance_eval { define_singleton_method(:foo) { :foo } } assert_equal(:foo, o.foo) end + PUBLIC_SINGLETON_TEST = Object.new + class << PUBLIC_SINGLETON_TEST + private + PUBLIC_SINGLETON_TEST.define_singleton_method(:dsm){} + def PUBLIC_SINGLETON_TEST.def; end + end + def test_define_singleton_method_public + assert_nil(PUBLIC_SINGLETON_TEST.dsm) + assert_nil(PUBLIC_SINGLETON_TEST.def) + end + + def test_define_singleton_method_no_proc + o = Object.new + assert_raise(ArgumentError) { + o.instance_eval { define_singleton_method(:bar) } + } + + bug11283 = '[ruby-core:69655] [Bug #11283]' + def o.define(n) + define_singleton_method(n) + end + assert_raise(ArgumentError, bug11283) {o.define(:bar) {:bar}} + end + + def test_define_method_invalid_arg + assert_raise(TypeError) do + Class.new.class_eval { define_method(:foo, Object.new) } + end + + assert_raise(TypeError) do + Module.new.module_eval {define_method(:foo, Base.instance_method(:foo))} + end + end + + def test_define_singleton_method_with_extended_method + bug8686 = "[ruby-core:56174]" + + m = Module.new do + extend self + + def a + "a" + end + end + + assert_nothing_raised(bug8686) do + m.define_singleton_method(:a, m.method(:a)) + end + end + + def test_define_method_transplating + feature4254 = '[ruby-core:34267]' + m = Module.new {define_method(:meth, M.instance_method(:meth))} + assert_equal(:meth, Object.new.extend(m).meth, feature4254) + c = Class.new {define_method(:meth, M.instance_method(:meth))} + assert_equal(:meth, c.new.meth, feature4254) + end + + def test_define_method_visibility + c = Class.new do + public + define_method(:foo) {:foo} + protected + define_method(:bar) {:bar} + private + define_method(:baz) {:baz} + end + + assert_equal(true, c.public_method_defined?(:foo)) + assert_equal(false, c.public_method_defined?(:bar)) + assert_equal(false, c.public_method_defined?(:baz)) + + assert_equal(false, c.protected_method_defined?(:foo)) + assert_equal(true, c.protected_method_defined?(:bar)) + assert_equal(false, c.protected_method_defined?(:baz)) + + assert_equal(false, c.private_method_defined?(:foo)) + assert_equal(false, c.private_method_defined?(:bar)) + assert_equal(true, c.private_method_defined?(:baz)) + + m = Module.new do + module_function + define_method(:foo) {:foo} + end + assert_equal(true, m.respond_to?(:foo)) + assert_equal(false, m.public_method_defined?(:foo)) + assert_equal(false, m.protected_method_defined?(:foo)) + assert_equal(true, m.private_method_defined?(:foo)) + end + + def test_define_method_in_private_scope + bug9005 = '[ruby-core:57747] [Bug #9005]' + c = Class.new + class << c + public :define_method + end + TOPLEVEL_BINDING.eval("proc{|c|c.define_method(:x) {|x|throw x}}").call(c) + o = c.new + assert_throw(bug9005) {o.x(bug9005)} + end + + def test_singleton_define_method_in_private_scope + bug9141 = '[ruby-core:58497] [Bug #9141]' + o = Object.new + class << o + public :define_singleton_method + end + TOPLEVEL_BINDING.eval("proc{|o|o.define_singleton_method(:x) {|x|throw x}}").call(o) + assert_throw(bug9141) do + o.x(bug9141) + end + end + + def test_super_in_proc_from_define_method + c1 = Class.new { + def m + :m1 + end + } + c2 = Class.new(c1) { define_method(:m) { Proc.new { super() } } } + assert_equal(:m1, c2.new.m.call, 'see [Bug #4881] and [Bug #3136]') + end + def test_clone o = Object.new def o.foo; :foo; end @@ -189,40 +476,70 @@ class TestMethod < Test::Unit::TestCase assert_equal(:bar, m.clone.bar) end - def test_call - o = Object.new - def o.foo; p 1; end - def o.bar(x); x; end - m = o.method(:foo) - m.taint - assert_raise(SecurityError) { m.call } + def test_clone_under_gc_compact_stress + omit "compaction doesn't work well on s390x" if RUBY_PLATFORM =~ /s390x/ # https://github.com/ruby/ruby/pull/5077 + EnvUtil.under_gc_compact_stress do + o = Object.new + def o.foo; :foo; end + m = o.method(:foo) + def m.bar; :bar; end + assert_equal(:foo, m.clone.call) + assert_equal(:bar, m.clone.bar) + end end def test_inspect o = Object.new - def o.foo; end + def o.foo; end; line_no = __LINE__ m = o.method(:foo) - assert_equal("#<Method: #{ o.inspect }.foo>", m.inspect) + assert_equal("#<Method: #{ o.inspect }.foo() #{__FILE__}:#{line_no}>", m.inspect) m = o.method(:foo) - assert_equal("#<UnboundMethod: #{ class << o; self; end.inspect }#foo>", m.unbind.inspect) + assert_match("#<UnboundMethod: #{ class << o; self; end.inspect }#foo() #{__FILE__}:#{line_no}", m.unbind.inspect) c = Class.new - c.class_eval { def foo; end; } + c.class_eval { def foo; end; }; line_no = __LINE__ m = c.new.method(:foo) - assert_equal("#<Method: #{ c.inspect }#foo>", m.inspect) + assert_equal("#<Method: #{ c.inspect }#foo() #{__FILE__}:#{line_no}>", m.inspect) m = c.instance_method(:foo) - assert_equal("#<UnboundMethod: #{ c.inspect }#foo>", m.inspect) + assert_equal("#<UnboundMethod: #{ c.inspect }#foo() #{__FILE__}:#{line_no}>", m.inspect) c2 = Class.new(c) c2.class_eval { private :foo } m2 = c2.new.method(:foo) - assert_equal("#<Method: #{ c2.inspect }(#{ c.inspect })#foo>", m2.inspect) + assert_equal("#<Method: #{ c2.inspect }(#{ c.inspect })#foo() #{__FILE__}:#{line_no}>", m2.inspect) + + bug7806 = '[ruby-core:52048] [Bug #7806]' + c3 = Class.new(c) + c3.class_eval { alias bar foo } + m3 = c3.new.method(:bar) + assert_equal("#<Method: #{c3.inspect}(#{c.inspect})#bar(foo)() #{__FILE__}:#{line_no}>", m3.inspect, bug7806) + + bug15608 = '[ruby-core:91570] [Bug #15608]' + c4 = Class.new(c) + c4.class_eval { alias bar foo } + o = c4.new + o.singleton_class + m4 = o.method(:bar) + assert_equal("#<Method: #{c4.inspect}(#{c.inspect})#bar(foo)() #{__FILE__}:#{line_no}>", m4.inspect, bug15608) + + bug17428 = '[ruby-core:101635] [Bug #17428]' + assert_equal("#<Method: #<Class:String>(Module)#prepend(*)>", String.method(:prepend).inspect, bug17428) + + c5 = Class.new(String) + m = Module.new{def prepend; end; alias prep prepend}; line_no = __LINE__ + c5.extend(m) + c6 = Class.new(c5) + assert_equal("#<Method: #<Class:#{c6.inspect}>(#{m.inspect})#prep(prepend)() #{__FILE__}:#{line_no}>", c6.method(:prep).inspect, bug17428) end def test_callee_top_level assert_in_out_err([], "p __callee__", %w(nil), []) end + def test_caller_top_level + assert_in_out_err([], "p caller", %w([]), []) + end + def test_caller_negative_level assert_raise(ArgumentError) { caller(-1) } end @@ -238,10 +555,32 @@ class TestMethod < Test::Unit::TestCase end def test_default_accessibility - assert T.public_instance_methods.include?(:normal_method), 'normal methods are public by default' - assert !T.public_instance_methods.include?(:initialize), '#initialize is private' - assert !M.public_instance_methods.include?(:func), 'module methods are private by default' - assert M.public_instance_methods.include?(:meth), 'normal methods are public by default' + tmethods = T.public_instance_methods + assert_include tmethods, :normal_method, 'normal methods are public by default' + assert_not_include tmethods, :initialize, '#initialize is private' + assert_not_include tmethods, :initialize_copy, '#initialize_copy is private' + assert_not_include tmethods, :initialize_clone, '#initialize_clone is private' + assert_not_include tmethods, :initialize_dup, '#initialize_dup is private' + assert_not_include tmethods, :respond_to_missing?, '#respond_to_missing? is private' + mmethods = M.public_instance_methods + assert_not_include mmethods, :func, 'module methods are private by default' + assert_include mmethods, :meth, 'normal methods are public by default' + end + + def test_respond_to_missing_argument + obj = Struct.new(:mid).new + def obj.respond_to_missing?(id, *) + self.mid = id + true + end + assert_kind_of(Method, obj.method("bug15640")) + assert_kind_of(Symbol, obj.mid) + assert_equal("bug15640", obj.mid.to_s) + + arg = Struct.new(:to_str).new("bug15640_2") + assert_kind_of(Method, obj.method(arg)) + assert_kind_of(Symbol, obj.mid) + assert_equal("bug15640_2", obj.mid.to_s) end define_method(:pm0) {||} @@ -254,7 +593,16 @@ class TestMethod < Test::Unit::TestCase define_method(:pmo5) {|a, *b, c|} define_method(:pmo6) {|a, *b, c, &d|} define_method(:pmo7) {|a, b = nil, *c, d, &e|} - define_method(:pma1) {|(a), &b|} + define_method(:pma1) {|(a), &b| nil && a} + define_method(:pmk1) {|**|} + define_method(:pmk2) {|**o|} + define_method(:pmk3) {|a, **o|} + define_method(:pmk4) {|a = nil, **o|} + define_method(:pmk5) {|a, b = nil, **o|} + define_method(:pmk6) {|a, b = nil, c, **o|} + define_method(:pmk7) {|a, b = nil, *c, d, **o|} + define_method(:pmk8) {|a, b = nil, *c, d, e:, f: nil, **o|} + define_method(:pmnk) {|**nil|} def test_bound_parameters assert_equal([], method(:m0).parameters) @@ -267,7 +615,19 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:rest, :b], [:req, :c]], method(:mo5).parameters) assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:mo6).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:mo7).parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :*], [:req, :d], [:block, :e]], method(:mo8).parameters) assert_equal([[:req], [:block, :b]], method(:ma1).parameters) + assert_equal([[:keyrest, :**]], method(:mk1).parameters) + assert_equal([[:keyrest, :o]], method(:mk2).parameters) + assert_equal([[:req, :a], [:keyrest, :o]], method(:mk3).parameters) + assert_equal([[:opt, :a], [:keyrest, :o]], method(:mk4).parameters) + assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], method(:mk5).parameters) + assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], method(:mk6).parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:mk7).parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], method(:mk8).parameters) + assert_equal([[:nokey]], method(:mnk).parameters) + # pending + assert_equal([[:rest, :*], [:keyrest, :**], [:block, :&]], method(:mf).parameters) end def test_unbound_parameters @@ -281,7 +641,19 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:rest, :b], [:req, :c]], self.class.instance_method(:mo5).parameters) assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:mo6).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:mo7).parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :*], [:req, :d], [:block, :e]], self.class.instance_method(:mo8).parameters) assert_equal([[:req], [:block, :b]], self.class.instance_method(:ma1).parameters) + assert_equal([[:keyrest, :**]], self.class.instance_method(:mk1).parameters) + assert_equal([[:keyrest, :o]], self.class.instance_method(:mk2).parameters) + assert_equal([[:req, :a], [:keyrest, :o]], self.class.instance_method(:mk3).parameters) + assert_equal([[:opt, :a], [:keyrest, :o]], self.class.instance_method(:mk4).parameters) + assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], self.class.instance_method(:mk5).parameters) + assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], self.class.instance_method(:mk6).parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:mk7).parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], self.class.instance_method(:mk8).parameters) + assert_equal([[:nokey]], self.class.instance_method(:mnk).parameters) + # pending + assert_equal([[:rest, :*], [:keyrest, :**], [:block, :&]], self.class.instance_method(:mf).parameters) end def test_bmethod_bound_parameters @@ -296,6 +668,15 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], method(:pmo6).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], method(:pmo7).parameters) assert_equal([[:req], [:block, :b]], method(:pma1).parameters) + assert_equal([[:keyrest, :**]], method(:pmk1).parameters) + assert_equal([[:keyrest, :o]], method(:pmk2).parameters) + assert_equal([[:req, :a], [:keyrest, :o]], method(:pmk3).parameters) + assert_equal([[:opt, :a], [:keyrest, :o]], method(:pmk4).parameters) + assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], method(:pmk5).parameters) + assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], method(:pmk6).parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], method(:pmk7).parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], method(:pmk8).parameters) + assert_equal([[:nokey]], method(:pmnk).parameters) end def test_bmethod_unbound_parameters @@ -310,5 +691,1203 @@ class TestMethod < Test::Unit::TestCase assert_equal([[:req, :a], [:rest, :b], [:req, :c], [:block, :d]], self.class.instance_method(:pmo6).parameters) assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:block, :e]], self.class.instance_method(:pmo7).parameters) assert_equal([[:req], [:block, :b]], self.class.instance_method(:pma1).parameters) + assert_equal([[:req], [:block, :b]], self.class.instance_method(:pma1).parameters) + assert_equal([[:keyrest, :**]], self.class.instance_method(:pmk1).parameters) + assert_equal([[:keyrest, :o]], self.class.instance_method(:pmk2).parameters) + assert_equal([[:req, :a], [:keyrest, :o]], self.class.instance_method(:pmk3).parameters) + assert_equal([[:opt, :a], [:keyrest, :o]], self.class.instance_method(:pmk4).parameters) + assert_equal([[:req, :a], [:opt, :b], [:keyrest, :o]], self.class.instance_method(:pmk5).parameters) + assert_equal([[:req, :a], [:opt, :b], [:req, :c], [:keyrest, :o]], self.class.instance_method(:pmk6).parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyrest, :o]], self.class.instance_method(:pmk7).parameters) + assert_equal([[:req, :a], [:opt, :b], [:rest, :c], [:req, :d], [:keyreq, :e], [:key, :f], [:keyrest, :o]], self.class.instance_method(:pmk8).parameters) + assert_equal([[:nokey]], self.class.instance_method(:pmnk).parameters) + end + + def test_hidden_parameters + instance_eval("def m((_)"+",(_)"*256+");end") + assert_empty(method(:m).parameters.map{|_,n|n}.compact) + end + + def test_method_parameters_inspect + assert_include(method(:m0).inspect, "()") + assert_include(method(:m1).inspect, "(a)") + assert_include(method(:m2).inspect, "(a, b)") + assert_include(method(:mo1).inspect, "(a=..., &b)") + assert_include(method(:mo2).inspect, "(a, b=...)") + assert_include(method(:mo3).inspect, "(*a)") + assert_include(method(:mo4).inspect, "(a, *b, &c)") + assert_include(method(:mo5).inspect, "(a, *b, c)") + assert_include(method(:mo6).inspect, "(a, *b, c, &d)") + assert_include(method(:mo7).inspect, "(a, b=..., *c, d, &e)") + assert_include(method(:mo8).inspect, "(a, b=..., *, d, &e)") + assert_include(method(:ma1).inspect, "(_, &b)") + assert_include(method(:mk1).inspect, "(**)") + assert_include(method(:mk2).inspect, "(**o)") + assert_include(method(:mk3).inspect, "(a, **o)") + assert_include(method(:mk4).inspect, "(a=..., **o)") + assert_include(method(:mk5).inspect, "(a, b=..., **o)") + assert_include(method(:mk6).inspect, "(a, b=..., c, **o)") + assert_include(method(:mk7).inspect, "(a, b=..., *c, d, **o)") + assert_include(method(:mk8).inspect, "(a, b=..., *c, d, e:, f: ..., **o)") + assert_include(method(:mnk).inspect, "(**nil)") + assert_include(method(:mf).inspect, "(...)") + end + + def test_unbound_method_parameters_inspect + assert_include(self.class.instance_method(:m0).inspect, "()") + assert_include(self.class.instance_method(:m1).inspect, "(a)") + assert_include(self.class.instance_method(:m2).inspect, "(a, b)") + assert_include(self.class.instance_method(:mo1).inspect, "(a=..., &b)") + assert_include(self.class.instance_method(:mo2).inspect, "(a, b=...)") + assert_include(self.class.instance_method(:mo3).inspect, "(*a)") + assert_include(self.class.instance_method(:mo4).inspect, "(a, *b, &c)") + assert_include(self.class.instance_method(:mo5).inspect, "(a, *b, c)") + assert_include(self.class.instance_method(:mo6).inspect, "(a, *b, c, &d)") + assert_include(self.class.instance_method(:mo7).inspect, "(a, b=..., *c, d, &e)") + assert_include(self.class.instance_method(:mo8).inspect, "(a, b=..., *, d, &e)") + assert_include(self.class.instance_method(:ma1).inspect, "(_, &b)") + assert_include(self.class.instance_method(:mk1).inspect, "(**)") + assert_include(self.class.instance_method(:mk2).inspect, "(**o)") + assert_include(self.class.instance_method(:mk3).inspect, "(a, **o)") + assert_include(self.class.instance_method(:mk4).inspect, "(a=..., **o)") + assert_include(self.class.instance_method(:mk5).inspect, "(a, b=..., **o)") + assert_include(self.class.instance_method(:mk6).inspect, "(a, b=..., c, **o)") + assert_include(self.class.instance_method(:mk7).inspect, "(a, b=..., *c, d, **o)") + assert_include(self.class.instance_method(:mk8).inspect, "(a, b=..., *c, d, e:, f: ..., **o)") + assert_include(self.class.instance_method(:mnk).inspect, "(**nil)") + assert_include(self.class.instance_method(:mf).inspect, "(...)") + end + + def test_public_method_with_zsuper_method + c = Class.new + c.class_eval do + def foo + :ok + end + private :foo + end + d = Class.new(c) + d.class_eval do + public :foo + end + assert_equal(:ok, d.new.public_method(:foo).call) + end + + def test_public_methods_with_extended + m = Module.new do def m1; end end + a = Class.new do def a; end end + bug = '[ruby-dev:41553]' + obj = a.new + assert_equal([:a], obj.public_methods(false), bug) + obj.extend(m) + assert_equal([:m1, :a], obj.public_methods(false), bug) + end + + def test_visibility + assert_equal('method', defined?(mv1)) + assert_equal('method', defined?(mv2)) + assert_equal('method', defined?(mv3)) + + assert_equal('method', defined?(self.mv1)) + assert_equal(nil, defined?(self.mv2)) + assert_equal('method', defined?(self.mv3)) + + assert_equal(true, respond_to?(:mv1)) + assert_equal(false, respond_to?(:mv2)) + assert_equal(false, respond_to?(:mv3)) + + assert_equal(true, respond_to?(:mv1, true)) + assert_equal(true, respond_to?(:mv2, true)) + assert_equal(true, respond_to?(:mv3, true)) + + assert_nothing_raised { mv1 } + assert_nothing_raised { mv2 } + assert_nothing_raised { mv3 } + + assert_nothing_raised { self.mv1 } + assert_nothing_raised { self.mv2 } + assert_raise(NoMethodError) { (self).mv2 } + assert_nothing_raised { self.mv3 } + + class << (obj = Object.new) + private def [](x) x end + def mv1(x) self[x] end + def mv2(x) (self)[x] end + end + assert_nothing_raised { obj.mv1(0) } + assert_raise(NoMethodError) { obj.mv2(0) } + + v = Visibility.new + + assert_equal('method', defined?(v.mv1)) + assert_equal(nil, defined?(v.mv2)) + assert_equal(nil, defined?(v.mv3)) + + assert_equal(true, v.respond_to?(:mv1)) + assert_equal(false, v.respond_to?(:mv2)) + assert_equal(false, v.respond_to?(:mv3)) + + assert_equal(true, v.respond_to?(:mv1, true)) + assert_equal(true, v.respond_to?(:mv2, true)) + assert_equal(true, v.respond_to?(:mv3, true)) + + assert_nothing_raised { v.mv1 } + assert_raise(NoMethodError) { v.mv2 } + assert_raise(NoMethodError) { v.mv3 } + + assert_nothing_raised { v.__send__(:mv1) } + assert_nothing_raised { v.__send__(:mv2) } + assert_nothing_raised { v.__send__(:mv3) } + + assert_nothing_raised { v.instance_eval { mv1 } } + assert_nothing_raised { v.instance_eval { mv2 } } + assert_nothing_raised { v.instance_eval { mv3 } } + end + + def test_bound_method_entry + bug6171 = '[ruby-core:43383]' + assert_ruby_status([], <<-EOC, bug6171) + class Bug6171 + def initialize(target) + define_singleton_method(:reverse, target.method(:reverse).to_proc) + end + end + 100.times {p = Bug6171.new('test'); 1000.times {p.reverse}} + EOC + end + + def test_unbound_method_proc_coerce + # '&' coercion of an UnboundMethod raises TypeError + assert_raise(TypeError) do + Class.new do + define_method('foo', &Object.instance_method(:to_s)) + end + end + end + + def test___dir__ + assert_instance_of String, __dir__ + assert_equal(File.dirname(File.realpath(__FILE__)), __dir__) + bug8436 = '[ruby-core:55123] [Bug #8436]' + file, line = *binding.source_location + file = File.realpath(file) + assert_equal(__dir__, eval("__dir__", binding, file, line), bug8436) + bug8662 = '[ruby-core:56099] [Bug #8662]' + assert_equal("arbitrary", eval("__dir__", binding, "arbitrary/file.rb"), bug8662) + assert_equal("arbitrary", Object.new.instance_eval("__dir__", "arbitrary/file.rb"), bug8662) + end + + def test_alias_owner + bug7613 = '[ruby-core:51105]' + bug7993 = '[Bug #7993]' + c = Class.new { + def foo + end + prepend Module.new + attr_reader :zot + } + x = c.new + class << x + alias bar foo + end + assert_equal(c, c.instance_method(:foo).owner) + assert_equal(c, x.method(:foo).owner) + assert_equal(x.singleton_class, x.method(:bar).owner) + assert_equal(x.method(:foo), x.method(:bar), bug7613) + assert_equal(c, x.method(:zot).owner, bug7993) + assert_equal(c, c.instance_method(:zot).owner, bug7993) + end + + def test_included + m = Module.new { + def foo + end + } + c = Class.new { + def foo + end + include m + } + assert_equal(c, c.instance_method(:foo).owner) + end + + def test_prepended + bug7836 = '[ruby-core:52160] [Bug #7836]' + bug7988 = '[ruby-core:53038] [Bug #7988]' + m = Module.new { + def foo + end + } + c = Class.new { + def foo + end + prepend m + } + assert_raise(NameError, bug7988) {Module.new{prepend m}.instance_method(:bar)} + true || c || bug7836 + end + + def test_gced_bmethod + assert_normal_exit %q{ + require 'irb' + IRB::Irb.module_eval do + define_method(:eval_input) do + IRB::Irb.module_eval { alias_method :eval_input, :to_s } + GC.start + Kernel + end + end + IRB.start + }, '[Bug #7825]' + end + + def test_singleton_method + feature8391 = '[ruby-core:54914] [Feature #8391]' + c1 = Class.new + c1.class_eval { def foo; :foo; end } + o = c1.new + def o.bar; :bar; end + assert_nothing_raised(NameError) {o.method(:foo)} + assert_raise(NameError, feature8391) {o.singleton_method(:foo)} + m = assert_nothing_raised(NameError, feature8391) {break o.singleton_method(:bar)} + assert_equal(:bar, m.call, feature8391) + end + + def test_singleton_method_prepend + bug14658 = '[Bug #14658]' + c1 = Class.new + o = c1.new + def o.bar; :bar; end + class << o; prepend Module.new; end + m = assert_nothing_raised(NameError, bug14658) {o.singleton_method(:bar)} + assert_equal(:bar, m.call, bug14658) + + o = Object.new + assert_raise(NameError, bug14658) {o.singleton_method(:bar)} + end + + def test_singleton_method_included_or_prepended_bug_20620 + m = Module.new do + extend self + def foo = :foo + end + assert_equal(:foo, m.singleton_method(:foo).call) + assert_raise(NameError) {m.singleton_method(:puts)} + + sc = Class.new do + def t = :t + end + c = Class.new(sc) do + singleton_class.prepend(Module.new do + def bar = :bar + end) + extend(Module.new do + def quux = :quux + end) + def self.baz = :baz + end + assert_equal(:bar, c.singleton_method(:bar).call) + assert_equal(:baz, c.singleton_method(:baz).call) + assert_equal(:quux, c.singleton_method(:quux).call) + + assert_raise(NameError) {c.singleton_method(:t)} + + c2 = Class.new(c) + assert_raise(NameError) {c2.singleton_method(:bar)} + assert_raise(NameError) {c2.singleton_method(:baz)} + assert_raise(NameError) {c2.singleton_method(:quux)} + end + + Feature9783 = '[ruby-core:62212] [Feature #9783]' + + def assert_curry_three_args(m) + curried = m.curry + assert_equal(6, curried.(1).(2).(3), Feature9783) + + curried = m.curry(3) + assert_equal(6, curried.(1).(2).(3), Feature9783) + + assert_raise_with_message(ArgumentError, /wrong number/) {m.curry(2)} + end + + def test_curry_method + c = Class.new { + def three_args(a,b,c) a + b + c end + } + assert_curry_three_args(c.new.method(:three_args)) + end + + def test_curry_from_proc + c = Class.new { + define_method(:three_args) {|x,y,z| x + y + z} + } + assert_curry_three_args(c.new.method(:three_args)) + end + + def assert_curry_var_args(m) + curried = m.curry(3) + assert_equal([1, 2, 3], curried.(1).(2).(3), Feature9783) + + curried = m.curry(2) + assert_equal([1, 2], curried.(1).(2), Feature9783) + + curried = m.curry(0) + assert_equal([1], curried.(1), Feature9783) + end + + def test_curry_var_args + c = Class.new { + def var_args(*args) args end + } + assert_curry_var_args(c.new.method(:var_args)) + end + + def test_curry_from_proc_var_args + c = Class.new { + define_method(:var_args) {|*args| args} + } + assert_curry_var_args(c.new.method(:var_args)) + end + + Feature9781 = '[ruby-core:62202] [Feature #9781]' + + def test_super_method + o = Derived.new + m = o.method(:foo).super_method + assert_equal(Base, m.owner, Feature9781) + assert_same(o, m.receiver, Feature9781) + assert_equal(:foo, m.name, Feature9781) + m = assert_nothing_raised(NameError, Feature9781) {break m.super_method} + assert_nil(m, Feature9781) + end + + def test_super_method_unbound + m = Derived.instance_method(:foo) + m = m.super_method + assert_equal(Base.instance_method(:foo), m, Feature9781) + m = assert_nothing_raised(NameError, Feature9781) {break m.super_method} + assert_nil(m, Feature9781) + + bug11419 = '[ruby-core:70254]' + m = Object.instance_method(:tap) + m = assert_nothing_raised(NameError, bug11419) {break m.super_method} + assert_nil(m, bug11419) + end + + def test_super_method_module + m1 = Module.new {def foo; end} + c1 = Class.new(Derived) {include m1; def foo; end} + m = c1.instance_method(:foo) + assert_equal(c1, m.owner, Feature9781) + m = m.super_method + assert_equal(m1, m.owner, Feature9781) + m = m.super_method + assert_equal(Derived, m.owner, Feature9781) + m = m.super_method + assert_equal(Base, m.owner, Feature9781) + m2 = Module.new {def foo; end} + o = c1.new.extend(m2) + m = o.method(:foo) + assert_equal(m2, m.owner, Feature9781) + m = m.super_method + assert_equal(c1, m.owner, Feature9781) + assert_same(o, m.receiver, Feature9781) + + c1 = Class.new {def foo; end} + c2 = Class.new(c1) {include m1; include m2} + m = c2.instance_method(:foo) + assert_equal(m2, m.owner) + m = m.super_method + assert_equal(m1, m.owner) + m = m.super_method + assert_equal(c1, m.owner) + assert_nil(m.super_method) + end + + def test_super_method_bind_unbind_clone + bug15629_m1 = Module.new do + def foo; end + end + + bug15629_m2 = Module.new do + def foo; end + end + + bug15629_c = Class.new do + include bug15629_m1 + include bug15629_m2 + end + + o = bug15629_c.new + m = o.method(:foo) + sm = m.super_method + im = bug15629_c.instance_method(:foo) + sim = im.super_method + + assert_equal(sm, m.clone.super_method) + assert_equal(sim, m.unbind.super_method) + assert_equal(sim, m.unbind.clone.super_method) + assert_equal(sim, im.clone.super_method) + assert_equal(sm, m.unbind.bind(o).super_method) + assert_equal(sm, m.unbind.clone.bind(o).super_method) + assert_equal(sm, im.bind(o).super_method) + assert_equal(sm, im.clone.bind(o).super_method) + end + + def test_super_method_removed_public + c1 = Class.new {private def foo; end} + c2 = Class.new(c1) {public :foo} + c3 = Class.new(c2) {def foo; end} + c1.class_eval {undef foo} + m = c3.instance_method(:foo) + m = assert_nothing_raised(NameError, Feature9781) {break m.super_method} + assert_nil(m, Feature9781) + end + + def test_super_method_removed_regular + c1 = Class.new { def foo; end } + c2 = Class.new(c1) { def foo; end } + assert_equal c1.instance_method(:foo), c2.instance_method(:foo).super_method + c1.remove_method :foo + assert_equal nil, c2.instance_method(:foo).super_method + end + + def test_prepended_public_zsuper + mod = EnvUtil.labeled_module("Mod") {private def foo; [:ok] end} + obj = Object.new.extend(mod) + + class << obj + public :foo + end + + mod1 = EnvUtil.labeled_module("Mod1") {def foo; [:mod1] + super end} + obj.singleton_class.prepend(mod1) + + mod2 = EnvUtil.labeled_module("Mod2") {def foo; [:mod2] + super end} + obj.singleton_class.prepend(mod2) + + m = obj.method(:foo) + assert_equal mod2, m.owner + assert_equal mod1, m.super_method.owner + assert_equal obj.singleton_class, m.super_method.super_method.owner + assert_equal nil, m.super_method.super_method.super_method + + assert_equal [:mod2, :mod1, :ok], obj.foo + end + + def test_super_method_with_prepended_module + bug = '[ruby-core:81666] [Bug #13656] should be the method of the parent' + c1 = EnvUtil.labeled_class("C1") {def m; end} + c2 = EnvUtil.labeled_class("C2", c1) {def m; end} + c2.prepend(EnvUtil.labeled_module("M")) + m1 = c1.instance_method(:m) + m2 = c2.instance_method(:m).super_method + assert_equal(m1, m2, bug) + assert_equal(c1, m2.owner, bug) + assert_equal(m1.source_location, m2.source_location, bug) + end + + def test_super_method_after_bind + assert_nil String.instance_method(:length).bind(String.new).super_method, + '[ruby-core:85231] [Bug #14421]' + end + + def test_super_method_alias + c0 = Class.new do + def m1 + [:C0_m1] + end + def m2 + [:C0_m2] + end + end + + c1 = Class.new(c0) do + def m1 + [:C1_m1] + super + end + alias m2 m1 + end + + c2 = Class.new(c1) do + def m2 + [:C2_m2] + super + end + end + o1 = c2.new + assert_equal([:C2_m2, :C1_m1, :C0_m1], o1.m2) + + m = o1.method(:m2) + assert_equal([:C2_m2, :C1_m1, :C0_m1], m.call) + + m = m.super_method + assert_equal([:C1_m1, :C0_m1], m.call) + + m = m.super_method + assert_equal([:C0_m1], m.call) + + assert_nil(m.super_method) + end + + def test_super_method_alias_to_prepended_module + m = Module.new do + def m1 + [:P_m1] + super + end + + def m2 + [:P_m2] + super + end + end + + c0 = Class.new do + def m1 + [:C0_m1] + end + end + + c1 = Class.new(c0) do + def m1 + [:C1_m1] + super + end + prepend m + alias m2 m1 + end + + o1 = c1.new + assert_equal([:P_m2, :P_m1, :C1_m1, :C0_m1], o1.m2) + + m = o1.method(:m2) + assert_equal([:P_m2, :P_m1, :C1_m1, :C0_m1], m.call) + + m = m.super_method + assert_equal([:P_m1, :C1_m1, :C0_m1], m.call) + + m = m.super_method + assert_equal([:C1_m1, :C0_m1], m.call) + + m = m.super_method + assert_equal([:C0_m1], m.call) + + assert_nil(m.super_method) + end + + # Bug 17780 + def test_super_method_module_alias + m = Module.new do + def foo + end + alias :f :foo + end + + method = m.instance_method(:f) + super_method = method.super_method + assert_nil(super_method) + end + + # Bug 18435 + def test_instance_methods_owner_consistency + a = Module.new { def method1; end } + + b = Class.new do + include a + protected :method1 + end + + assert_equal [:method1], b.instance_methods(false) + assert_equal b, b.instance_method(:method1).owner + end + + def test_zsuper_method_removed + a = EnvUtil.labeled_class('A') do + private + def foo(arg = nil) + 1 + end + end + line = __LINE__ - 4 + + b = EnvUtil.labeled_class('B', a) do + public :foo + end + + unbound = b.instance_method(:foo) + + assert_equal unbound, b.public_instance_method(:foo) + assert_equal "#<UnboundMethod: A#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect + assert_equal [[:opt, :arg]], unbound.parameters + + a.remove_method(:foo) + + assert_equal "#<UnboundMethod: A#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect + assert_equal [[:opt, :arg]], unbound.parameters + + obj = b.new + assert_equal 1, unbound.bind_call(obj) + + assert_include b.instance_methods(false), :foo + link = 'https://github.com/ruby/ruby/pull/6467#issuecomment-1262159088' + assert_raise(NameError, link) { b.instance_method(:foo) } + # For #test_method_list below, otherwise we get the same error as just above + b.remove_method(:foo) + end + + def test_zsuper_method_removed_higher_method + a0 = EnvUtil.labeled_class('A0') do + def foo(arg1 = nil, arg2 = nil) + 0 + end + end + line0 = __LINE__ - 4 + a0_foo = a0.instance_method(:foo) + + a = EnvUtil.labeled_class('A', a0) do + private + def foo(arg = nil) + 1 + end + end + line = __LINE__ - 4 + + b = EnvUtil.labeled_class('B', a) do + public :foo + end + + unbound = b.instance_method(:foo) + + assert_equal a0_foo, unbound.super_method + + a.remove_method(:foo) + + assert_equal "#<UnboundMethod: A#foo(arg=...) #{__FILE__}:#{line}>", unbound.inspect + assert_equal [[:opt, :arg]], unbound.parameters + assert_equal a0_foo, unbound.super_method + + obj = b.new + assert_equal 1, unbound.bind_call(obj) + + assert_include b.instance_methods(false), :foo + assert_equal "#<UnboundMethod: A0#foo(arg1=..., arg2=...) #{__FILE__}:#{line0}>", b.instance_method(:foo).inspect + end + + def test_zsuper_method_redefined_bind_call + c0 = EnvUtil.labeled_class('C0') do + def foo + [:foo] + end + end + + c1 = EnvUtil.labeled_class('C1', c0) do + def foo + super + [:bar] + end + end + m1 = c1.instance_method(:foo) + + c2 = EnvUtil.labeled_class('C2', c1) do + private :foo + end + + assert_equal [:foo], c2.private_instance_methods(false) + m2 = c2.instance_method(:foo) + + c1.class_exec do + remove_method :foo + def foo + [:bar2] + end + end + + m3 = c2.instance_method(:foo) + c = c2.new + assert_equal [:foo, :bar], m1.bind_call(c) + assert_equal c1, m1.owner + assert_equal [:foo, :bar], m2.bind_call(c) + assert_equal c2, m2.owner + assert_equal [:bar2], m3.bind_call(c) + assert_equal c2, m3.owner + end + + # Bug #18751 + def method_equality_visbility_alias + c = Class.new do + class << self + alias_method :n, :new + private :new + end + end + + assert_equal c.method(:n), c.method(:new) + + assert_not_equal c.method(:n), Class.method(:new) + assert_equal c.method(:n) == Class.instance_method(:new).bind(c) + + assert_not_equal c.method(:new), Class.method(:new) + assert_equal c.method(:new), Class.instance_method(:new).bind(c) + end + + def rest_parameter(*rest) + rest + end + + def test_splat_long_array + if File.exist?('/etc/os-release') && File.read('/etc/os-release').include?('openSUSE Leap') + # For RubyCI's openSUSE machine http://rubyci.s3.amazonaws.com/opensuseleap/ruby-trunk/recent.html, which tends to die with NoMemoryError here. + omit 'do not exhaust memory on RubyCI openSUSE Leap machine' + end + n = 10_000_000 + assert_equal n , rest_parameter(*(1..n)).size, '[Feature #10440]' + end + + class C + D = "Const_D" + def foo + a = b = c = a = b = c = 12345 + end + + def binding_noarg + a = a = 12345 + binding + end + + def binding_one_arg(x) + a = a = 12345 + binding + end + + def binding_optargs(x, y=42) + a = a = 12345 + binding + end + + def binding_anyargs(*x) + a = a = 12345 + binding + end + + def binding_keywords(x: 42) + a = a = 12345 + binding + end + + def binding_anykeywords(**x) + a = a = 12345 + binding + end + + def binding_forwarding(...) + a = a = 12345 + binding + end + + def binding_forwarding1(x, ...) + a = a = 12345 + binding + end + end + + def test_to_proc_binding + bug11012 = '[ruby-core:68673] [Bug #11012]' + + b = C.new.method(:foo).to_proc.binding + assert_equal([], b.local_variables, bug11012) + assert_equal("Const_D", b.eval("D"), bug11012) # Check CREF + + assert_raise(NameError, bug11012){ b.local_variable_get(:foo) } + assert_equal(123, b.local_variable_set(:foo, 123), bug11012) + assert_equal(123, b.local_variable_get(:foo), bug11012) + assert_equal(456, b.local_variable_set(:bar, 456), bug11012) + assert_equal(123, b.local_variable_get(:foo), bug11012) + assert_equal(456, b.local_variable_get(:bar), bug11012) + assert_equal([:bar, :foo], b.local_variables.sort, bug11012) + end + + def test_method_binding + c = C.new + + b = c.binding_noarg + assert_equal(12345, b.local_variable_get(:a)) + + b = c.binding_one_arg(0) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(0, b.local_variable_get(:x)) + + b = c.binding_anyargs() + assert_equal(12345, b.local_variable_get(:a)) + assert_equal([], b.local_variable_get(:x)) + b = c.binding_anyargs(0) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal([0], b.local_variable_get(:x)) + b = c.binding_anyargs(0, 1) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal([0, 1], b.local_variable_get(:x)) + + b = c.binding_optargs(0) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(0, b.local_variable_get(:x)) + assert_equal(42, b.local_variable_get(:y)) + b = c.binding_optargs(0, 1) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(0, b.local_variable_get(:x)) + assert_equal(1, b.local_variable_get(:y)) + + b = c.binding_keywords() + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(42, b.local_variable_get(:x)) + b = c.binding_keywords(x: 102) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(102, b.local_variable_get(:x)) + + b = c.binding_anykeywords() + assert_equal(12345, b.local_variable_get(:a)) + assert_equal({}, b.local_variable_get(:x)) + b = c.binding_anykeywords(foo: 999) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal({foo: 999}, b.local_variable_get(:x)) + + b = c.binding_forwarding() + assert_equal(12345, b.local_variable_get(:a)) + b = c.binding_forwarding(0) + assert_equal(12345, b.local_variable_get(:a)) + b = c.binding_forwarding(0, 1) + assert_equal(12345, b.local_variable_get(:a)) + b = c.binding_forwarding(foo: 42) + assert_equal(12345, b.local_variable_get(:a)) + + b = c.binding_forwarding1(987) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(987, b.local_variable_get(:x)) + b = c.binding_forwarding1(987, 654) + assert_equal(12345, b.local_variable_get(:a)) + assert_equal(987, b.local_variable_get(:x)) + end + + MethodInMethodClass_Setup = -> do + remove_const :MethodInMethodClass if defined? MethodInMethodClass + + class MethodInMethodClass + def m1 + def m2 + end + self.class.send(:define_method, :m3){} # [Bug #11754] + end + private + end + end + + def test_method_in_method_visibility_should_be_public + MethodInMethodClass_Setup.call + + assert_equal([:m1].sort, MethodInMethodClass.public_instance_methods(false).sort) + assert_equal([].sort, MethodInMethodClass.private_instance_methods(false).sort) + + MethodInMethodClass.new.m1 + assert_equal([:m1, :m2, :m3].sort, MethodInMethodClass.public_instance_methods(false).sort) + assert_equal([].sort, MethodInMethodClass.private_instance_methods(false).sort) + end + + def test_define_method_with_symbol + assert_normal_exit %q{ + define_method(:foo, &:to_s) + define_method(:bar, :to_s.to_proc) + }, '[Bug #11850]' + c = Class.new{ + define_method(:foo, &:to_s) + define_method(:bar, :to_s.to_proc) + } + obj = c.new + assert_equal('1', obj.foo(1)) + assert_equal('1', obj.bar(1)) + end + + def test_argument_error_location + body = <<~'END_OF_BODY' + eval <<~'EOS', nil, "main.rb" + $line_lambda = __LINE__; $f = lambda do + _x = 1 + end + $line_method = __LINE__; def foo + _x = 1 + end + begin + $f.call(1) + rescue ArgumentError => e + assert_equal "main.rb:#{$line_lambda}:in 'block in <main>'", e.backtrace.first + end + begin + foo(1) + rescue ArgumentError => e + assert_equal "main.rb:#{$line_method}:in 'Object#foo'", e.backtrace.first + end + EOS + END_OF_BODY + + assert_separately [], body + # without trace insn + assert_separately [], "RubyVM::InstructionSequence.compile_option = {trace_instruction: false}\n" + body + end + + def test_zsuper_private_override_instance_method + assert_separately([], <<-'end;', timeout: 30) + # Bug #16942 [ruby-core:98691] + module M + def x + end + end + + module M2 + prepend Module.new + include M + private :x + end + + ::Object.prepend(M2) + + m = Object.instance_method(:x) + assert_equal M2, m.owner + end; + end + + def test_override_optimized_method_on_class_using_prepend + assert_separately([], <<-'end;', timeout: 30) + # Bug #17725 [ruby-core:102884] + $VERBOSE = nil + String.prepend(Module.new) + class String + def + other + 'blah blah' + end + end + + assert_equal('blah blah', 'a' + 'b') + end; + end + + def test_eqq + assert_operator(0.method(:<), :===, 5) + assert_not_operator(0.method(:<), :===, -5) + end + + def test_compose_with_method + c = Class.new { + def f(x) x * 2 end + def g(x) x + 1 end + } + f = c.new.method(:f) + g = c.new.method(:g) + + assert_equal(6, (f << g).call(2)) + assert_equal(6, (g >> f).call(2)) + end + + def test_compose_with_proc + c = Class.new { + def f(x) x * 2 end + } + f = c.new.method(:f) + g = proc {|x| x + 1} + + assert_equal(6, (f << g).call(2)) + assert_equal(6, (g >> f).call(2)) + end + + def test_compose_with_callable + c = Class.new { + def f(x) x * 2 end + } + c2 = Class.new { + def call(x) x + 1 end + } + f = c.new.method(:f) + g = c2.new + + assert_equal(6, (f << g).call(2)) + assert_equal(5, (f >> g).call(2)) + end + + def test_compose_with_noncallable + c = Class.new { + def f(x) x * 2 end + } + f = c.new.method(:f) + + assert_raise(TypeError) { + f << 5 + } + assert_raise(TypeError) { + f >> 5 + } + end + + def test_umethod_bind_call + foo = Base.instance_method(:foo) + assert_equal(:base, foo.bind_call(Base.new)) + assert_equal(:base, foo.bind_call(Derived.new)) + + plus = Integer.instance_method(:+) + assert_equal(3, plus.bind_call(1, 2)) + end + + def test_method_list + # chkbuild lists all methods. + # The following code emulate this listing. + + # use_symbol = Object.instance_methods[0].is_a?(Symbol) + nummodule = nummethod = 0 + mods = [] + ObjectSpace.each_object(Module) {|m| mods << m if String === m.name } + mods = mods.sort_by {|m| m.name } + mods.each {|mod| + nummodule += 1 + mc = mod.kind_of?(Class) ? "class" : "module" + puts_line = "#{mc} #{mod.name} #{(mod.ancestors - [mod]).inspect}" + puts_line = puts_line # prevent unused var warning + mod.singleton_methods(false).sort.each {|methname| + nummethod += 1 + meth = mod.method(methname) + line = "#{mod.name}.#{methname} #{meth.arity}" + line << " not-implemented" if !mod.respond_to?(methname) + # puts line + } + ms = mod.instance_methods(false) + if true or use_symbol + ms << :initialize if mod.private_instance_methods(false).include? :initialize + else + ms << "initialize" if mod.private_instance_methods(false).include? "initialize" + end + + ms.sort.each {|methname| + nummethod += 1 + meth = mod.instance_method(methname) + line = "#{mod.name}\##{methname} #{meth.arity}" + line << " not-implemented" if /\(not-implemented\)/ =~ meth.inspect + # puts line + } + } + # puts "#{nummodule} modules, #{nummethod} methods" + + assert_operator nummodule, :>, 0 + assert_operator nummethod, :>, 0 + end + + def test_invalidating_CC_ASAN + assert_ruby_status(['-e', 'using Module.new']) + end + + def test_kwarg_eval_memory_leak + assert_no_memory_leak([], "", <<~RUBY, rss: true, limit: 1.2) + obj = Object.new + def obj.test(**kwargs) = nil + + 100_000.times do + eval("obj.test(foo: 123)") + end + RUBY + end + + def test_super_with_splat + c = Class.new { + attr_reader :x + + def initialize(*args) + @x, _ = args + end + } + b = Class.new(c) { def initialize(...) = super } + a = Class.new(b) { def initialize(*args) = super } + obj = a.new(1, 2, 3) + assert_equal 1, obj.x + end + + def test_warn_unused_block + assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status| + def foo = nil + foo{} # warn + send(:foo){} # don't warn because it uses send + b = Proc.new{} + foo(&b) # warn + RUBY + errstr = err.join("\n") + assert_equal 2, err.size, errstr + + assert_match(/-:2: warning/, errstr) + assert_match(/-:5: warning/, errstr) + end + + assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status| + def foo = nil + 10.times{foo{}} # warn once + RUBY + assert_equal 1, err.size + end + + assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status| + def foo = nil; b = nil + foo(&b) # no warning + 1.object_id{} # no warning because it is written in C + + class C + def initialize + end + end + C.new{} # no warning + + RUBY + assert_equal 0, err.size + end + + assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status| + class C0 + def f1 = nil + def f2 = nil + def f3 = nil + def f4 = nil + def f5 = nil + def f6 = nil + end + + class C1 < C0 + def f1 = super # zsuper / use + def f2 = super() # super / use + def f3(&_) = super(&_) # super / use + def f4 = super(&nil) # super / unuse + def f5 = super(){} # super / unuse + def f6 = super{} # zsuper / unuse + end + + C1.new.f1{} # no warning + C1.new.f2{} # no warning + C1.new.f3{} # no warning + C1.new.f4{} # warning + C1.new.f5{} # warning + C1.new.f6{} # warning + RUBY + assert_equal 3, err.size, err.join("\n") + assert_match(/-:22: warning.+f4/, err.join) + assert_match(/-:23: warning.+f5/, err.join) + assert_match(/-:24: warning.+f6/, err.join) + end + + assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status| + class C0 + def f = yield + end + + class C1 < C0 + def f = nil + end + + C1.new.f{} # do not warn on duck typing + RUBY + assert_equal 0, err.size, err.join("\n") + end + + assert_in_out_err '-w', <<-'RUBY' do |_out, err, _status| + def foo(*, &block) = block + def bar(buz, ...) = foo(buz, ...) + bar(:test) {} # do not warn because of forwarding + RUBY + assert_equal 0, err.size, err.join("\n") + end end end |
