1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2012 Jean Privat <jean@pryen.org>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # A program that collects various data about nit programs and libraries
19 # Collected datas are :
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
30 # The job of this visitor is to resolve all types found
31 class ATypeCounterVisitor
33 var modelbuilder
: ModelBuilder
34 var nclassdef
: AClassdef
36 var typecount
: HashMap[MType, Int]
39 # Get a new visitor on a classef to add type count in `typecount'.
40 init(modelbuilder
: ModelBuilder, nclassdef
: AClassdef, typecount
: HashMap[MType, Int])
42 self.modelbuilder
= modelbuilder
43 self.nclassdef
= nclassdef
44 self.typecount
= typecount
50 var mtype
= modelbuilder
.resolve_mtype
(self.nclassdef
, n
)
53 if self.typecount
.has_key
(mtype
) then
54 self.typecount
[mtype
] += 1
56 self.typecount
[mtype
] = 1
58 if not mtype
.need_anchor
then
59 var cldefs
= mtype
.collect_mclassdefs
(nclassdef
.mclassdef
.mmodule
)
69 var modelbuilder
: ModelBuilder
70 var nclassdef
: AClassdef
74 # Get a new visitor on a classef to add type count in `typecount'.
75 init(modelbuilder
: ModelBuilder, nclassdef
: AClassdef, news
: Set[MClass])
77 self.modelbuilder
= modelbuilder
78 self.nclassdef
= nclassdef
84 if n
isa ANewExpr then
85 var mtype
= modelbuilder
.resolve_mtype
(self.nclassdef
, n
.n_type
)
87 assert mtype
isa MClassType
88 self.news
.add
(mtype
.mclass
)
95 # Visit all `nmodules' of a modelbuilder and compute statistics on the usage of explicit static types
96 fun count_ntypes
(modelbuilder
: ModelBuilder)
98 # Count each occurence of a specific static type
99 var typecount
= new HashMap[MType, Int]
100 # Total number of explicit static types
103 # Visit all the source code to collect data
104 for nmodule
in modelbuilder
.nmodules
do
105 for nclassdef
in nmodule
.n_classdefs
do
106 var visitor
= new ATypeCounterVisitor(modelbuilder
, nclassdef
, typecount
)
107 visitor
.enter_visit
(nclassdef
)
108 total
+= visitor
.total
113 print
"--- Statistics of the explitic static types ---"
114 print
"Total number of explicit static types: {total}"
115 if total
== 0 then return
117 # types sorted by usage
118 var types
= typecount
.keys
.to_a
119 types
.sort
!cmp a
, b
= typecount
[a
] <=> typecount
[b
]
121 # Display most used types (ie the last of `types')
122 print
"Most used types: "
124 if types
.length
< min
then min
= types
.length
126 var t
= types
[types
.length-i-1
]
127 print
" {t}: {typecount[t]}"
130 # Compute the distribution of type usage
131 print
"Distribution of type usage:"
136 if typecount
[t
] > limit
then
137 print
" <={limit}: {count} ({div(count*100,types.length)}% of types; {div(sum*100,total)}% of usage)"
140 while typecount
[t
] > limit
do limit
= limit
* 2
145 print
" <={limit}: {count} ({div(count*100,types.length)}% of types; {div(sum*100,total)}% of usage)"
148 fun visit_news
(modelbuilder
: ModelBuilder, mainmodule
: MModule)
150 print
"--- Dead classes ---"
151 # Count each occurence of a specific static type
152 var news
= new HashSet[MClass]
154 # Visit all the source code to collect data
155 for nmodule
in modelbuilder
.nmodules
do
156 for nclassdef
in nmodule
.n_classdefs
do
157 var visitor
= new ANewVisitor(modelbuilder
, nclassdef
, news
)
158 visitor
.enter_visit
(nclassdef
)
162 for c
in modelbuilder
.model
.mclasses
do
163 if c
.kind
== concrete_kind
and not news
.has
(c
) then
164 print
"{c.full_name}"
168 var hier
= mainmodule
.flatten_mclass_hierarchy
170 if c
.kind
!= abstract_kind
and not (c
.kind
== concrete_kind
and not news
.has
(c
)) then continue
172 for sup
in hier
[c
].greaters
do
173 for cd
in sup
.mclassdefs
do
174 for p
in cd
.intro_mproperties
do
175 if p
isa MAttribute then
176 continue label classes
181 print
"no attributes: {c.full_name}"
186 # Compute general statistics on a model
187 fun compute_statistics
(model
: Model)
189 print
"--- Statistics of the model ---"
190 var nbmod
= model
.mmodules
.length
191 print
"Number of modules: {nbmod}"
195 var nbcla
= model
.mclasses
.length
196 var nbcladef
= model
.mclassdef_hierarchy
.length
197 print
"Number of classes: {nbcla}"
199 # determine the distribution of:
200 # * class kinds (interface, abstract class, etc.)
201 # * refinex classes (vs. unrefined ones)
202 var kinds
= new HashMap[MClassKind, Int]
204 for c
in model
.mclasses
do
205 if kinds
.has_key
(c
.kind
) then
210 if c
.mclassdefs
.length
> 1 then
215 print
" Number of {k} kind: {v} ({div(v*100,nbcla)}%)"
221 print
"Nomber of class definitions: {nbcladef}"
222 print
"Number of refined classes: {refined} ({div(refined*100,nbcla)}%)"
223 print
"Average number of class refinments by classes: {div(nbcladef-nbcla,nbcla)}"
224 print
"Average number of class refinments by refined classes: {div(nbcladef-nbcla,refined)}"
228 var nbprop
= model
.mproperties
.length
229 print
"Number of properties: {model.mproperties.length}"
232 # Compute class tables for the classes of the program main
233 fun compute_tables
(main
: MModule)
235 var model
= main
.model
237 var nc
= 0 # Number of runtime classes
238 var nl
= 0 # Number of usages of class definitions (a class definition can be used more than once)
239 var nhp
= 0 # Number of usages of properties (a property can be used more than once)
240 var npas
= 0 # Number of usages of properties without lookup (easy easy case, easier that CHA)
242 # Collect the full class hierarchy
243 var hier
= main
.flatten_mclass_hierarchy
245 # Skip classes without direct instances
246 if c
.kind
== interface_kind
or c
.kind
== abstract_kind
then continue
250 # Now, we need to collect all properties defined/inherited/imported
251 # So, visit all definitions of all super-classes
252 for sup
in hier
[c
].greaters
do
253 for cd
in sup
.mclassdefs
do
256 # Now, search properties introduced
257 for p
in cd
.intro_mproperties
do
260 # Select property definition
261 if p
.mpropdefs
.length
== 1 then
264 var sels
= p
.lookup_definitions
(main
, c
.mclassdefs
.first
.bound_mtype
)
265 if sels
.length
> 1 then
266 print
"conflict for {p.full_name} in class {c.full_name}: {sels.join(", ")}"
267 else if sels
.is_empty
then
268 print
"ERROR: no property for {p.full_name} in class {c.full_name}!"
276 print
"--- Construction of tables ---"
277 print
"Number of runtime classes: {nc} (excluding interfaces and abstract classes)"
278 print
"Average number of composing class definition by runtime class: {div(nl,nc)}"
279 print
"Total size of tables (classes and instances): {nhp} (not including stuff like info for subtyping or call-next-method)"
280 print
"Average size of table by runtime class: {div(nhp,nc)}"
281 print
"Values never redefined: {npas} ({div(npas*100,nhp)}%)"
284 # Helper function to display n/d and handle division by 0
285 fun div
(n
: Int, d
: Int): String
287 if d
== 0 then return "na"
288 return ((100*n
/d
).to_f
/100.0).to_precision
(2)
291 # Create a dot file representing the module hierarchy of a model.
292 # Importation relation is represented with arrow
293 # Nesting relation is represented with nested boxes
294 fun generate_module_hierarchy
(model
: Model)
297 buf
.append
("digraph \{\n")
298 buf
.append
("node [shape=box];\n")
299 buf
.append
("rankdir=BT;\n")
300 for mmodule
in model
.mmodules
do
301 if mmodule
.direct_owner
== null then
302 generate_module_hierarchy_for
(mmodule
, buf
)
305 for mmodule
in model
.mmodules
do
306 for s
in mmodule
.in_importation
.direct_greaters
do
307 buf
.append
("\"{mmodule}\
" -> \"{s}\
";\n")
311 var f
= new OFStream.open
("module_hierarchy.dot")
316 # Helper function for `generate_module_hierarchy'.
317 # Create graphviz nodes for the module and recusrively for its nested modules
318 private fun generate_module_hierarchy_for
(mmodule
: MModule, buf
: Buffer)
320 if mmodule
.in_nesting
.direct_greaters
.is_empty
then
321 buf
.append
("\"{mmodule.name}\
";\n")
323 buf
.append
("subgraph \"cluster_
{mmodule.name}\
" \{label=\"\
"\n")
324 buf
.append
("\"{mmodule.name}\
";\n")
325 for s
in mmodule
.in_nesting
.direct_greaters
do
326 generate_module_hierarchy_for
(s
, buf
)
332 # Create a dot file representing the class hierarchy of a model.
333 fun generate_class_hierarchy
(mmodule
: MModule)
336 buf
.append
("digraph \{\n")
337 buf
.append
("node [shape=box];\n")
338 buf
.append
("rankdir=BT;\n")
339 var hierarchy
= mmodule
.flatten_mclass_hierarchy
340 for mclass
in hierarchy
do
341 buf
.append
("\"{mclass}\
" [label=\"{mclass}\
"];\n")
342 for s
in hierarchy
[mclass
].direct_greaters
do
343 buf
.append
("\"{mclass}\
" -> \"{s}\
";\n")
347 var f
= new OFStream.open
("class_hierarchy.dot")
352 # Create a dot file representing the classdef hierarchy of a model.
353 # For a simple user of the model, the classdef hierarchy is not really usefull, it is more an internal thing.
354 fun generate_classdef_hierarchy
(model
: Model)
357 buf
.append
("digraph \{\n")
358 buf
.append
("node [shape=box];\n")
359 buf
.append
("rankdir=BT;\n")
360 for mmodule
in model
.mmodules
do
361 for mclassdef
in mmodule
.mclassdefs
do
362 buf
.append
("\"{mclassdef} {mclassdef.bound_mtype}\
" [label=\"{mclassdef.mmodule}\\n
{mclassdef.bound_mtype}\
"];\n")
363 for s
in mclassdef
.in_hierarchy
.direct_greaters
do
364 buf
.append
("\"{mclassdef} {mclassdef.bound_mtype}\
" -> \"{s} {s.bound_mtype}\
";\n")
369 var f
= new OFStream.open
("classdef_hierarchy.dot")
374 fun generate_model_hyperdoc
(model
: Model)
377 buf
.append
("<html>\n<body>\n")
378 buf
.append
("<h1>Model</h1>\n")
380 buf
.append
("<h2>Modules</h2>\n")
381 for mmodule
in model
.mmodules
do
382 buf
.append
("<h3 id='module-{mmodule}'>{mmodule}</h3>\n")
384 buf
.append
("<dt>direct owner</dt>\n")
385 var own
= mmodule
.direct_owner
386 if own
!= null then buf
.append
("<dd>{linkto(own)}</dd>\n")
387 buf
.append
("<dt>nested</dt>\n")
388 for x
in mmodule
.in_nesting
.direct_greaters
do
389 buf
.append
("<dd>{linkto(x)}</dd>\n")
391 buf
.append
("<dt>direct import</dt>\n")
392 for x
in mmodule
.in_importation
.direct_greaters
do
393 buf
.append
("<dd>{linkto(x)}</dd>\n")
395 buf
.append
("<dt>direct clients</dt>\n")
396 for x
in mmodule
.in_importation
.direct_smallers
do
397 buf
.append
("<dd>{linkto(x)}</dd>\n")
399 buf
.append
("<dt>introduced classes</dt>\n")
400 for x
in mmodule
.mclassdefs
do
401 if not x
.is_intro
then continue
402 buf
.append
("<dd>{linkto(x.mclass)} by {linkto(x)}</dd>\n")
404 buf
.append
("<dt>refined classes</dt>\n")
405 for x
in mmodule
.mclassdefs
do
406 if x
.is_intro
then continue
407 buf
.append
("<dd>{linkto(x.mclass)} by {linkto(x)}</dd>\n")
409 buf
.append
("</dl>\n")
411 buf
.append
("<h2>Classes</h2>\n")
412 for mclass
in model
.mclasses
do
413 buf
.append
("<h3 id='class-{mclass}'>{mclass}</h3>\n")
415 buf
.append
("<dt>module of introduction</dt>\n")
416 buf
.append
("<dd>{linkto(mclass.intro_mmodule)}</dd>\n")
417 buf
.append
("<dt>class definitions</dt>\n")
418 for x
in mclass
.mclassdefs
do
419 buf
.append
("<dd>{linkto(x)} in {linkto(x.mmodule)}</dd>\n")
421 buf
.append
("</dl>\n")
423 buf
.append
("<h2>Class Definitions</h2>\n")
424 for mclass
in model
.mclasses
do
425 for mclassdef
in mclass
.mclassdefs
do
426 buf
.append
("<h3 id='classdef-{mclassdef}'>{mclassdef}</h3>\n")
428 buf
.append
("<dt>module</dt>\n")
429 buf
.append
("<dd>{linkto(mclassdef.mmodule)}</dd>\n")
430 buf
.append
("<dt>class</dt>\n")
431 buf
.append
("<dd>{linkto(mclassdef.mclass)}</dd>\n")
432 buf
.append
("<dt>direct refinements</dt>\n")
433 for x
in mclassdef
.in_hierarchy
.direct_greaters
do
434 if x
.mclass
!= mclass
then continue
435 buf
.append
("<dd>{linkto(x)} in {linkto(x.mmodule)}</dd>\n")
437 buf
.append
("<dt>direct refinemees</dt>\n")
438 for x
in mclassdef
.in_hierarchy
.direct_smallers
do
439 if x
.mclass
!= mclass
then continue
440 buf
.append
("<dd>{linkto(x)} in {linkto(x.mmodule)}</dd>\n")
442 buf
.append
("<dt>direct superclasses</dt>\n")
443 for x
in mclassdef
.supertypes
do
444 buf
.append
("<dd>{linkto(x.mclass)} by {x}</dd>\n")
446 buf
.append
("<dt>introduced properties</dt>\n")
447 for x
in mclassdef
.mpropdefs
do
448 if not x
.is_intro
then continue
449 buf
.append
("<dd>{linkto(x.mproperty)} by {linkto(x)}</dd>\n")
451 buf
.append
("<dt>redefined properties</dt>\n")
452 for x
in mclassdef
.mpropdefs
do
453 if x
.is_intro
then continue
454 buf
.append
("<dd>{linkto(x.mproperty)} by {linkto(x)}</dd>\n")
456 buf
.append
("</dl>\n")
459 buf
.append
("<h2>Properties</h2>\n")
460 for mprop
in model
.mproperties
do
461 buf
.append
("<h3 id='property-{mprop}'>{mprop}</h3>\n")
463 buf
.append
("<dt>module of introdcution</dt>\n")
464 buf
.append
("<dd>{linkto(mprop.intro_mclassdef.mmodule)}</dd>\n")
465 buf
.append
("<dt>class of introduction</dt>\n")
466 buf
.append
("<dd>{linkto(mprop.intro_mclassdef.mclass)}</dd>\n")
467 buf
.append
("<dt>class definition of introduction</dt>\n")
468 buf
.append
("<dd>{linkto(mprop.intro_mclassdef)}</dd>\n")
469 buf
.append
("<dt>property definitions</dt>\n")
470 for x
in mprop
.mpropdefs
do
471 buf
.append
("<dd>{linkto(x)} in {linkto(x.mclassdef)}</dd>\n")
473 buf
.append
("</dl>\n")
475 buf
.append
("<h2>Property Definitions</h2>\n")
476 for mprop
in model
.mproperties
do
477 for mpropdef
in mprop
.mpropdefs
do
478 buf
.append
("<h3 id='propdef-{mpropdef}'>{mpropdef}</h3>\n")
480 buf
.append
("<dt>module</dt>\n")
481 buf
.append
("<dd>{linkto(mpropdef.mclassdef.mmodule)}</dd>\n")
482 buf
.append
("<dt>class</dt>\n")
483 buf
.append
("<dd>{linkto(mpropdef.mclassdef.mclass)}</dd>\n")
484 buf
.append
("<dt>class definition</dt>\n")
485 buf
.append
("<dd>{linkto(mpropdef.mclassdef)}</dd>\n")
486 buf
.append
("<dt>super definitions</dt>\n")
487 for x
in mpropdef
.mproperty
.lookup_super_definitions
(mpropdef
.mclassdef
.mmodule
, mpropdef
.mclassdef
.bound_mtype
) do
488 buf
.append
("<dd>{linkto(x)} in {linkto(x.mclassdef)}</dd>\n")
492 buf
.append
("</body></html>\n")
493 var f
= new OFStream.open
("model.html")
498 fun linkto
(o
: Object): String
500 if o
isa MModule then
501 return "<a href='#module-{o}'>{o}</a>"
502 else if o
isa MClass then
503 return "<a href='#class-{o}'>{o}</a>"
504 else if o
isa MClassDef then
505 return "<a href='#classdef-{o}'>{o}</a>"
506 else if o
isa MProperty then
507 return "<a href='#property-{o}'>{o}</a>"
508 else if o
isa MPropDef then
509 return "<a href='#propdef-{o}'>{o}</a>"
511 print
"cannot linkto {o.class_name}"
516 fun runtime_type
(modelbuilder
: ModelBuilder, mainmodule
: MModule)
518 var analysis
= modelbuilder
.do_runtime_type
(mainmodule
)
520 print
"--- Type Analysis ---"
521 print
"Number of live runtime types (instantied resolved type): {analysis.live_types.length}"
522 print
"Number of live polymorphic method: {analysis.polymorphic_methods.length}"
523 print
"Number of live method definitions: {analysis.live_methoddefs.length}"
524 print
"Number of live runtime method definitions (with customization): {analysis.runtime_methods.length}"
525 print
"Number of live runtime cast types (ie used in as and isa): {analysis.live_cast_types.length}"
527 for mprop
in modelbuilder
.model
.mproperties
do
528 if not mprop
isa MMethod then continue
529 if analysis
.polymorphic_methods
.has
(mprop
) then continue
530 for methoddef
in mprop
.mpropdefs
do
531 if analysis
.live_methoddefs
.has
(methoddef
) then continue label l
533 #print " {mprop.full_name} is dead"
537 # Create a tool context to handle options and paths
538 var toolcontext
= new ToolContext
539 # We do not add other options, so process them now!
540 toolcontext
.process_options
542 # We need a model to collect stufs
543 var model
= new Model
544 # An a model builder to parse files
545 var modelbuilder
= new ModelBuilder(model
, toolcontext
)
547 # Here we load an process all modules passed on the command line
548 var mmodules
= modelbuilder
.parse_and_build
(toolcontext
.option_context
.rest
)
550 if mmodules
.length
== 0 then return
552 var mainmodule
: MModule
553 if mmodules
.length
== 1 then
554 mainmodule
= mmodules
.first
556 # We need a main module, so we build it by importing all modules
557 mainmodule
= new MModule(model
, null, "<main>", new Location(null, 0, 0, 0, 0))
558 mainmodule
.set_imported_mmodules
(mmodules
)
561 # Now, we just have to play with the model!
562 print
"*** STATS ***"
565 compute_statistics
(model
)
568 #visit_news(modelbuilder, mainmodule)
571 count_ntypes
(modelbuilder
)
573 generate_module_hierarchy
(model
)
574 generate_classdef_hierarchy
(model
)
575 generate_class_hierarchy
(mainmodule
)
576 generate_model_hyperdoc
(model
)
579 compute_tables
(mainmodule
)
582 modelbuilder
.full_propdef_semantic_analysis
583 runtime_type
(modelbuilder
, mainmodule
)