[元编程系列] 邪恶

下面的一些代码,出自一个古老的库:evil.rb,你可以 gem install evil-ruby 来获得它。
如果想要兼容1.9的 evil-ruby,请用 Yugui 姐姐的修改版:
http://github.com/yugui/evil-ruby/tree/master

evil.rb 使用了核心库 Ruby/DL 来获得 C 层次的 Ruby 对象访问

不过……Yugui 姐姐你写的 DL,不仅大改 API,而且文档几乎为0……

作为实用主义者,先总结一下 API~ (源文件里注释写得很好,推荐直接看邪恶的源代码)

你可以暂停GC的运行:

Ruby代码
  1. require 'evil'  
  2. RubyInternal.critical do  
  3.   # GC暂停了!做坏事要打紧!  
  4. end  
  5.   
  6. # critical 块和 RubyInternal 是下面很多方法实现的基础。  



你还可以看看一个对象在内存中是个什么东西 :

Ruby代码
  1. require 'pp'  
  2. pp '12'.internal  



可以获得地址……:

Ruby代码
  1. '12'.internal_ptr.to_i # 47699480  
  2. '12'.internal_ptr.to_i # 47667740,显示这两个'12'是不同的对象  



可以获得内部类型在 C 中的定义数值:

Ruby代码
  1. nil.internal_type # 在1.8是32,在1.9是16  



还可以解冻对象~~(这个例子是作者举的):

Ruby代码
  1. obj = "Hello World".freeze  
  2. obj.frozen? # => true  
  3. obj.unfreeze  
  4. obj.frozen? # => false  

 

解冻是超越安全等级的!
虽然在在高于 0 的安全等级用 unfreeze 时,会装模做样的弹出一个 Error,但是删掉 raise 那行就过去了……
通过 RubyInternal 操作 flag 和 mask,还可以解除 tainted 等状态 -___-

怪不得 Programing Ruby 里面讲安全等级的那章说:

引用
You can also use some devious tricks to do this without using untaint. We’ll leave it up to your darker side to find them.

教训就是:千万不要 eval 信不过的代码!



检查对象是否直接量(关于直接量和非直接量,请参考 JE 知识库里的 Ruby Hacking Guide):

Ruby代码
  1. 12.direct_value? # => true  



可以改变对象所属的类!

Ruby代码
  1. class A;def 自白;puts '我是一个A';end;end  
  2. class B;def 自白;puts '我是一个B';end;end  
  3. a = A.new  
  4. a.自白      # => 我是一个A  
  5. a.class= B  # 无敌的class=  
  6. a.自白      # => 我是一个B  



也可以改变继承关系!

Ruby代码
  1. A.superclass = B  



类,就是蜡烛一般弱的东西……噗~~~就没了~~~



一心同体……共享实例变量:

Ruby代码
  1. class Body  
  2.   attr_accessor :heart  
  3. end  
  4. good_guy = Body.new  
  5. good_guy.heart = 'good'  
  6. bad_guy = Body.new  
  7.   
  8. bad_guy.share_instance_variables good_guy # 建立连接,同步率直线上升!  
  9.   
  10. puts bad_guy.heart     # => kind  
  11. bad_guy.heart = 'bad'  # 一个修改,全部改变  
  12. puts good_guy.heart    # => bad  



复制单例方法:

Ruby代码
  1. a.grab_singleton_methods b  



著名的 class to module:

Ruby代码
  1. class A  
  2.   include Array.as_module  # A可以当Array用了~  
  3. end  



Ruby不支持多重继承?当然支持了~

Ruby代码
  1. class C  
  2.   inherit A, B  # 后面的同名方法会覆盖前面的  
  3. end  



KernellessObject——更清洁干净的 BlankSlate 替代物,用来做基于 method_missing 的 DSL 载体非常合适。

在1.9里面返回一个 BasicObject。

Ruby代码
  1. clean_obj = KernellessObject.new  # 在irb里会弹出一堆错,因为它连inspect方法都没有,irb想偷窥都不行  



UnboundedMethod#bind(obj) 原本要求 obj 属于该方法所在的类,不过 force_bind 超越了这个限制。
下面是作者给的例子,我无耻的copy出来了:

Ruby代码
  1. foo_klass = Class.new do  
  2.   def greet; "#{self.inspect} says 'Hi!'"end  
  3. end  
  4. obj = []  
  5. greet = foo_klass.instance_method(:greet)  
  6. greet.bind(obj).call        # raises TypeError  
  7. greet.force_bind(obj).call  # => "[] says 'Hi!'"  



还有两个简单的self的运用,其实现和C无关:

Ruby代码
  1. class Object  
  2.   def meta_class  
  3.     class << self  
  4.       self  
  5.     end  
  6.   end  
  7. end  
  8.   
  9. # meta_class(或者singleton_class,或者prototype)是一个class,  
  10. # 但是对它的修改只影响该对象,不会影响对象的类。  
  11.   
  12. s = '不卫生'  
  13. s.meta_class.class_eval do  
  14.   def xit  
  15.     'xit'  
  16.   end  
  17. end  
  18. s.xit         # => 'xit'  
  19. '很卫生'.xit # => undefined method,String没有被改变  

 

 

Ruby代码
  1. class Proc  
  2.   def self  
  3.     eval 'self'self  
  4.     # eval的效果是——获得proc所在的对象,而直接用self只返回Proc本身  
  5.   end  
  6. end  

 

posted @ 2009-08-17 16:40  麦飞  阅读(264)  评论(0编辑  收藏  举报