nitstats: new class Counter (replace uses of HashMap[E, Int])
[nit.git] / src / nitstats.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2012 Jean Privat <jean@pryen.org>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # A program that collects various data about nit programs and libraries
18 module nitstats
19 # Collected datas are :
20 # * number of modules
21 # * number of classes (interface, class, enum, extern, abstract)
22 # * number of class definitions and refinments
23 # * number of properties
24 # * number of used types and runtime classes
25
26 import modelbuilder
27 import exprbuilder
28 import rapid_type_analysis
29
30 # A counter counts occurence of things
31 # Use this instead of a HashMap[E, Int]
32 class Counter[E]
33 # Total number of counted occurences
34 var total: Int = 0
35
36 private var map = new HashMap[E, Int]
37
38 # The number of counted occurences of `e'
39 fun [](e: E): Int
40 do
41 var map = self.map
42 if map.has_key(e) then return map[e]
43 return 0
44 end
45
46 # Count one more occurence of `e'
47 fun inc(e: E)
48 do
49 self.map[e] = self[e] + 1
50 total += 1
51 end
52
53 # Return an array of elements sorted by occurences
54 fun sort: Array[E]
55 do
56 var res = map.keys.to_a
57 res.sort !cmp a, b = map[a] <=> map[b]
58 return res
59 end
60 end
61
62 # The job of this visitor is to resolve all types found
63 class ATypeCounterVisitor
64 super Visitor
65 var modelbuilder: ModelBuilder
66 var nclassdef: AClassdef
67
68 var typecount: Counter[MType]
69
70 # Get a new visitor on a classef to add type count in `typecount'.
71 init(modelbuilder: ModelBuilder, nclassdef: AClassdef, typecount: Counter[MType])
72 do
73 self.modelbuilder = modelbuilder
74 self.nclassdef = nclassdef
75 self.typecount = typecount
76 end
77
78 redef fun visit(n)
79 do
80 if n isa AType then
81 var mtype = modelbuilder.resolve_mtype(self.nclassdef, n)
82 if mtype != null then
83 self.typecount.inc(mtype)
84 end
85 end
86 n.visit_all(self)
87 end
88 end
89
90 class ANewVisitor
91 super Visitor
92 var modelbuilder: ModelBuilder
93 var nclassdef: AClassdef
94
95 var news: Set[MClass]
96
97 # Get a new visitor on a classef to add type count in `typecount'.
98 init(modelbuilder: ModelBuilder, nclassdef: AClassdef, news: Set[MClass])
99 do
100 self.modelbuilder = modelbuilder
101 self.nclassdef = nclassdef
102 self.news = news
103 end
104
105 redef fun visit(n)
106 do
107 if n isa ANewExpr then
108 var mtype = modelbuilder.resolve_mtype(self.nclassdef, n.n_type)
109 if mtype != null then
110 assert mtype isa MClassType
111 self.news.add(mtype.mclass)
112 end
113 end
114 n.visit_all(self)
115 end
116 end
117
118 class ASelfVisitor
119 super Visitor
120 var total: Int = 0
121 var implicits: Int = 0
122
123 redef fun visit(n)
124 do
125 if n isa ASelfExpr then
126 self.total += 1
127 if n isa AImplicitSelfExpr then
128 self.implicits += 1
129 end
130 end
131 n.visit_all(self)
132 end
133 end
134
135 class NullableSends
136 super Visitor
137 var modelbuilder: ModelBuilder
138 var nclassdef: AClassdef
139
140 var total_sends: Int = 0
141 var nullable_sends: Int = 0
142 var buggy_sends: Int = 0
143
144 # Get a new visitor on a classef to add type count in `typecount'.
145 init(modelbuilder: ModelBuilder, nclassdef: AClassdef)
146 do
147 self.modelbuilder = modelbuilder
148 self.nclassdef = nclassdef
149 end
150
151 redef fun visit(n)
152 do
153 n.visit_all(self)
154 if n isa ASendExpr then
155 self.total_sends += 1
156 var t = n.n_expr.mtype
157 if t == null then
158 self.buggy_sends += 1
159 return
160 end
161 t = t.anchor_to(self.nclassdef.mclassdef.mmodule, self.nclassdef.mclassdef.bound_mtype)
162 if t isa MNullableType then
163 self.nullable_sends += 1
164 else if t isa MClassType then
165 # Nothing
166 else
167 n.debug("Problem: strange receiver type found: {t} ({t.class_name})")
168 end
169 end
170 end
171 end
172
173 # Visit all `nmodules' of a modelbuilder and compute statistics on the usage of explicit static types
174 fun count_ntypes(modelbuilder: ModelBuilder)
175 do
176 # Count each occurence of a specific static type
177 var typecount = new Counter[MType]
178
179 # Visit all the source code to collect data
180 for nmodule in modelbuilder.nmodules do
181 for nclassdef in nmodule.n_classdefs do
182 var visitor = new ATypeCounterVisitor(modelbuilder, nclassdef, typecount)
183 visitor.enter_visit(nclassdef)
184 end
185 end
186
187 # Display data
188 print "--- Statistics of the explitic static types ---"
189 print "Total number of explicit static types: {typecount.total}"
190 if typecount.total == 0 then return
191
192 # types sorted by usage
193 var types = typecount.sort
194
195 # Display most used types (ie the last of `types')
196 print "Most used types: "
197 var min = 10
198 if types.length < min then min = types.length
199 for i in [0..min[ do
200 var t = types[types.length-i-1]
201 print " {t}: {typecount[t]}"
202 end
203
204 # Compute the distribution of type usage
205 print "Distribution of type usage:"
206 var count = 0
207 var sum = 0
208 var limit = 1
209 for t in types do
210 if typecount[t] > limit then
211 print " <={limit}: {count} ({div(count*100,types.length)}% of types; {div(sum*100,typecount.total)}% of usage)"
212 count = 0
213 sum = 0
214 while typecount[t] > limit do limit = limit * 2
215 end
216 count += 1
217 sum += typecount[t]
218 end
219 print " <={limit}: {count} ({div(count*100,types.length)}% of types; {div(sum*100,typecount.total)}% of usage)"
220 end
221
222 fun visit_news(modelbuilder: ModelBuilder, mainmodule: MModule)
223 do
224 print "--- Dead classes ---"
225 # Count each occurence of a specific static type
226 var news = new HashSet[MClass]
227
228 # Visit all the source code to collect data
229 for nmodule in modelbuilder.nmodules do
230 for nclassdef in nmodule.n_classdefs do
231 var visitor = new ANewVisitor(modelbuilder, nclassdef, news)
232 visitor.enter_visit(nclassdef)
233 end
234 end
235
236 for c in modelbuilder.model.mclasses do
237 if c.kind == concrete_kind and not news.has(c) then
238 print "{c.full_name}"
239 end
240 end
241
242 var hier = mainmodule.flatten_mclass_hierarchy
243 for c in hier do
244 if c.kind != abstract_kind and not (c.kind == concrete_kind and not news.has(c)) then continue
245
246 for sup in hier[c].greaters do
247 for cd in sup.mclassdefs do
248 for p in cd.intro_mproperties do
249 if p isa MAttribute then
250 continue label classes
251 end
252 end
253 end
254 end
255 print "no attributes: {c.full_name}"
256
257 end label classes
258 end
259
260 fun visit_self(modelbuilder: ModelBuilder)
261 do
262 print "--- Explicit vs. Implicit Self ---"
263 # Visit all the source code to collect data
264 var visitor = new ASelfVisitor
265 for nmodule in modelbuilder.nmodules do
266 for nclassdef in nmodule.n_classdefs do
267 visitor.enter_visit(nclassdef)
268 end
269 end
270 print "Total number of self: {visitor.total}"
271 print "Total number of implicit self: {visitor.implicits} ({div(visitor.implicits*100,visitor.total)}%)"
272 end
273
274 fun visit_nullable_sends(modelbuilder: ModelBuilder)
275 do
276 print "--- Sends on Nullable Reciever ---"
277 var total_sends = 0
278 var nullable_sends = 0
279 var buggy_sends = 0
280
281 # Visit all the source code to collect data
282 for nmodule in modelbuilder.nmodules do
283 for nclassdef in nmodule.n_classdefs do
284 var visitor = new NullableSends(modelbuilder, nclassdef)
285 visitor.enter_visit(nclassdef)
286 total_sends += visitor.total_sends
287 nullable_sends += visitor.nullable_sends
288 buggy_sends += visitor.buggy_sends
289 end
290 end
291 print "Total number of sends: {total_sends}"
292 print "Number of sends on a nullable receiver: {nullable_sends} ({div(nullable_sends*100,total_sends)}%)"
293 print "Number of buggy sends (cannot determine the type of the receiver): {buggy_sends} ({div(buggy_sends*100,total_sends)}%)"
294 end
295
296 # Compute general statistics on a model
297 fun compute_statistics(model: Model)
298 do
299 print "--- Statistics of the model ---"
300 var nbmod = model.mmodules.length
301 print "Number of modules: {nbmod}"
302
303 print ""
304
305 var nbcla = model.mclasses.length
306 var nbcladef = model.mclassdef_hierarchy.length
307 print "Number of classes: {nbcla}"
308
309 # determine the distribution of:
310 # * class kinds (interface, abstract class, etc.)
311 # * refinex classes (vs. unrefined ones)
312 var kinds = new Counter[MClassKind]
313 var refined = 0
314 for c in model.mclasses do
315 kinds.inc(c.kind)
316 if c.mclassdefs.length > 1 then
317 refined += 1
318 end
319 end
320 for k in kinds.sort do
321 var v = kinds[k]
322 print " Number of {k} kind: {v} ({div(v*100,nbcla)}%)"
323 end
324
325
326 print ""
327
328 print "Nomber of class definitions: {nbcladef}"
329 print "Number of refined classes: {refined} ({div(refined*100,nbcla)}%)"
330 print "Average number of class refinments by classes: {div(nbcladef-nbcla,nbcla)}"
331 print "Average number of class refinments by refined classes: {div(nbcladef-nbcla,refined)}"
332
333 print ""
334
335 var nbprop = model.mproperties.length
336 print "Number of properties: {model.mproperties.length}"
337 end
338
339 # Compute class tables for the classes of the program main
340 fun compute_tables(main: MModule)
341 do
342 var model = main.model
343
344 var nc = 0 # Number of runtime classes
345 var nl = 0 # Number of usages of class definitions (a class definition can be used more than once)
346 var nhp = 0 # Number of usages of properties (a property can be used more than once)
347 var npas = 0 # Number of usages of properties without lookup (easy easy case, easier that CHA)
348
349 # Collect the full class hierarchy
350 var hier = main.flatten_mclass_hierarchy
351 for c in hier do
352 # Skip classes without direct instances
353 if c.kind == interface_kind or c.kind == abstract_kind then continue
354
355 nc += 1
356
357 # Now, we need to collect all properties defined/inherited/imported
358 # So, visit all definitions of all super-classes
359 for sup in hier[c].greaters do
360 for cd in sup.mclassdefs do
361 nl += 1
362
363 # Now, search properties introduced
364 for p in cd.intro_mproperties do
365
366 nhp += 1
367 # Select property definition
368 if p.mpropdefs.length == 1 then
369 npas += 1
370 else
371 var sels = p.lookup_definitions(main, c.mclassdefs.first.bound_mtype)
372 if sels.length > 1 then
373 print "conflict for {p.full_name} in class {c.full_name}: {sels.join(", ")}"
374 else if sels.is_empty then
375 print "ERROR: no property for {p.full_name} in class {c.full_name}!"
376 end
377 end
378 end
379 end
380 end
381 end
382
383 print "--- Construction of tables ---"
384 print "Number of runtime classes: {nc} (excluding interfaces and abstract classes)"
385 print "Average number of composing class definition by runtime class: {div(nl,nc)}"
386 print "Total size of tables (classes and instances): {nhp} (not including stuff like info for subtyping or call-next-method)"
387 print "Average size of table by runtime class: {div(nhp,nc)}"
388 print "Values never redefined: {npas} ({div(npas*100,nhp)}%)"
389 end
390
391 # Helper function to display n/d and handle division by 0
392 fun div(n: Int, d: Int): String
393 do
394 if d == 0 then return "na"
395 return ((100*n/d).to_f/100.0).to_precision(2)
396 end
397
398 # Create a dot file representing the module hierarchy of a model.
399 # Importation relation is represented with arrow
400 # Nesting relation is represented with nested boxes
401 fun generate_module_hierarchy(model: Model)
402 do
403 var buf = new Buffer
404 buf.append("digraph \{\n")
405 buf.append("node [shape=box];\n")
406 buf.append("rankdir=BT;\n")
407 for mmodule in model.mmodules do
408 if mmodule.direct_owner == null then
409 generate_module_hierarchy_for(mmodule, buf)
410 end
411 end
412 for mmodule in model.mmodules do
413 for s in mmodule.in_importation.direct_greaters do
414 buf.append("\"{mmodule}\" -> \"{s}\";\n")
415 end
416 end
417 buf.append("\}\n")
418 var f = new OFStream.open("module_hierarchy.dot")
419 f.write(buf.to_s)
420 f.close
421 end
422
423 # Helper function for `generate_module_hierarchy'.
424 # Create graphviz nodes for the module and recusrively for its nested modules
425 private fun generate_module_hierarchy_for(mmodule: MModule, buf: Buffer)
426 do
427 if mmodule.in_nesting.direct_greaters.is_empty then
428 buf.append("\"{mmodule.name}\";\n")
429 else
430 buf.append("subgraph \"cluster_{mmodule.name}\" \{label=\"\"\n")
431 buf.append("\"{mmodule.name}\";\n")
432 for s in mmodule.in_nesting.direct_greaters do
433 generate_module_hierarchy_for(s, buf)
434 end
435 buf.append("\}\n")
436 end
437 end
438
439 # Create a dot file representing the class hierarchy of a model.
440 fun generate_class_hierarchy(mmodule: MModule)
441 do
442 var buf = new Buffer
443 buf.append("digraph \{\n")
444 buf.append("node [shape=box];\n")
445 buf.append("rankdir=BT;\n")
446 var hierarchy = mmodule.flatten_mclass_hierarchy
447 for mclass in hierarchy do
448 buf.append("\"{mclass}\" [label=\"{mclass}\"];\n")
449 for s in hierarchy[mclass].direct_greaters do
450 buf.append("\"{mclass}\" -> \"{s}\";\n")
451 end
452 end
453 buf.append("\}\n")
454 var f = new OFStream.open("class_hierarchy.dot")
455 f.write(buf.to_s)
456 f.close
457 end
458
459 # Create a dot file representing the classdef hierarchy of a model.
460 # For a simple user of the model, the classdef hierarchy is not really usefull, it is more an internal thing.
461 fun generate_classdef_hierarchy(model: Model)
462 do
463 var buf = new Buffer
464 buf.append("digraph \{\n")
465 buf.append("node [shape=box];\n")
466 buf.append("rankdir=BT;\n")
467 for mmodule in model.mmodules do
468 for mclassdef in mmodule.mclassdefs do
469 buf.append("\"{mclassdef} {mclassdef.bound_mtype}\" [label=\"{mclassdef.mmodule}\\n{mclassdef.bound_mtype}\"];\n")
470 for s in mclassdef.in_hierarchy.direct_greaters do
471 buf.append("\"{mclassdef} {mclassdef.bound_mtype}\" -> \"{s} {s.bound_mtype}\";\n")
472 end
473 end
474 end
475 buf.append("\}\n")
476 var f = new OFStream.open("classdef_hierarchy.dot")
477 f.write(buf.to_s)
478 f.close
479 end
480
481 fun generate_model_hyperdoc(model: Model)
482 do
483 var buf = new Buffer
484 buf.append("<html>\n<body>\n")
485 buf.append("<h1>Model</h1>\n")
486
487 buf.append("<h2>Modules</h2>\n")
488 for mmodule in model.mmodules do
489 buf.append("<h3 id='module-{mmodule}'>{mmodule}</h3>\n")
490 buf.append("<dl>\n")
491 buf.append("<dt>direct owner</dt>\n")
492 var own = mmodule.direct_owner
493 if own != null then buf.append("<dd>{linkto(own)}</dd>\n")
494 buf.append("<dt>nested</dt>\n")
495 for x in mmodule.in_nesting.direct_greaters do
496 buf.append("<dd>{linkto(x)}</dd>\n")
497 end
498 buf.append("<dt>direct import</dt>\n")
499 for x in mmodule.in_importation.direct_greaters do
500 buf.append("<dd>{linkto(x)}</dd>\n")
501 end
502 buf.append("<dt>direct clients</dt>\n")
503 for x in mmodule.in_importation.direct_smallers do
504 buf.append("<dd>{linkto(x)}</dd>\n")
505 end
506 buf.append("<dt>introduced classes</dt>\n")
507 for x in mmodule.mclassdefs do
508 if not x.is_intro then continue
509 buf.append("<dd>{linkto(x.mclass)} by {linkto(x)}</dd>\n")
510 end
511 buf.append("<dt>refined classes</dt>\n")
512 for x in mmodule.mclassdefs do
513 if x.is_intro then continue
514 buf.append("<dd>{linkto(x.mclass)} by {linkto(x)}</dd>\n")
515 end
516 buf.append("</dl>\n")
517 end
518 buf.append("<h2>Classes</h2>\n")
519 for mclass in model.mclasses do
520 buf.append("<h3 id='class-{mclass}'>{mclass}</h3>\n")
521 buf.append("<dl>\n")
522 buf.append("<dt>module of introduction</dt>\n")
523 buf.append("<dd>{linkto(mclass.intro_mmodule)}</dd>\n")
524 buf.append("<dt>class definitions</dt>\n")
525 for x in mclass.mclassdefs do
526 buf.append("<dd>{linkto(x)} in {linkto(x.mmodule)}</dd>\n")
527 end
528 buf.append("</dl>\n")
529 end
530 buf.append("<h2>Class Definitions</h2>\n")
531 for mclass in model.mclasses do
532 for mclassdef in mclass.mclassdefs do
533 buf.append("<h3 id='classdef-{mclassdef}'>{mclassdef}</h3>\n")
534 buf.append("<dl>\n")
535 buf.append("<dt>module</dt>\n")
536 buf.append("<dd>{linkto(mclassdef.mmodule)}</dd>\n")
537 buf.append("<dt>class</dt>\n")
538 buf.append("<dd>{linkto(mclassdef.mclass)}</dd>\n")
539 buf.append("<dt>direct refinements</dt>\n")
540 for x in mclassdef.in_hierarchy.direct_greaters do
541 if x.mclass != mclass then continue
542 buf.append("<dd>{linkto(x)} in {linkto(x.mmodule)}</dd>\n")
543 end
544 buf.append("<dt>direct refinemees</dt>\n")
545 for x in mclassdef.in_hierarchy.direct_smallers do
546 if x.mclass != mclass then continue
547 buf.append("<dd>{linkto(x)} in {linkto(x.mmodule)}</dd>\n")
548 end
549 buf.append("<dt>direct superclasses</dt>\n")
550 for x in mclassdef.supertypes do
551 buf.append("<dd>{linkto(x.mclass)} by {x}</dd>\n")
552 end
553 buf.append("<dt>introduced properties</dt>\n")
554 for x in mclassdef.mpropdefs do
555 if not x.is_intro then continue
556 buf.append("<dd>{linkto(x.mproperty)} by {linkto(x)}</dd>\n")
557 end
558 buf.append("<dt>redefined properties</dt>\n")
559 for x in mclassdef.mpropdefs do
560 if x.is_intro then continue
561 buf.append("<dd>{linkto(x.mproperty)} by {linkto(x)}</dd>\n")
562 end
563 buf.append("</dl>\n")
564 end
565 end
566 buf.append("<h2>Properties</h2>\n")
567 for mprop in model.mproperties do
568 buf.append("<h3 id='property-{mprop}'>{mprop}</h3>\n")
569 buf.append("<dl>\n")
570 buf.append("<dt>module of introdcution</dt>\n")
571 buf.append("<dd>{linkto(mprop.intro_mclassdef.mmodule)}</dd>\n")
572 buf.append("<dt>class of introduction</dt>\n")
573 buf.append("<dd>{linkto(mprop.intro_mclassdef.mclass)}</dd>\n")
574 buf.append("<dt>class definition of introduction</dt>\n")
575 buf.append("<dd>{linkto(mprop.intro_mclassdef)}</dd>\n")
576 buf.append("<dt>property definitions</dt>\n")
577 for x in mprop.mpropdefs do
578 buf.append("<dd>{linkto(x)} in {linkto(x.mclassdef)}</dd>\n")
579 end
580 buf.append("</dl>\n")
581 end
582 buf.append("<h2>Property Definitions</h2>\n")
583 for mprop in model.mproperties do
584 for mpropdef in mprop.mpropdefs do
585 buf.append("<h3 id='propdef-{mpropdef}'>{mpropdef}</h3>\n")
586 buf.append("<dl>\n")
587 buf.append("<dt>module</dt>\n")
588 buf.append("<dd>{linkto(mpropdef.mclassdef.mmodule)}</dd>\n")
589 buf.append("<dt>class</dt>\n")
590 buf.append("<dd>{linkto(mpropdef.mclassdef.mclass)}</dd>\n")
591 buf.append("<dt>class definition</dt>\n")
592 buf.append("<dd>{linkto(mpropdef.mclassdef)}</dd>\n")
593 buf.append("<dt>super definitions</dt>\n")
594 for x in mpropdef.mproperty.lookup_super_definitions(mpropdef.mclassdef.mmodule, mpropdef.mclassdef.bound_mtype) do
595 buf.append("<dd>{linkto(x)} in {linkto(x.mclassdef)}</dd>\n")
596 end
597 end
598 end
599 buf.append("</body></html>\n")
600 var f = new OFStream.open("model.html")
601 f.write(buf.to_s)
602 f.close
603 end
604
605 fun linkto(o: Object): String
606 do
607 if o isa MModule then
608 return "<a href='#module-{o}'>{o}</a>"
609 else if o isa MClass then
610 return "<a href='#class-{o}'>{o}</a>"
611 else if o isa MClassDef then
612 return "<a href='#classdef-{o}'>{o}</a>"
613 else if o isa MProperty then
614 return "<a href='#property-{o}'>{o}</a>"
615 else if o isa MPropDef then
616 return "<a href='#propdef-{o}'>{o}</a>"
617 else
618 print "cannot linkto {o.class_name}"
619 abort
620 end
621 end
622
623 fun runtime_type(modelbuilder: ModelBuilder, mainmodule: MModule)
624 do
625 var analysis = modelbuilder.do_rapid_type_analysis(mainmodule)
626
627 print "--- Type Analysis ---"
628 print "Number of live runtime types (instantied resolved type): {analysis.live_types.length}"
629 if analysis.live_types.length < 8 then print "\t{analysis.live_types.join(" ")}"
630 print "Number of live method definitions: {analysis.live_methoddefs.length}"
631 if analysis.live_methoddefs.length < 8 then print "\t{analysis.live_methoddefs.join(" ")}"
632 print "Number of live customized method definitions: {analysis.live_customized_methoddefs.length}"
633 if analysis.live_customized_methoddefs.length < 8 then print "\t{analysis.live_customized_methoddefs.join(" ")}"
634 print "Number of live runtime cast types (ie used in as and isa): {analysis.live_cast_types.length}"
635 if analysis.live_cast_types.length < 8 then print "\t{analysis.live_cast_types.join(" ")}"
636 end
637
638 # Create a tool context to handle options and paths
639 var toolcontext = new ToolContext
640 # We do not add other options, so process them now!
641 toolcontext.process_options
642
643 # We need a model to collect stufs
644 var model = new Model
645 # An a model builder to parse files
646 var modelbuilder = new ModelBuilder(model, toolcontext)
647
648 # Here we load an process all modules passed on the command line
649 var mmodules = modelbuilder.parse_and_build(toolcontext.option_context.rest)
650 modelbuilder.full_propdef_semantic_analysis
651
652 if mmodules.length == 0 then return
653
654 var mainmodule: MModule
655 if mmodules.length == 1 then
656 mainmodule = mmodules.first
657 else
658 # We need a main module, so we build it by importing all modules
659 mainmodule = new MModule(model, null, "<main>", new Location(null, 0, 0, 0, 0))
660 mainmodule.set_imported_mmodules(mmodules)
661 end
662
663 # Now, we just have to play with the model!
664 print "*** STATS ***"
665
666 print ""
667 compute_statistics(model)
668
669 print ""
670 visit_self(modelbuilder)
671
672 print ""
673 visit_nullable_sends(modelbuilder)
674
675 print ""
676 #visit_news(modelbuilder, mainmodule)
677
678 print ""
679 count_ntypes(modelbuilder)
680
681 generate_module_hierarchy(model)
682 generate_classdef_hierarchy(model)
683 generate_class_hierarchy(mainmodule)
684 generate_model_hyperdoc(model)
685
686 print ""
687 compute_tables(mainmodule)
688
689 print ""
690 runtime_type(modelbuilder, mainmodule)