ruby--ruby/tool/make_hgraph.rb

96 lines
2.1 KiB
Ruby

#
# Make dot file of internal class/module hierarchy graph.
#
require 'objspace'
module ObjectSpace
def self.object_id_of obj
if obj.kind_of?(ObjectSpace::InternalObjectWrapper)
obj.internal_object_id
else
obj.object_id
end
end
T_ICLASS_NAME = {}
def self.class_name_of klass
case klass
when Class, Module
# (singleton class).name returns nil
klass.name || klass.inspect
when InternalObjectWrapper # T_ICLASS
if klass.type == :T_ICLASS
"#<I:#{class_name_of(ObjectSpace.internal_class_of(klass))}>"
else
klass.inspect
end
else
klass.inspect
end
end
def self.module_refenreces klass
h = {} # object_id -> [klass, class_of, super]
stack = [klass]
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| ObjectSpace.class_name_of(e)}
end
h.values
end
def self.module_refenreces_dot klass
result = []
rank_set = {}
result << "digraph mod_h {"
# result << " rankdir=LR;"
module_refenreces(klass).each{|(m, k, s)|
# next if /singleton/ =~ m
result << "#{m.dump} -> #{s.dump} [label=\"super\"];"
result << "#{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|
result << "{rank = same; #{ms.map{|m| m.dump}.join(", ")}};"
}
result << "}"
result.join("\n")
end
def self.module_refenreces_image klass, file
dot = module_refenreces_dot(klass)
img = nil
IO.popen("dot -Tpng", 'r+'){|io|
#
io.puts dot
io.close_write
img = io.read
}
open(File.expand_path(file), 'w+'){|f| f.puts img}
end
end