# # 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 "#" 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