drx 0.0.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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