def method_missing; evil; end
将以上代码复制到 irb 中运行…… 聪明的你,是否悟到了其中的奥妙?lol
#1 楼 @zlx_star 99% 正确,我完善一点细节啊:
如果调用了一个对象上不存在的方法,Ruby 最终会执行每一个对象都会有的method_missing
方法,由它抛出NoMethodError
异常,。
而 def method_missing; evil; end
这一行代码重写并立即执行了 irb 默认 main 对象的method_missing
方法,奥妙就在与这个重写的方法里我又胡乱编造了一个不存在的方法 evil(),irb 在执行 method_missing 时找不到这个 evil(),所以它只好再回去调用 method_missing,结果又遇到 evil()……以此循环往复,陷入无限死结中,最终结果就是堆栈溢出,irb 崩溃。
@ugoa 你说的不对 你可以在 irb 下发现(1.9.3)
1.9.3-p327 :002 > def method_missing;end
=> nil
1.9.3-p327 :003 > xx
(irb):2:in `method_missing': wrong number of arguments (1 for 0) (ArgumentError)
from /Users/jasl/.rvm/scripts/irbrc.rb:32:in `initialize'
from /Users/jasl/.rvm/scripts/irbrc.rb:32:in `open'
from /Users/jasl/.rvm/scripts/irbrc.rb:32:in `block in <top (required)>'
(irb):2:in `method_missing': wrong number of arguments (1 for 0) (ArgumentError)
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/ruby-lex.rb:800:in `identify_identifier'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/ruby-lex.rb:731:in `block in lex_int2'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/slex.rb:236:in `call'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/slex.rb:236:in `match_io'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/slex.rb:75:in `match'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/ruby-lex.rb:286:in `token'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/ruby-lex.rb:262:in `lex'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/ruby-lex.rb:233:in `block (2 levels) in each_top_level_statement'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/ruby-lex.rb:229:in `loop'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/ruby-lex.rb:229:in `block in each_top_level_statement'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/ruby-lex.rb:228:in `catch'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb/ruby-lex.rb:228:in `each_top_level_statement'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb.rb:155:in `eval_input'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb.rb:70:in `block in start'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb.rb:69:in `catch'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/lib/ruby/1.9.1/irb.rb:69:in `start'
from /Users/jasl/.rvm/rubies/ruby-1.9.3-p327/bin/irb:16:in `<main>'
在 pry 下发现
[1] pry(main)> def method_missing;end
(pry) output error: #<ArgumentError: wrong number of arguments (1 for 0)>
[2] pry(main)> xxx
CodeRay::Scanners::Scanner::ScanError:
***ERROR in scanner.rb:200:in `rescue in tokenize': wrong number of arguments (1 for 0) (after 0 tokens)
tokens:
current line: 1 column: 1 pos: 0
matched: nil state: "Error in CodeRay::Scanners::Ruby#scan_tokens, initial state was: :initial"
bol? = true, eos? = false
surrounding code:
nil ~~ "xxx"
***ERROR***
from (pry):1:in `method_missing'
这个应该是和 ruby 的词法分析或者对象模型有关,具体的没深入研究过
def test
puts 'hello'
end
test "hello"
之前版本我没测试,我这里是 1.9.3,不需要参数的函数如果你传参进去的话是会报错的
ruby test.rb
/Users/sail/test.rb:1:in test': wrong number of arguments (1 for 0) (ArgumentError)
from /Users/sail/test.rb:4:in
test'
from (irb):5
from /usr/local/bin/irb:12:in
'@sailtsao 当然不是
[jasl@localhost:~]$ pry
[1] pry(main)> def x(name);end
=> nil
[2] pry(main)> def x;end
=> nil
[3] pry(main)> x
=> nil
@sailtsao 如http://stackoverflow.com/questions/9491462/why-do-i-get-stack-level-too-deep-from-method-missing-in-irb-1-9-3 Defining it as a top-level method replaces BasicObject#method_missing, which probably affected some irb internals like Phrogz said. 这是一个 irb 内部的设计问题
@sailtsao 我用无形参的函数覆写有形参的 为啥要调用有形参的? 如果你说的情况会调用 method_missing,那么
[1] pry(main)> def self.method_missing(*args)
[1] pry(main)* puts args.join(' ')
[1] pry(main)* end
=> nil
[2] pry(main)> xx oo
oo
xx
=> nil
[3] pry(main)> def x(name);end
=> nil
[4] pry(main)> def x;end
=> nil
[5] pry(main)> x('abc')
ArgumentError: wrong number of arguments (1 for 0)
from (pry):6:in `x'
如何解释?这里明显没有触发 method_missing
@jasl 用这个解释挺累的,不如用 skype 了,如果你用的话就加我吧,在我发的帖子里面有一个 skype 的联系方法,你可以加我,我拖你到群里慢慢聊,我那句 x("abcd") 是告诉你的 ruby 解释器是这么调用 method_missing 的,这里面的 abcd 就是你调用的不存在的函数名
@luikore 不知道你又没看过 wat 那个视频,同样的代码在 1.8 下正常,在 1.9 下就会发生问题, 里的解释是会触发 irb 内部的问题,所以搞的很困惑
#30 楼 @jasl irb 不是什么魔法,它也是 ruby 实现的,最简单的 irb 就像这样:
# simple irb
prompt = '[0] '
binding = Object.new.send :binding
loop do
print prompt
prompt.succ!
begin
res = binding.eval gets
rescue SystemExit
exit
rescue Exception
puts $!.message
puts $!.backtrace
end
puts "=> #{res.inspect}"
end
irb 里还有各种各样的功能,例如按上箭头可以把以前输入过的东西重现出来,那就需要引入 readline, 或者色彩高亮显示方法源代码,那就是 pry 里用 coderay 着色的处理。而这些功能要调用方法,或者要触发 method_missing
都不奇怪,irb 还在不断改进中,1.8 和 1.9 的实现自然很不相同,引入了不同的库,行为也会不一样...