1class Base
2  def foo() :base end
3end
4
5class Derived < Base
6  def foo() :derived end
7end
8
9class Interpreter
10  attr_accessor :ret
11
12  def do_a() @ret += "there, "; end
13  def do_d() @ret += "Hello ";  end
14  def do_e() @ret += "!\n";     end
15  def do_v() @ret += "Dave";    end
16  Dispatcher = {
17    "a" => instance_method(:do_a),
18    "d" => instance_method(:do_d),
19    "e" => instance_method(:do_e),
20    "v" => instance_method(:do_v)
21  }
22  def interpret(string)
23    @ret = ""
24    string.each_char {|b| Dispatcher[b].bind(self).call }
25  end
26end
27
28assert 'demo' do
29  interpreter = Interpreter.new
30  interpreter.interpret('dave')
31  assert_equal "Hello there, Dave!\n", interpreter.ret
32end
33
34assert 'Method#arity' do
35  Class.new {
36    attr_accessor :done
37    def initialize; @done = false; end
38    def m0() end
39    def m1(a) end
40    def m2(a, b) end
41    def mo1(a = nil, &b) end
42    def mo2(a, b = nil) end
43    def mo3(*a) end
44    def mo4(a, *b, &c) end
45    def mo5(a, *b, c) end
46    def mo6(a, *b, c, &d) end
47    def mo7(a, b = nil, *c, d, &e) end
48    def ma1((a), &b) nil && a end
49
50    def run
51      assert_equal(0, method(:m0).arity)
52      assert_equal(1, method(:m1).arity)
53      assert_equal(2, method(:m2).arity)
54      assert_equal(-1, method(:mo1).arity)
55      assert_equal(-2, method(:mo2).arity)
56      assert_equal(-1, method(:mo3).arity)
57      assert_equal(-2, method(:mo4).arity)
58      assert_equal(-3, method(:mo5).arity)
59      assert_equal(-3, method(:mo6).arity)
60      assert_equal(-3, method(:mo7).arity)
61      assert_equal(1, method(:ma1).arity)
62
63      assert_equal(-1, method(:__send__).arity)
64      assert_equal(-1, method(:nothing).arity)
65    end
66
67    def respond_to_missing?(m, b)
68      m == :nothing
69    end
70  }.new.run
71end
72
73assert 'Method and UnboundMethod should not be have a `new` method' do
74  assert_raise(NoMethodError){ Method.new }
75  assert_raise(NoMethodError){ UnboundMethod.new }
76end
77
78assert 'instance' do
79  assert_kind_of Method, 1.method(:+)
80  assert_kind_of UnboundMethod, Fixnum.instance_method(:+)
81end
82
83assert 'Method#call' do
84  assert_equal 3, 1.method(:+).call(2)
85  assert_equal "ab", "a".method(:+)["b"]
86  klass = Class.new {
87    def foo; 42; end
88  }
89  klass2 = Class.new(klass) {
90    def foo; super; end
91  }
92  assert_equal 42, klass2.new.method(:foo).call
93
94  i = Class.new {
95   def bar
96     yield 3
97   end
98  }.new
99  assert_raise(LocalJumpError) { i.method(:bar).call }
100  assert_equal 3, i.method(:bar).call { |i| i }
101end
102
103assert 'Method#call for regression' do
104  obj = BasicObject.new
105  assert_equal String, Kernel.instance_method(:inspect).bind(obj).call().class, "https://github.com/ksss/mruby-method/issues/4"
106end
107
108assert 'Method#call with undefined method' do
109  c = Class.new {
110    attr_accessor :m, :argv
111    def respond_to_missing?(m, b)
112      m == :foo
113    end
114
115    def method_missing(m, *argv)
116      @m = m
117      @argv = argv
118      super
119    end
120  }
121  cc = c.new
122  assert_raise(NameError) { cc.method(:nothing) }
123  assert_kind_of Method, cc.method(:foo)
124  assert_raise(NoMethodError) { cc.method(:foo).call(:arg1, :arg2) }
125  assert_equal :foo, cc.m
126  assert_equal [:arg1, :arg2], cc.argv
127
128  cc = c.new
129  m = cc.method(:foo)
130  c.class_eval do
131    def foo
132      :ng
133    end
134  end
135  assert_raise(NoMethodError) { m.call(:arg1, :arg2) }
136end
137
138assert 'Method#source_location' do
139  skip if proc{}.source_location.nil?
140
141  filename = __FILE__
142  klass = Class.new
143
144  lineno = __LINE__ + 1
145  klass.define_method(:find_me_if_you_can) {}
146  assert_equal [filename, lineno], klass.new.method(:find_me_if_you_can).source_location
147
148  lineno = __LINE__ + 1
149  class <<klass; define_method(:s_find_me_if_you_can) {}; end
150  assert_equal [filename, lineno], klass.method(:s_find_me_if_you_can).source_location
151
152  klass = Class.new { def respond_to_missing?(m, b); m == :nothing; end }
153  assert_nil klass.new.method(:nothing).source_location
154end
155
156assert 'UnboundMethod#source_location' do
157  skip if proc{}.source_location.nil?
158
159  filename = __FILE__
160  klass = Class.new {
161    def respond_to_missing?(m, b)
162      m == :nothing
163    end
164  }
165
166  lineno = __LINE__ + 1
167  klass.define_method(:find_me_if_you_can) {}
168  assert_equal [filename, lineno], klass.instance_method(:find_me_if_you_can).source_location
169  assert_nil klass.new.method(:nothing).unbind.source_location
170end
171
172assert 'Method#parameters' do
173  klass = Class.new {
174    def foo(a, b=nil, *c) end
175    def respond_to_missing?(m, b)
176      m == :missing
177    end
178  }
179  assert_equal [[:req, :a], [:opt, :b], [:rest, :c]], klass.new.method(:foo).parameters
180  assert_equal [[:rest]], klass.new.method(:missing).parameters
181end
182
183assert 'UnboundMethod#parameters' do
184  klass = Class.new {
185    def foo(a, b=nil, *c) end
186    def respond_to_missing?(m, b)
187      m == :nothing
188    end
189  }
190  assert_equal [[:req, :a], [:opt, :b], [:rest, :c]], klass.instance_method(:foo).parameters
191  assert_equal [[:rest]], klass.new.method(:nothing).unbind.parameters
192end
193
194assert 'Method#to_proc' do
195  m = 3.method(:+)
196  assert_kind_of Proc, m.to_proc
197  assert_equal 7, m.call(4)
198
199  o = Object.new
200  def o.foo(a, b=nil, *c)
201    [a, b, c]
202  end
203  assert_equal [:bar, nil, []], o.method(:foo).to_proc.call(:bar)
204#  We can fix this issue but leave until the problem
205#  assert_equal o.method(:foo).arity, o.method(:foo).to_proc.arity
206
207  def o.bar
208    yield 39
209  end
210  assert_equal 42, o.bar(&3.method(:+))
211end
212
213assert 'to_s' do
214  o = Object.new
215  def o.foo; end
216  m = o.method(:foo)
217  assert_equal("#<UnboundMethod: #{ class << o; self; end.inspect }#foo>", m.unbind.inspect)
218
219  c = Class.new
220  c.class_eval { def foo; end; }
221  m = c.new.method(:foo)
222  assert_equal("#<Method: #{ c.inspect }#foo>", m.inspect)
223  m = c.instance_method(:foo)
224  assert_equal("#<UnboundMethod: #{ c.inspect }#foo>", m.inspect)
225end
226
227assert 'owner' do
228  c = Class.new do
229    def foo; end
230    def self.bar; end
231  end
232  m = Module.new do
233    def baz; end
234  end
235  c.include(m)
236  c2 = Class.new(c)
237
238  assert_equal(c, c.instance_method(:foo).owner)
239  assert_equal(c, c2.instance_method(:foo).owner)
240
241  assert_equal(c, c.new.method(:foo).owner)
242  assert_equal(c, c2.new.method(:foo).owner)
243  assert_equal((class <<c; self; end), c2.method(:bar).owner)
244end
245
246assert 'owner missing' do
247  c = Class.new do
248    def respond_to_missing?(name, bool)
249      name == :foo
250    end
251  end
252  c2 = Class.new(c)
253  assert_equal(c, c.new.method(:foo).owner)
254  assert_equal(c2, c2.new.method(:foo).owner)
255end
256
257assert 'receiver name owner' do
258  o = Object.new
259  def o.foo; end
260  m = o.method(:foo)
261  assert_equal(o, m.receiver)
262  assert_equal(:foo, m.name)
263  assert_equal(class << o; self; end, m.owner)
264  assert_equal(:foo, m.unbind.name)
265  assert_equal(class << o; self; end, m.unbind.owner)
266end
267
268assert 'Method#unbind' do
269  assert_equal(:derived, Derived.new.foo)
270  um = Derived.new.method(:foo).unbind
271  assert_kind_of(UnboundMethod, um)
272  Derived.class_eval do
273    def foo() :changed end
274  end
275  assert_equal(:changed, Derived.new.foo)
276  assert_equal(:changed, Derived.new.foo{})
277  assert_equal(:derived, um.bind(Derived.new).call)
278  assert_raise(TypeError) do
279    um.bind(Base.new)
280  end
281
282  # TODO:
283  #  Block passed method not handled correctly with workaround.
284  #  See comment near `mrb_funcall_with_block` for detail.
285  # assert_equal(:derived, um.bind(Derived.new).call{})
286end
287
288assert 'Kernel#method' do
289  c1 = Class.new {
290    def foo; :foo; end
291  }
292  o = c1.new
293  assert_kind_of Method, o.method(:foo)
294  assert_kind_of Method, o.method('foo')
295  assert_raise(TypeError) { o.method(nil) }
296  assert_raise(NameError) { o.method('bar') }
297  assert_raise(NameError) { o.method(:bar) }
298end
299
300assert "Module#instance_method" do
301  assert_kind_of UnboundMethod, Object.instance_method(:object_id)
302  assert_raise(NameError) { Object.instance_method(:nothing) }
303  c = Class.new {
304    def respond_to_missing?(m, b)
305      false
306    end
307  }
308  assert_raise(NameError) { c.instance_method(:nothing) }
309end
310
311assert 'Kernel#singleton_method' do
312  c1 = Class.new {
313    def foo; :foo; end
314  }
315  o = c1.new
316  def o.bar; :bar; end
317  assert_kind_of Method, o.method(:foo)
318  assert_raise(NameError) { o.singleton_method(:foo) }
319  assert_kind_of Method, o.singleton_method(:bar)
320  assert_raise(TypeError) { o.singleton_method(nil) }
321  m = assert_nothing_raised(NameError) { break o.singleton_method(:bar) }
322  assert_equal(:bar, m.call)
323end
324
325assert 'Method#super_method' do
326  o = Derived.new
327  m = o.method(:foo).super_method
328  assert_equal(Base, m.owner)
329  assert_true(o.equal? m.receiver)
330  assert_equal(:foo, m.name)
331  assert_nil(m.super_method)
332
333  c = Class.new {
334    def foo; end
335  }
336  o = c.new
337  o.extend Module.new {
338    def foo; end
339  }
340  assert_equal c, o.method(:foo).super_method.owner
341  assert_equal :foo, o.method(:foo).super_method.name
342  assert_equal o, o.method(:foo).super_method.receiver
343end
344
345assert 'Method#==' do
346  o = Object.new
347  class << o
348    def foo; end
349  end
350  assert_not_equal(o.method(:foo), nil)
351  m = o.method(:foo)
352  def m.foo; end
353  # TODO: assert_not_equal(o.method(:foo), m)
354  assert_equal(o.method(:foo), o.method(:foo))
355  # TODO: assert_false(o.method(:foo).eql? m)
356  assert_true(o.method(:foo).eql? o.method(:foo))
357
358  assert_false(0.method(:+) == 1.method(:+))
359  assert_false(0.method(:+) == 0.method(:-))
360  a = 0.method(:+)
361  assert_true(a.method(:==) == a.method(:eql?))
362end
363
364assert "Method#initialize_copy" do
365  c = Class.new {
366    def foo
367    end
368  }.new
369  m1 = c.method(:foo)
370  m2 = m1.clone
371  assert_equal(m1, m2)
372end
373
374assert "Method#<< and Method#>>" do
375  obj = Object.new
376  class << obj
377    def mul2(n); n * 2; end
378    def add3(n); n + 3; end
379  end
380
381  f = obj.method(:mul2)
382  g = obj.method(:add3)
383
384  m1 = f << g
385  assert_kind_of Proc, m1
386  assert_equal 16, m1.call(5)
387
388  m2 = f >> g
389  assert_kind_of Proc, m2
390  assert_equal 13, m2.call(5)
391end
392
393assert 'UnboundMethod#arity' do
394  c = Class.new {
395    def foo(a, b)
396    end
397
398    def respond_to_missing?(m, b)
399      m == :nothing
400    end
401  }
402  assert_equal 2, c.instance_method(:foo).arity
403  assert_equal(-1, c.new.method(:nothing).unbind.arity)
404end
405
406assert 'UnboundMethod#==' do
407  assert_false(Fixnum.instance_method(:+) == Fixnum.instance_method(:-))
408  assert_true(Fixnum.instance_method(:+) == Fixnum.instance_method(:+))
409  assert_false(Fixnum.instance_method(:+) == Float.instance_method(:+))
410  assert_true(UnboundMethod.instance_method(:==) == UnboundMethod.instance_method(:eql?))
411end
412
413assert 'UnboundMethod#super_method' do
414  m = Derived.instance_method(:foo)
415  m = m.super_method
416  assert_equal(Base.instance_method(:foo), m)
417  assert_nil(m.super_method)
418
419  m = Object.instance_method(:object_id)
420  assert_nil(m.super_method)
421end
422
423assert 'UnboundMethod#bind' do
424  m = Module.new{ def meth() :meth end }.instance_method(:meth)
425  assert_raise(ArgumentError) { m.bind }
426  assert_kind_of Method, m.bind(1)
427  assert_kind_of Method, m.bind(:sym)
428  assert_kind_of Method, m.bind(Object.new)
429  assert_equal(:meth, m.bind(1).call)
430  assert_equal(:meth, m.bind(:sym).call)
431  assert_equal(:meth, m.bind(Object.new).call)
432  sc = nil
433  Class.new {
434    sc = class << self
435      def foo
436      end
437      self
438    end
439  }
440  assert_raise(TypeError) { sc.instance_method(:foo).bind([]) }
441  assert_raise(TypeError) { Array.instance_method(:each).bind(1) }
442  assert_kind_of Method, Object.instance_method(:object_id).bind(Object.new)
443end
444