K.Sasada's Home Page

Diary - 2015 May

研究日記

皐月

_29(Fri)

色々行き詰まっているので、誰得な新機能を入れました。Rubyのしくみ読者用。

例えば、嘘偽りの無い ancestors が欲しい時はこうします。

require 'objspace'

def ObjectSpace.internal_ancestors_of klass
  ancestors = []
  while klass
    ancestors << klass
    klass = ObjectSpace.internal_super_of(klass)
  end
  ancestors
end

require 'pp'
pp String.ancestors
pp ObjectSpace.internal_ancestors_of(String)

結果。

[String, Comparable, Object, PP::ObjectMixin, Kernel, BasicObject]
[String,
 #<InternalObject:0x7c671c T_ICLASS>,
 Object,
 #<InternalObject:0x282eb58 T_ICLASS>,
 #<InternalObject:0x7c7568 T_ICLASS>,
 BasicObject]

ほうら、わかりやすい。

クラス / モジュールの参照図を書いてみるのも分かりやすい。

require 'objspace'
require 'pp'

def ObjectSpace.object_id_of obj
  if obj.kind_of?(ObjectSpace::InternalObjectWrapper)
    obj.internal_object_id
  else
    obj.object_id
  end
end

class Module
  def references
    h = {} # object_id -> [klass, class_of, super]
    stack = [self]
    while klass = stack.pop
      obj_id = ObjectSpace.object_id_of(klass)
      next if h.has_key?(obj_id)
      cls = ObjectSpace.internal_class_of(klass)
      sup = ObjectSpace.internal_super_of(klass)
      stack << cls if cls
      stack << sup if sup
      h[obj_id] = [klass, cls, sup].map{|e| (defined?(e.name) && e.name) ? e.name : e.inspect}
    end
    h.values
  end
end

pp String.references
[["String", "#<Class:String>", "#<InternalObject:0x806718 T_ICLASS>"],
 ["#<InternalObject:0x806718 T_ICLASS>", "Comparable", "Object"],
 ["Object", "#<Class:Object>", "#<InternalObject:0x271dbc4 T_ICLASS>"],
 ["#<InternalObject:0x271dbc4 T_ICLASS>",
  "PP::ObjectMixin",
  "#<InternalObject:0x807564 T_ICLASS>"],
 ["#<InternalObject:0x807564 T_ICLASS>", "Kernel", "BasicObject"],
 ["BasicObject", "#<Class:BasicObject>", "nil"],
 ["#<Class:BasicObject>", "#<Class:#<Class:BasicObject>>", "Class"],
 ["Class", "#<Class:Class>", "Module"],
 ["Module", "#<Class:Module>", "Object"],
 ["#<Class:Module>", "#<Class:#<Class:Module>>", "#<Class:Object>"],
 ["#<Class:Object>", "#<Class:#<Class:Object>>", "#<Class:BasicObject>"],
 ["#<Class:#<Class:Object>>",
  "#<Class:#<Class:Class>>",
  "#<Class:#<Class:BasicObject>>"],
 ["#<Class:#<Class:BasicObject>>",
  "#<Class:#<Class:Class>>",
  "#<Class:Class>"],
 ["#<Class:Class>", "#<Class:#<Class:Class>>", "#<Class:Module>"],
 ["#<Class:#<Class:Class>>",
  "#<Class:#<Class:Class>>",
  "#<Class:#<Class:Module>>"],
 ["#<Class:#<Class:Module>>",
  "#<Class:#<Class:Class>>",
  "#<Class:#<Class:Object>>"],
 ["Kernel", "#<Class:Kernel>", "nil"],
 ["#<Class:Kernel>", "Class", "Module"],
 ["PP::ObjectMixin", "Module", "nil"],
 ["Comparable", "Module", "nil"],
 ["#<Class:String>", "#<Class:Class>", "#<Class:Object>"]]

この配列の各要素は m, k, s の配列になっており、m のクラスは k、m の super は s という関係になっています。

で、これはグラフなので、graphviz に食わせるようにしてみると、

require 'objspace'
require 'pp'

def ObjectSpace.object_id_of obj
  if obj.kind_of?(ObjectSpace::InternalObjectWrapper)
    obj.internal_object_id
  else
    obj.object_id
  end
end

class Module
  def references
    h = {} # object_id -> [klass, class_of, super]
    stack = [self]
    while klass = stack.pop
      obj_id = ObjectSpace.object_id_of(klass)
      next if h.has_key?(obj_id)
      cls = ObjectSpace.internal_class_of(klass)
      sup = ObjectSpace.internal_super_of(klass)
      stack << cls if cls
      stack << sup if sup
      h[obj_id] = [klass, cls, sup].map{|e| (defined?(e.name) && e.name) ? e.name : e.inspect}
    end
    h.values
  end
end

class C
end

rank_set = {}

puts "digraph mod_h {"
C.references.each{|(m, s, k)|
  # next if /singleton/ =~ m
  puts "#{m.dump} -> #{s.dump} [label=\"super\"];"
  puts "#{m.dump} -> #{k.dump} [label=\"klass\"];"

  unless rank = rank_set[m]
    rank = rank_set[m] = 0
  end
  unless rank_set[s]
    rank_set[s] = rank + 1
  end
  unless rank_set[k]
    rank_set[k] = rank
  end
}

rs = [] # [[mods...], ...]
rank_set.each{|m, r|
  rs[r] = [] unless rs[r]
  rs[r] << m
}

rs.each{|ms|
  puts "{rank = same; #{ms.map{|m| m.dump}.join(", ")}};"
}

puts "}"

こんな絵が出ます。

http://www.atdot.net/fp_store/f.z1u2pn/file.g.png

ちょっと複雑なものを食わせてみると、

module M0; end
module M1; end
class C0; end
class C1 < C0
  include M0
  prepend M1
end

http://www.atdot.net/fp_store/f.wfu2pn/file.g.png

凄いコトに。

方向を弄ってみたら、

http://www.atdot.net/fp_store/f.9nu2pn/file.g.png

ちょっと見やすくなったかな。


k と s の順番間違えてた。

http://www.atdot.net/fp_store/f.ksu2pn/file.g.png

Sasada Koichi / ko1 at atdot dot net
$Date: 2003/04/28 10:27:51 $