drx 0.0.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  require 'rubygems'
2
- require 'drxtk'
2
+ require 'drx'
3
3
 
4
4
  require 'date'
5
5
 
@@ -16,9 +16,8 @@ def zmn.koko
16
16
  9090
17
17
  end
18
18
 
19
- Drx.examine(zmn)
19
+ Drx::ObjInfo.new(zmn).examine
20
20
 
21
21
  #############################
22
22
 
23
- Drx.examinetk(zmn)
24
- #Drx.examinetk("some_string")
23
+ zmn.see
@@ -1,25 +1,30 @@
1
1
  require 'rubygems'
2
2
  require 'dm-core'
3
3
 
4
+ #
5
+ # This is part of a blogging website. Users write posts. A post
6
+ # belongs to a user.
7
+ #
8
+
4
9
  class Post
5
10
  include DataMapper::Resource
6
-
7
- property :post_id, Integer, :serial => true
11
+
12
+ property :post_id, Serial
8
13
  property :title, String
9
14
  property :body, Text
10
-
15
+
11
16
  belongs_to :user
12
17
  end
13
18
 
14
19
  class User
15
20
  include DataMapper::Resource
16
-
17
- property :user_uid, Integer, :serial => true
21
+
22
+ property :user_uid, Serial
18
23
  property :name, String
19
24
  property :mail, String
20
25
  end
21
26
 
22
27
  post = Post.new
23
28
 
24
- require 'drxtk'
25
- Drx.see(post)
29
+ require 'drx'
30
+ post.see
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+
5
+ require 'drx'
6
+
7
+ class Instrument
8
+ include Enumerable
9
+ end
10
+
11
+ class Guitar < Instrument
12
+ def initialize
13
+ @strings = 5
14
+ # puts class # .add_some
15
+ end
16
+
17
+ def self.add_some
18
+ @max_strings = 10
19
+ @@approved = 'by obama team'
20
+ end
21
+ end
22
+
23
+ require 'dm-core'
24
+ class Post
25
+ include DataMapper::Resource
26
+ property :id, Serial
27
+ end
28
+
29
+ o = Guitar.new
30
+ o = Post.new
31
+ def o.unq
32
+ def g;end
33
+ end
34
+
35
+ o.see
@@ -30,7 +30,7 @@ static VALUE t_get_iv_tbl(VALUE self, VALUE obj)
30
30
  if (TYPE(obj) != T_OBJECT && TYPE(obj) != T_CLASS && TYPE(obj) != T_ICLASS && TYPE(obj) != T_MODULE) {
31
31
  rb_raise(rb_eTypeError, "Only T_OBJECT/T_CLASS/T_MODULE is expected as the argument (got %d)", TYPE(obj));
32
32
  }
33
-
33
+
34
34
  if (ROBJECT(obj)->iv_tbl) {
35
35
  st_foreach(ROBJECT(obj)->iv_tbl, record_var, (st_data_t)hash);
36
36
  }
@@ -124,73 +124,94 @@ static VALUE t_get_address(VALUE self, VALUE obj)
124
124
 
125
125
  #define RSTR(s) rb_str_new2(s)
126
126
 
127
- static t_do_locate_method(NODE *ND_method) {
127
+ static VALUE t_do_locate_method(NODE *ND_method) {
128
128
  NODE *ND_scope = NULL, *ND_block = NULL;
129
129
  VALUE place;
130
130
  char line_s[20];
131
-
131
+
132
132
  //
133
133
  // The NODE_METHOD node
134
134
  //
135
135
 
136
136
  if (nd_type(ND_method) != NODE_METHOD/*0*/) {
137
- return RSTR("I'm expecting a NODE_METHOD here...");
137
+ return RSTR("<I'm expecting a NODE_METHOD here...>");
138
138
  }
139
-
139
+
140
140
  //
141
141
  // The NODE_SCOPE node
142
142
  //
143
-
143
+
144
144
  ND_scope = ND_method->u2.node;
145
+ if (!ND_scope) {
146
+ // When we use undef() to undefine a method.
147
+ return RSTR("<undef>");
148
+ }
149
+
150
+ if (nd_type(ND_scope) == NODE_FBODY/*1*/) {
151
+ return RSTR("<alias>");
152
+ }
145
153
 
146
154
  if (nd_type(ND_scope) == NODE_CFUNC/*2*/) {
147
- return RSTR("That's a C function");
155
+ return RSTR("<c>");
148
156
  }
149
-
150
- if (nd_type(ND_scope) == NODE_ATTRSET/*89*/) {
151
- return RSTR("That's an attr setter");
157
+
158
+ if (nd_type(ND_scope) == NODE_IVAR/*50*/) {
159
+ return RSTR("<attr reader>");
152
160
  }
153
161
 
154
- if (nd_type(ND_scope) == NODE_FBODY/*1*/) {
155
- return RSTR("That's an alias");
162
+ if (nd_type(ND_scope) == NODE_ATTRSET/*89*/) {
163
+ return RSTR("<attr setter>");
156
164
  }
157
-
165
+
158
166
  if (nd_type(ND_scope) == NODE_ZSUPER/*41*/) {
159
- // @todo The DateTime clas has a lot of these.
160
- return RSTR("That's a ZSUPER, whatver the heck it means!");
167
+ // When we change visibility (using 'private :method' or 'public :method') of
168
+ // a base method, this node is created.
169
+ return RSTR("<zsuper>");
170
+ }
171
+
172
+ if (nd_type(ND_scope) == NODE_BMETHOD/*99*/) {
173
+ // This is created by define_method().
174
+ VALUE proc;
175
+ proc = (VALUE)ND_scope->u3.node;
176
+ // proc_to_s(), in eval.c, shows how to extract the location. However,
177
+ // for this we need access to the BLOCK structure. Unfortunately,
178
+ // that BLOCK is defined in eval.c, not in any .h file we can #include.
179
+ // So instead we resort to a dirty trick: we parse the output of Proc#to_s.
180
+ return rb_funcall(proc, rb_intern("_location"), 0, 0);
161
181
  }
162
182
 
163
183
  if (nd_type(ND_scope) != NODE_SCOPE/*3*/) {
164
184
  printf("I'm expecting a NODE_SCOPE HERE (got %d instead)\n", nd_type(ND_scope));
165
- return RSTR("I'm expecting a NODE_SCOPE HERE...");
185
+ return RSTR("<I'm expecting a NODE_SCOPE HERE...>");
166
186
  }
167
-
187
+
168
188
  //
169
189
  // The NODE_BLOCK node
170
190
  //
171
-
191
+
172
192
  ND_block = ND_scope->u3.node;
173
193
 
174
- if (nd_type(ND_block) != NODE_BLOCK/*4*/) {
175
- return RSTR("I'm expecting a NODE_BLOCK here...");
194
+ if (!ND_block || nd_type(ND_block) != NODE_BLOCK/*4*/) {
195
+ return RSTR("<I'm expecting a NODE_BLOCK here...>");
176
196
  }
177
-
197
+
178
198
  sprintf(line_s, "%d:", nd_line(ND_block));
179
199
  place = RSTR(line_s);
180
200
  rb_str_cat2(place, ND_block->nd_file);
181
-
201
+
182
202
  return place;
183
203
  }
184
204
 
185
205
  /*
186
206
  * call-seq:
187
- * Drx.locate_method(Date, "to_s") => str
188
- *
207
+ * Drx::Core::locate_method(Date, "to_s") => str
208
+ *
189
209
  * Locates the filename and line-number where a method was defined. Returns a
190
- * string of the form "89:/path/to/file.rb", or nil if method doens't exist.
191
- * If the method exist but isn't a Ruby method (i.e., if it's written in C),
192
- * the string returned will include an erorr message, e.g. "That's a C
193
- * function".
210
+ * string of the form "89:/path/to/file.rb", or nil if the method doesn't exist,
211
+ * or a string of the form "<identifier>".
212
+ *
213
+ * If the method exists but isn't a Ruby method (e.g., if it's written in C),
214
+ * an <identifier> string is returned. E.g., <c>, <alias>, <attr reader>.
194
215
  */
195
216
  static VALUE t_locate_method(VALUE self, VALUE obj, VALUE method_name)
196
217
  {
@@ -205,7 +226,7 @@ static VALUE t_locate_method(VALUE self, VALUE obj, VALUE method_name)
205
226
  }
206
227
  c_name = StringValuePtr(method_name);
207
228
  ID id = rb_intern(c_name);
208
- if (st_lookup(RCLASS(obj)->m_tbl, id, &method_node)) {
229
+ if (st_lookup(RCLASS(obj)->m_tbl, id, (st_data_t *)&method_node)) {
209
230
  return t_do_locate_method(method_node);
210
231
  } else {
211
232
  return Qnil;
@@ -215,21 +236,31 @@ static VALUE t_locate_method(VALUE self, VALUE obj, VALUE method_name)
215
236
  // }}}
216
237
 
217
238
  VALUE mDrx;
239
+ VALUE mCore;
240
+
241
+ void Init_drx_core() {
242
+ mDrx = rb_define_module("Drx");
243
+ mCore = rb_define_module_under(mDrx, "Core");
244
+ rb_define_module_function(mCore, "get_iv_tbl", t_get_iv_tbl, 1);
245
+ rb_define_module_function(mCore, "get_m_tbl", t_get_m_tbl, 1);
246
+ rb_define_module_function(mCore, "get_super", t_get_super, 1);
247
+ rb_define_module_function(mCore, "get_klass", t_get_klass, 1);
248
+ rb_define_module_function(mCore, "get_flags", t_get_flags, 1);
249
+ rb_define_module_function(mCore, "get_address", t_get_address, 1);
250
+ rb_define_module_function(mCore, "get_type", t_get_type, 1);
251
+ rb_define_module_function(mCore, "get_ivar", t_get_ivar, 2);
252
+ rb_define_module_function(mCore, "locate_method", t_locate_method, 2);
253
+ rb_define_const(mCore, "FL_SINGLETON", INT2FIX(FL_SINGLETON));
254
+ rb_define_const(mCore, "T_OBJECT", INT2FIX(T_OBJECT));
255
+ rb_define_const(mCore, "T_CLASS", INT2FIX(T_CLASS));
256
+ rb_define_const(mCore, "T_ICLASS", INT2FIX(T_ICLASS));
257
+ rb_define_const(mCore, "T_MODULE", INT2FIX(T_MODULE));
218
258
 
219
- void Init_drx_ext() {
220
- mDrx = rb_define_module("Drx");
221
- rb_define_module_function(mDrx, "get_iv_tbl", t_get_iv_tbl, 1);
222
- rb_define_module_function(mDrx, "get_m_tbl", t_get_m_tbl, 1);
223
- rb_define_module_function(mDrx, "get_super", t_get_super, 1);
224
- rb_define_module_function(mDrx, "get_klass", t_get_klass, 1);
225
- rb_define_module_function(mDrx, "get_flags", t_get_flags, 1);
226
- rb_define_module_function(mDrx, "get_address", t_get_address, 1);
227
- rb_define_module_function(mDrx, "get_type", t_get_type, 1);
228
- rb_define_module_function(mDrx, "get_ivar", t_get_ivar, 2);
229
- rb_define_module_function(mDrx, "locate_method", t_locate_method, 2);
230
- rb_define_const(mDrx, "FL_SINGLETON", INT2FIX(FL_SINGLETON));
231
- rb_define_const(mDrx, "T_OBJECT", INT2FIX(T_OBJECT));
232
- rb_define_const(mDrx, "T_CLASS", INT2FIX(T_CLASS));
233
- rb_define_const(mDrx, "T_ICLASS", INT2FIX(T_ICLASS));
234
- rb_define_const(mDrx, "T_MODULE", INT2FIX(T_MODULE));
259
+ // For the following, see explanation in t_do_locate_method().
260
+ rb_eval_string("\
261
+ class ::Proc;\
262
+ def _location;\
263
+ if to_s =~ /@(.*?):(\\d+)>$/ then \"#$2:#$1\" end;\
264
+ end;\
265
+ end");
235
266
  }
data/ext/extconf.rb CHANGED
@@ -1,2 +1,2 @@
1
1
  require 'mkmf'
2
- create_makefile("drx_ext")
2
+ create_makefile("drx_core")
@@ -0,0 +1,240 @@
1
+ # Adds Graphviz diagraming capability to ObjInfo
2
+
3
+ module Drx
4
+
5
+ class ObjInfo
6
+
7
+ # Notes:
8
+ # - Windows' CMD.EXE too supports "2>&1"
9
+ # - We're generating GIF, not PNG, because that's a format Tk
10
+ # supports out of the box.
11
+ GRAPHVIZ_COMMAND = 'dot "%s" -Tgif -o "%s" -Tcmapx -o "%s" 2>&1'
12
+
13
+ @@sizes = {
14
+ '100%' => "
15
+ node[fontsize=10]
16
+ ",
17
+ '90%' => "
18
+ node[fontsize=10]
19
+ ranksep=0.4
20
+ edge[arrowsize=0.8]
21
+ ",
22
+ '80%' => "
23
+ node[fontsize=10]
24
+ ranksep=0.3
25
+ nodesep=0.2
26
+ node[height=0.4]
27
+ edge[arrowsize=0.6]
28
+ ",
29
+ '60%' => "
30
+ node[fontsize=8]
31
+ ranksep=0.18
32
+ nodesep=0.2
33
+ node[height=0]
34
+ edge[arrowsize=0.5]
35
+ "
36
+ }
37
+
38
+ # Create an ID for the DOT node representing this object.
39
+ def dot_id
40
+ ('o' + address.to_s).sub('-', '_')
41
+ end
42
+
43
+ # Creates a pseudo URL for the HTML imagemap.
44
+ def dot_url
45
+ "http://server/obj/#{dot_id}"
46
+ end
47
+
48
+ # Quotes a string to be used in DOT source.
49
+ def dot_quote(s)
50
+ # @todo: find the documentation for tr()?
51
+ '"' + s.gsub('\\') { '\\\\' }.gsub('"', '\\"').gsub("\n", '\\n') + '"'
52
+ end
53
+
54
+ # Returns the DOT style for the node.
55
+ def dot_style__default
56
+ if singleton?
57
+ # A singleton class
58
+ "shape=oval,color=skyblue1,style=filled"
59
+ elsif t_class?
60
+ # A class
61
+ "shape=oval,color=lightblue1,style=filled"
62
+ elsif t_iclass? or t_module?
63
+ # A module
64
+ if repr['#']
65
+ # Paint anonymous modules only lightly.
66
+ "shape=box,style=filled,color=\"#D9FFF2\",fontcolor=gray60"
67
+ else
68
+ "shape=box,style=filled,color=aquamarine"
69
+ end
70
+ else
71
+ # Else: a "normal" object, or an immediate.
72
+ "shape=house,color=wheat1,style=filled"
73
+ end
74
+ end
75
+
76
+ # Returns the DOT style for the node.
77
+ def dot_style__crazy
78
+ craze = "distortion=#{2*rand-1},skew=#{2*rand-1},orientation=#{360*rand}"
79
+ crazy_oval = "shape=polygon,sides=25," + craze
80
+ crazy_rect = "shape=polygon,sides=#{4+rand(3)}," + craze
81
+ if singleton?
82
+ # A singleton class
83
+ "#{crazy_oval},color=palevioletred3,style=filled,fontcolor=white,peripheries=3"
84
+ elsif t_class?
85
+ # A class
86
+ "#{crazy_oval},color=palevioletred1,style=filled"
87
+ elsif t_iclass? or t_module?
88
+ # A module
89
+ "#{crazy_rect},color=peachpuff1,style=filled"
90
+ else
91
+ # Else: a "normal" object, or an immediate.
92
+ "shape=house,color=pink,style=filled"
93
+ end
94
+ end
95
+
96
+ # Returns the DOT label for the node.
97
+ #
98
+ # The representation may be quite big, so we trim it.
99
+ def dot_label(max = 20)
100
+ if class_like?
101
+ # Let's be more lenient when trimming a class/module name.
102
+ # We want to show The::Last::Component and possibly a singleton's
103
+ # trailing 'S.
104
+ max = 60 if max < 60
105
+ end
106
+ r = repr
107
+ if r.length > max
108
+ r[0, max] + ' ...'
109
+ else
110
+ r
111
+ end
112
+ end
113
+
114
+ def dot_source(level = 0, opts = {}, &block) # :yield:
115
+ out = ''
116
+ # Note: since 'obj' may be a T_ICLASS, it doesn't repond to many methods,
117
+ # including is_a?. So when we're querying things we're using Drx calls
118
+ # instead.
119
+
120
+ if level.zero?
121
+ out << 'digraph {' "\n"
122
+ out << @@sizes[opts[:size] || '100%']
123
+ @@seen = {}
124
+ end
125
+
126
+ seen = @@seen[address]
127
+ @@seen[address] = true
128
+
129
+ if not seen
130
+ dot_style = method('dot_style__' + (opts[:style] || 'default')).call
131
+ out << "#{dot_id} [#{dot_style}, label=#{dot_quote dot_label}, URL=#{dot_quote dot_url}];" "\n"
132
+ end
133
+
134
+ yield self if block_given?
135
+
136
+ return '' if seen
137
+
138
+ if class_like?
139
+ if spr = self.super and display_super?(spr)
140
+ out << spr.dot_source(level+1, opts, &block)
141
+ if [Module, ObjInfo.new(Module).klass.the_object].include? the_object
142
+ # We don't want these relatively insignificant lines to clutter the display,
143
+ # so we paint them lightly and tell DOT they aren't to affect the layout (width=0).
144
+ out << "#{dot_id} -> #{spr.dot_id} [color=gray85, weight=0];" "\n"
145
+ else
146
+ out << "#{dot_id} -> #{spr.dot_id};" "\n"
147
+ end
148
+ end
149
+ end
150
+
151
+ kls = effective_klass
152
+ if display_klass?(kls)
153
+ out << kls.dot_source(level+1, opts, &block)
154
+ out << "#{dot_id} -> #{kls.dot_id} [style=dotted];" "\n"
155
+ out << "{ rank=same; #{dot_id}; #{kls.dot_id}; }" "\n"
156
+ end
157
+
158
+ if level.zero?
159
+ out << '}' "\n"
160
+ end
161
+
162
+ return out
163
+ end
164
+
165
+ # Whether to display the klass.
166
+ def display_klass?(kls)
167
+ if t_iclass?
168
+ # We're interested in an ICLASS's klass only if it isn't Module.
169
+ #
170
+ # Usualy this means that the ICLASS has a singleton (see "Singletons
171
+ # of included modules" in display_super?()). We want to see this
172
+ # singleton.
173
+ return Module != kls.the_object
174
+ else
175
+ # Displaying a singletone's klass is confusing and usually unneeded.
176
+ return !singleton?
177
+ end
178
+ end
179
+
180
+ # Whether to display the super.
181
+ def display_super?(spr)
182
+ if (singleton? or t_iclass?) and Module == spr.the_object
183
+ # Singletons of included modules, and modules included in them,
184
+ # have their chain eventually reach Module. To prevent clutter,
185
+ # we don't show this final link.
186
+ #
187
+ # "Singletons of included modules" often exist only for their
188
+ # #included method. For example, DataMapper#Resource have
189
+ # such a singleton.
190
+ return false
191
+ end
192
+ return true
193
+ end
194
+
195
+ # Like klass(), but without surprises.
196
+ #
197
+ # Since the klass of an ICLASS is the module itself, we need to
198
+ # invoke klass() twice.
199
+ def effective_klass
200
+ if t_iclass?
201
+ klass.klass
202
+ else
203
+ klass
204
+ end
205
+ end
206
+
207
+ # Generates a diagram of the inheritance hierarchy. It accepts a hash
208
+ # pointing to pathnames to write the result to. A Tempfiles hash
209
+ # can be used instead.
210
+ #
211
+ # the_object = "some object"
212
+ # Tempfiles.new do |files|
213
+ # ObjInfo.new(the_object).generate_diagram
214
+ # system('xview ' + files['gif'])
215
+ # end
216
+ #
217
+ def generate_diagram(files, opts = {}, &block)
218
+ source = self.dot_source(0, opts, &block)
219
+ File.open(files['dot'], 'w') { |f| f.write(source) }
220
+ command = GRAPHVIZ_COMMAND % [files['dot'], files['gif'], files['map']]
221
+ message = Kernel.`(command) # `
222
+ if $? != 0
223
+ error = <<-EOS % [command, message]
224
+ ERROR: Failed to run the 'dot' command. Make sure you have the GraphViz
225
+ package installed and that its bin folder appears in your PATH.
226
+
227
+ The command I tried to execute is this:
228
+
229
+ %s
230
+
231
+ And the response I got is this:
232
+
233
+ %s
234
+ EOS
235
+ raise error
236
+ end
237
+ end
238
+
239
+ end
240
+ end
@@ -0,0 +1,147 @@
1
+
2
+ module Drx
3
+
4
+ # An object orieneted wrapper around the DrX::Core functions.
5
+ #
6
+ # This object lets you query various properties of an object.
7
+ #
8
+ # info = ObjInfo.new("foo")
9
+ # info.has_iv_tbl?
10
+ # info.klass
11
+ #
12
+ class ObjInfo
13
+ def initialize(obj)
14
+ @obj = obj
15
+ @type = Core::get_type(@obj)
16
+ end
17
+
18
+ def address
19
+ Core::get_address(@obj)
20
+ end
21
+
22
+ def the_object
23
+ @obj
24
+ end
25
+
26
+ # Returns true if this object is either a class or a module.
27
+ # When true, you know it has 'm_tbl' and 'super'.
28
+ def class_like?
29
+ [Core::T_CLASS, Core::T_ICLASS, Core::T_MODULE].include? @type
30
+ end
31
+
32
+ # Returns the method-table of an object.
33
+ def m_tbl
34
+ Core::get_m_tbl(@obj)
35
+ end
36
+
37
+ # Returns the source-code position where a method is defined.
38
+ def locate_method(method_name)
39
+ Core::locate_method(@obj, method_name)
40
+ end
41
+
42
+ def has_iv_tbl?
43
+ t_object? || class_like?
44
+ end
45
+
46
+ # Returns the variable-table of an object.
47
+ def iv_tbl
48
+ return nil if not has_iv_tbl?
49
+ Core::get_iv_tbl(@obj)
50
+ end
51
+
52
+ # @todo: this could be nicer. perhaps define an [] accessor.
53
+ def __get_ivar(name)
54
+ if class_like? and name.to_s =~ /^[A-Z]/
55
+ # If it's a constant, it may be 'autoloaded'. We
56
+ # trigger the loading by calling const_get().
57
+ @obj.const_get(name)
58
+ end
59
+ Core::get_ivar(@obj, name)
60
+ end
61
+
62
+ def singleton?
63
+ class_like? && (Core::get_flags(@obj) & Core::FL_SINGLETON).nonzero?
64
+ end
65
+
66
+ def t_iclass?
67
+ @type == Core::T_ICLASS
68
+ end
69
+
70
+ def t_class?
71
+ @type == Core::T_CLASS
72
+ end
73
+
74
+ def t_object?
75
+ @type == Core::T_OBJECT
76
+ end
77
+
78
+ def t_module?
79
+ @type == Core::T_MODULE
80
+ end
81
+
82
+ # Note: the klass of an iclass is the included module.
83
+ def klass
84
+ ObjInfo.new Core::get_klass(@obj)
85
+ end
86
+
87
+ # Returns the 'super' of a class-like object. Returns nil for end of chain.
88
+ #
89
+ # Examples: Kernel has a NULL super. Modules too have NULL super, unless
90
+ # when 'include'ing.
91
+ def super
92
+ spr = Core::get_super(@obj)
93
+ # Note: we can't do 'if spr.nil?' because T_ICLASS doesn't "have" #nil.
94
+ spr ? ObjInfo.new(spr) : nil
95
+ end
96
+
97
+ # Returns a string representation of the object. Similar to Object#inspect.
98
+ def repr
99
+ if t_iclass?
100
+ 'include ' + klass.repr
101
+ elsif singleton?
102
+ attached = __get_ivar('__attached__') || self
103
+ attached.inspect + " 'S"
104
+ else
105
+ @obj.inspect
106
+ end
107
+ end
108
+
109
+ # A utility function to print the inheritance hierarchy of an object.
110
+ def examine(level = 0, title = '', &block) # :yield:
111
+ # Note: since '@obj' may be a T_ICLASS, it doesn't repond to may methods,
112
+ # including is_a?. So when we're querying things we're using Drx calls
113
+ # instead.
114
+
115
+ @@seen = {} if level.zero?
116
+ line = (' ' * level) + title + ' ' + repr
117
+
118
+ seen = @@seen[address]
119
+ @@seen[address] = true
120
+
121
+ if seen
122
+ line += " [seen]"
123
+ end
124
+
125
+ if block_given?
126
+ yield line, self
127
+ else
128
+ puts line
129
+ end
130
+
131
+ return if seen
132
+
133
+ if class_like?
134
+ if spr = self.super
135
+ spr.examine(level + 1, '[super]', &block)
136
+ end
137
+ end
138
+
139
+ # Displaying a T_ICLASS's klass isn't very useful, because the data
140
+ # is already mirrored in the m_tbl and iv_tvl of the T_ICLASS itself.
141
+ if not t_iclass?
142
+ klass.examine(level + 1, '[klass]', &block)
143
+ end
144
+ end
145
+ end
146
+
147
+ end