Rubinius Ruby で、レシーバが代入されているローカル変数名を得る


“二変数を入れ替えるメソッド”を Squeak Smalltalk と Rubinius Ruby で について rubinius-irc で言及をいただいたみたいで、そのくだりでこんなやりとりを見つけました。

17:50:55   tmornini    Howdy all.
17:51:07 tmornini Is there a way to get the symbol for a current object?
17:51:25 tmornini i.e. foo.symbol => :foo
18:01:35 Defiler tmornini: What is 'foo' in that code?

http://www.donttreadonme.co.uk/rubinius-irc/rubinius.log.20080511.html


この種の情報を得るのには、Rubinius で、Ruby にインクリメント演算子モドキを実装 や、 Ruby勉強会@札幌-8「Rubinius 〜Smalltalk指向のRuby処理系〜」 のデモで #succ! モドキの実現に用いた方法がそのまま使えそうですね。

module Kernel
  def symbol
    sender = MethodContext.current.sender
    method = sender.method
    array = method.decode
    idx = array.index(array.detect{ |inst| inst.ip == sender.ip })
    bc = method.bytecodes.decode[idx - 2]
    if bc.size == 3
      bc[1].times{ sender = sender.home }
    else
      while sender.class == BlockContext do sender = sender.home end
    end
    sender.method.local_names[bc[-1]]
  end
end

foo = :something
p foo.symbol   #=> :foo

念のため、Squeak Smalltalk 版も。

Object >> getTempName
    | sender varIdx |
    sender := thisContext sender.
    varIdx := (sender method at: sender pc - 2) \\ 16 + 1.
    ^sender tempNames at: varIdx
| foo |
foo := #something.
^foo getTempName   "=> 'foo' "