Ruby 一行代码整垮 irb

ugoa · 发布于 2013年01月16日 · 最后由 YangZX 回复于 2013年01月18日 · 5012 次阅读
3078

def method_missing; evil; end

将以上代码复制到irb中运行…… 聪明的你,是否悟到了其中的奥妙?lol

共收到 33 条回复
2456

循环调用,堆栈溢出?

1107
def self.method_missing; evil; end;

即可

1107

你的那种在1.8时是没问题的,在1.9会崩掉

3078

#3楼 @jasl 我只在1.9上试过的。

#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崩溃。

1107

@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的词法分析或者对象模型有关,具体的没深入研究过

96

@jasl @ugoa 这个元编程书上有讲,method_missing要慎用,而且最好是自己要捕获的函数名有一定的规律可循或者有个范围,用正则或者数组来检测是否是你想捕获的方法,不是的话就super给原始的method_missing实现来处理. jasl碰到的错误是因为重载method_missing没写对参数,method_missing应该这么写

def method_missing(name,*args)
   super unless 符合你的判断条件
end
1107

@sailtsao 嗯,这样可以了,但是重载一般函数是不需要形参一致的吧?method_missing是特殊么?

3078

#6楼 @jasl 大哥你漏了一个重要的地方,在method_missing里面我还有一个自己编造的evil()方法啊(你可以自己随便编造一个),这个方法才是引发无限递归的罪魁祸首啊,具体我上面已经解释的很清楚了。

#7楼 @sailtsao 我就是看了Ruby元编程才想到这个例子的,你说的完全正确。

1107

@ugoa 你重现一下看看抛得是什么异常。。。是ArgumentError你的eval根本就没执行到,如果栈溢出是抛SystemStackError

172

这只是简单的SystemStackError 没出什么大错误啊

3078

#11楼 @iBachue #10楼 @jasl 可能我用的是Windows上的irb 1.9.3(工作环境没有Linux),结果是它直接闪退了,没有看到抛出的异常。没有在Linux上测试,所以结果可能没Windows这么夸张,我回头再在Linux试下。

我想突出的重点是Ruby方法执行的一个小细节,即method_missing。大家明白就好了,@sailtsao 对参数的解释我以前也没注意到,学习了,感谢。

96

這也說明了誤用 method_missing 可以做成難以除錯的錯誤

2622

#8楼 @jasl 重载没问题。。但找不到方法至少会把方法名传入method_missing,但是你这method_missing没参数。。

172

#12楼 @ugoa 呵呵 method_missing 这个东西很多人都知道啊 很多其他脚本语言都有类似功能呢 关于方法的选择的细节Ruby里有很多呢

1107

@jjym 没参数怎么了?函数体里用不到name自然不需要参数嘛

1107

@jjym @sailtsao 而且这个已经不是ruby的规矩了,不同的解释器对这句话的执行结果也是有差异的,可以在pry、mri1.8、mri1.9上试试定义def method_missing; end def method_missing(name, *args); end 等等,结果是不同的

1107

@luikore 大神有想法么?

96
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

'
irb ArgumentError: wrong number of arguments (1 for 0) from (irb):1:in test' from (irb):5 from /usr/local/bin/irb:12:in'
虽然你的method_missing可能不需要这些参数,不代表你不就不用写了
ruby是灵活,而像c++等强编译的语言你重载某个函数时,你声明的函数必须要和你重载的函数完全保持一致,特别是c++用到c的东西的时候,两种语言编译器生成的函数标识符很多地方不一样的,就算名字参数你都照着抄了都不行,必须加上extern "C"才能让C++编译器了解这些导入的C的库的函数名字,呵呵,扯远了...

96

#8楼 @jasl 虽然不需要形参一致,但是至少要数目一致,否则ruby解释器会报参数数目不对的错误的ArgumentError: wrong number of arguments

1107

@sailtsao 当然不是

[jasl@localhost:~]$ pry
[1] pry(main)> def x(name);end
=> nil
[2] pry(main)> def x;end
=> nil
[3] pry(main)> x
=> nil
1107

@sailtsaohttp://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内部的设计问题

96

#21楼 @jasl 你的例子明显不对啊,一个原本需要name的函数用新的不需要参数的覆盖,到此为止没错,但是你下面调用不该是x而是x("abcd"),因为你调用一个不存在的函数的时候,ruby解释器会把你调用的信息转换成 funcname,[parameters...]的形式调用method_missing

2880

@jasl 没什么想法...参数个数不匹配不是发生在定义方法时,而是发生在调用方法时,所以你这个不加参数的method_missing可以定义,但调不进去


(跑题,很多人都知道的), 一行代码整垮 linux (fork bomb):

:(){:|:&};:

mac 跑这个就不会挂...

1107

@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

96

#25楼 @jasl 我们的理解出现了偏差,我说的形参是特指method_missing的调用,你的例子与method_missing无关,我那句解释只针对你16楼说的method_missing不需要name参数
还有,ruby的method_missing是根据函数名触发的,与参数有无无关,这里本来就不应该触发,因为你的x函数已经定义了
我的解释特指你2楼的method_missing的实现是错误的,应该至少包含name参数,否则解释器调用method_missing就报错了

96

@jasl 用这个解释挺累的,不如用skype了,如果你用的话就加我吧,在我发的帖子里面有一个skype的联系方法,你可以加我,我拖你到群里慢慢聊,我那句x("abcd")是告诉你的ruby解释器是这么调用method_missing的,这里面的abcd就是你调用的不存在的函数名

1107

@sailtsao - - 哪个。。。

1107

@luikore 不知道你又没看过wat那个视频,同样的代码在1.8下正常,在1.9下就会发生问题, 里的解释是会触发irb内部的问题,所以搞的很困惑

2880

#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 的实现自然很不相同, 引入了不同的库, 行为也会不一样...

2622

#25楼 @jasl 打个比方。。可以模拟一下。。

def a
   "hello"
end

send :a,"a"

和这道理差不多。。另外1.8是没注意

4300

irb的REPL当前类是Object

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册