nitmetrics: add metrics about genericity inheritance
authorAlexandre Terrasa <alexandre@moz-code.org>
Wed, 12 Dec 2012 20:51:19 +0000 (15:51 -0500)
committerJean Privat <jean@pryen.org>
Thu, 24 Jan 2013 22:38:06 +0000 (17:38 -0500)
Signed-off-by: Alexandre Terrasa <alexandre@moz-code.org>

src/metrics/genericity_metrics.nit
src/metrics/inheritance_metrics.nit
src/metrics/metrics_base.nit
src/metrics/rta_metrics.nit

index d24ac99..60254ce 100644 (file)
@@ -232,6 +232,105 @@ do
                mclass.compute_class_genericity_metrics(model)
        end
 
+       # global summary metrics
+       var nc: Int = 0                         # (NC)  Number of Classes
+       var ni: Int = 0                         # (NI)  Number of Interfaces
+       var ncud: Int = 0                       # (NCUD) Number of Classes User Defined
+       var niud: Int = 0                       # (NIUD) Number of Interfaces User Defined
+
+       # global summary inheritance metrics
+       var dit = ""                            # (DIT) Global Depth in Inheritance Tree
+       var dui = ""                            # (DUI) Proportion of types that either implement an interface or extend another type other than Object
+       var ccdui = ""                          # (CCDUI) Proportion of classes that extend some other class.
+       var cidui = ""                          # (CIDUI) Proportion of classes that implement some other interface.
+       var iidui = ""                          # (IIDUI) Proportion of interfaces that extend some other interface.
+       var inhf = ""                           # (IF) Proportion of types Inherited From, that is, those types that are either extended or implemented
+       var ccif = ""                           # (CCIF) Proportion of classes extended by some other class.
+       var icif = ""                           # (ICIF) Proportion of interfaces implemented by some other class.
+       var iiif = ""                           # (IIIF) Proportion of interfaces extended by some other interface.
+
+       var ditsum = 0
+       var dui_count = 0
+       var ccdui_count = 0
+       var cidui_count = 0
+       var iidui_count = 0
+       var if_count = 0
+       var ccif_count = 0
+       var icif_count = 0
+       var iiif_count = 0
+
+       # (UD -> UD) User-defined summary inheritance metrics
+       var ditud = ""
+       var uddui = ""                          # (UDDUI) Proportion user-defined of types that either implement an interface or extend another type
+       var udccdui = ""                        # (UDCCDUI) Proportion of user-defined classes that extend some other class.
+       var udcidui = ""                        # (UDCIDUI) Proportion of user-defined classes that implement some other interface.
+       var udiidui = ""                        # (UDIIDUI) Proportion of user-defined interfaces that extend some other interface.
+       var udinhf = ""                         # (UDIF) Proportion of UD types Inherited From, that is, those types that are either extended or implemented
+       var udccif = ""                         # (UDCCIF) Proportion of UD classes extended by some other class.
+       var udicif = ""                         # (UDICIF) Proportion of UD interfaces implemented by some other class.
+       var udiiif = ""                         # (UDIIIF) Proportion of UD interfaces extended by some other interface.
+
+
+
+       var ditudsum = 0
+       var uddui_count = 0
+       var udccdui_count = 0
+       var udcidui_count = 0
+       var udiidui_count = 0
+       var udif_count = 0
+       var udccif_count = 0
+       var udicif_count = 0
+       var udiiif_count = 0
+
+       var nb_gen = 0
+       for mclass in model.mclasses do
+               if not mclass.arity > 0 then continue
+               ditsum += mclass.ditg
+               ditudsum += mclass.ditgud
+
+               # * -> *
+               if mclass.is_dui_eligible then dui_count += 1
+               if mclass.is_ccdui_eligible then ccdui_count += 1
+               if mclass.is_cidui_eligible then cidui_count += 1
+               if mclass.is_iidui_eligible then iidui_count += 1
+               if mclass.is_if_eligible(model) then if_count += 1
+               if mclass.is_ccif_eligible(model) then ccif_count += 1
+               if mclass.is_icif_eligible(model) then icif_count += 1
+               if mclass.is_iiif_eligible(model) then iiif_count += 1
+
+               # UD -> *
+               if mclass.is_uddui_eligible then uddui_count += 1
+               if mclass.is_udccdui_eligible then udccdui_count += 1
+               if mclass.is_udcidui_eligible then udcidui_count += 1
+               if mclass.is_udiidui_eligible then udiidui_count += 1
+               if mclass.is_udif_eligible(model) then udif_count += 1
+               if mclass.is_udccif_eligible(model) then udccif_count += 1
+               if mclass.is_udicif_eligible(model) then udicif_count += 1
+               if mclass.is_udiiif_eligible(model) then udiiif_count += 1
+
+       end
+
+       # * -> *
+       dit = div(ditsum, model.mclasses.length)
+       ditud = div(ditudsum, ncud + niud)
+       dui = div(dui_count * 100, model.mclasses.length)
+       ccdui = div(ccdui_count * 100, nc)
+       cidui = div(cidui_count * 100, nc)
+       iidui = div(iidui_count * 100, ni)
+       inhf = div(if_count * 100, nc + ni)
+       ccif = div(ccif_count * 100, nc)
+       icif = div(icif_count * 100, ni)
+       iiif = div(iiif_count * 100, ni)
+
+       uddui = div(uddui_count * 100, ncud + niud)
+       udccdui = div(udccdui_count * 100, ncud)
+       udcidui = div(udcidui_count * 100, ncud)
+       udiidui = div(udiidui_count * 100, niud)
+       udinhf = div(if_count * 100, ncud + niud)
+       udccif = div(ccif_count * 100, ncud)
+       udicif = div(icif_count * 100, niud)
+       udiiif = div(iiif_count * 100, niud)
+
        #TODO Comment monitorer l'évolution de la signature générique ? C[T, U] <: B <: A[V]
 
        # CSV generation
@@ -239,6 +338,8 @@ do
                # inheritance metrics
                var inheritanceCSV = new CSVDocument(toolcontext.output_dir.join_path("inheritance_genericity_metrics.csv"))
                inheritanceCSV.set_header("scope", "GDIT", "GDUI", "GCCDUI", "GCIDUI", "GIIDUI", "GIF", "GCCIF", "GICIF", "GIIIF")
+               inheritanceCSV.add_line("global", dit, dui, ccdui, cidui, iidui, inhf, ccif, icif, iiif)
+               inheritanceCSV.add_line("UD -> *", ditud, uddui, udccdui, udcidui, udiidui, udinhf, udccif, udicif, udiiif)
                for m in model.mmodules do
                        if m.intro_mclasses.is_empty and m.in_nesting.greaters.length == 1 then continue
                        inheritanceCSV.add_line(m.name, m.gdit, m.gdui, m.gccdui, m.gcidui, m.giidui, m.ginhf, m.gccif, m.gicif, m.giiif)
index 99408d4..951d164 100644 (file)
@@ -21,36 +21,6 @@ import model
 private import metrics_base
 
 redef class Model
-
-       # List of modules in std lib
-       # FIXME this is quite ugly, find a dynamic way...
-       fun std_modules: Set[String] do
-               if self.std_modules_cache == null then
-                       self.std_modules_cache = new HashSet[String]
-                       self.std_modules_cache.add("collection")
-                       self.std_modules_cache.add("abstract_collection")
-                       self.std_modules_cache.add("array")
-                       self.std_modules_cache.add("hash_collection")
-                       self.std_modules_cache.add("list")
-                       self.std_modules_cache.add("range")
-                       self.std_modules_cache.add("sorter")
-                       self.std_modules_cache.add("environ")
-                       self.std_modules_cache.add("exec")
-                       self.std_modules_cache.add("file")
-                       self.std_modules_cache.add("gc")
-                       self.std_modules_cache.add("hash")
-                       self.std_modules_cache.add("kernel")
-                       self.std_modules_cache.add("math")
-                       self.std_modules_cache.add("standard")
-                       self.std_modules_cache.add("stream")
-                       self.std_modules_cache.add("string")
-                       self.std_modules_cache.add("string_search")
-                       self.std_modules_cache.add("time")
-               end
-               return self.std_modules_cache.as(not null)
-       end
-       private var std_modules_cache: nullable Set[String]
-
        # Extract the subset of classes from a set of mclass
        fun extract_classes(mclasses: Collection[MClass]): Set[MClass] do
                var lst = new HashSet[MClass]
@@ -179,22 +149,6 @@ redef class MClass
                self.ditiud = ud_interface_path_to_object.length
        end
 
-       fun is_class: Bool do
-               return self.kind == concrete_kind or self.kind == abstract_kind
-       end
-
-       fun is_interface: Bool do
-               return self.kind == interface_kind
-       end
-
-       fun is_abstract: Bool do
-               return self.kind == abstract_kind
-       end
-
-       fun is_user_defined: Bool do
-               return self.intro_mmodule.is_user_defined
-       end
-
        # Get parents of the class (direct super classes only)
        fun parents: Set[MClass] do
                var lst = new HashSet[MClass]
@@ -683,15 +637,15 @@ redef class MModule
                var ccif_count = 0
                var icif_count = 0
                var iiif_count = 0
-
+               var count = 0
                for mmodule in self.in_nesting.greaters do
                        for mclass in mmodule.intro_mclasses do
+                               count += 1
                                if mclass.is_class then nc += 1
                                if mclass.is_class and mclass.arity > 0 then ngc += 1
                                if mclass.is_class and mclass.is_abstract then nac += 1
                                if mclass.is_interface then ni += 1
                                if mclass.is_interface and mclass.arity > 0 then ngi += 1
-
                                ditsum += mclass.path_to_object.length
                                if mclass.is_dui_eligible then dui_count += 1
                                if mclass.is_ccdui_eligible then ccdui_count += 1
@@ -705,20 +659,16 @@ redef class MModule
                end
 
                self.nm = self.in_nesting.greaters.length
-               dit = div(ditsum, nc + ni)
-               dui = div(dui_count * 100, nc + ni)
+               dit = div(ditsum, count)
+               dui = div(dui_count * 100, count)
                ccdui = div(ccdui_count * 100, nc)
                cidui = div(cidui_count * 100, nc)
                iidui = div(iidui_count * 100, ni)
-               inhf = div(if_count * 100, nc + ni)
+               inhf = div(if_count * 100, count)
                ccif = div(ccif_count * 100, nc)
                icif = div(icif_count * 100, ni)
                iiif = div(iiif_count * 100, ni)
        end
-
-       fun is_user_defined: Bool do
-               return not self.model.std_modules.has(self.name)
-       end
 end
 
 # Print inheritance usage metrics
@@ -796,6 +746,7 @@ do
        var udiiduisl = ""                      # (UDIIDUISL) Proportion of user-defined interfaces that extend some other SL interface.
 
        # (UD -> UD) User-defined summary inheritance metrics
+       var ditud = ""
        var udduiud = ""                        # (UDDUIUD) Proportion user-defined of types that either implement an interface or extend another type user-defined
        var udccduiud = ""                      # (UDCCDUIUD) Proportion of user-defined classes that extend some other user-defined class.
        var udciduiud = ""                      # (UDCIDUIUD) Proportion of user-defined classes that implement some other user-defined interface.
@@ -843,6 +794,8 @@ do
 
        # * -> *
        var ditsum = 0
+       var ditudsum = 0
+
        var dui_count = 0
        var ccdui_count = 0
        var cidui_count = 0
@@ -902,6 +855,7 @@ do
 
        for mclass in model.mclasses do
                ditsum += mclass.dit
+               ditudsum += mclass.ditud
 
                # * -> *
                if mclass.is_dui_eligible then dui_count += 1
@@ -964,7 +918,8 @@ do
 
        # * -> *
        dit = div(ditsum, model.mclasses.length)
-       dui = div(dui_count * 100, nc + ni)
+       ditud = div(ditudsum, ncud + niud)
+       dui = div(dui_count * 100, model.mclasses.length)
        ccdui = div(ccdui_count * 100, nc)
        cidui = div(cidui_count * 100, nc)
        iidui = div(iidui_count * 100, ni)
@@ -1043,7 +998,7 @@ do
                inheritanceCSV.add_line("SL -> UD", "", 0, 0, 0, 0, slinhfud, slccifud, slicifud, sliiifud)
                inheritanceCSV.add_line("UD -> *", "", uddui, udccdui, udcidui, udiidui, udinhf, udccif, udicif, udiiif)
                inheritanceCSV.add_line("UD -> SL", "", udduisl, udccduisl, udciduisl, udiiduisl, 0, 0, 0, 0)
-               inheritanceCSV.add_line("UD -> UD", "", udduiud, udccduiud, udciduiud, udiiduiud, udinhfud, udccifud, udicifud, udiiifud)
+               inheritanceCSV.add_line("UD -> UD", ditud, udduiud, udccduiud, udciduiud, udiiduiud, udinhfud, udccifud, udicifud, udiiifud)
                for m in model.mmodules do
                        if m.intro_mclasses.is_empty and m.in_nesting.greaters.length == 1 then continue
                        inheritanceCSV.add_line(m.name, m.dit, m.dui, m.ccdui, m.cidui, m.iidui, m.inhf, m.ccif, m.icif, m.iiif)
index b691f26..88ed133 100644 (file)
@@ -77,6 +77,66 @@ redef class ToolContext
        end
 end
 
+redef class Model
+
+       # List of modules in std lib
+       # FIXME this is quite ugly, find a dynamic way...
+       fun std_modules: Set[String] do
+               if self.std_modules_cache == null then
+                       self.std_modules_cache = new HashSet[String]
+                       self.std_modules_cache.add("collection")
+                       self.std_modules_cache.add("abstract_collection")
+                       self.std_modules_cache.add("array")
+                       self.std_modules_cache.add("hash_collection")
+                       self.std_modules_cache.add("list")
+                       self.std_modules_cache.add("range")
+                       self.std_modules_cache.add("sorter")
+                       self.std_modules_cache.add("environ")
+                       self.std_modules_cache.add("exec")
+                       self.std_modules_cache.add("file")
+                       self.std_modules_cache.add("gc")
+                       self.std_modules_cache.add("hash")
+                       self.std_modules_cache.add("kernel")
+                       self.std_modules_cache.add("math")
+                       self.std_modules_cache.add("standard")
+                       self.std_modules_cache.add("stream")
+                       self.std_modules_cache.add("string")
+                       self.std_modules_cache.add("string_search")
+                       self.std_modules_cache.add("time")
+               end
+               return self.std_modules_cache.as(not null)
+       end
+       private var std_modules_cache: nullable Set[String]
+end
+
+redef class MClass
+       fun is_class: Bool do
+               return self.kind == concrete_kind or self.kind == abstract_kind
+       end
+
+       fun is_interface: Bool do
+               return self.kind == interface_kind
+       end
+
+       fun is_enum: Bool do
+               return self.kind == enum_kind
+       end
+
+       fun is_abstract: Bool do
+               return self.kind == abstract_kind
+       end
+
+       fun is_user_defined: Bool do
+               return self.intro_mmodule.is_user_defined
+       end
+end
+
+redef class MModule
+       fun is_user_defined: Bool do
+               return not self.model.std_modules.has(self.name)
+       end
+end
+
 # A counter counts occurence of things
 # Use this instead of a HashMap[E, Int]
 class Counter[E: Object]
index cebf766..7ba1899 100644 (file)
@@ -19,12 +19,144 @@ module rta_metrics
 
 import modelbuilder
 private import rapid_type_analysis
+private import metrics_base
+
+redef class RapidTypeAnalysis
+       redef fun add_type(mtype)
+       do
+               mtype.nlvt += 1
+               mtype.mclass.nlvt += 1
+               mtype.mclass.live_types.add(mtype)
+               super(mtype)
+       end
+
+       redef fun add_cast_type(mtype)
+       do
+               mtype.nlct += 1
+               mtype.mclass.nlct += 1
+               mtype.mclass.cast_types.add(mtype)
+               super(mtype)
+       end
+end
+
+redef class MType
+       var nlvt: Int = 0
+       var nlct: Int = 0
+
+       fun is_user_defined: Bool do
+               var mtype = self
+               if mtype isa MNullableType then mtype = mtype.mtype
+               return self.as(MClassType).mclass.is_user_defined
+       end
+
+       fun get_depth: Int do
+               var mtype = self
+               if mtype isa MNullableType then mtype = mtype.mtype
+               if not mtype isa MGenericType then return 0
+
+               var depth = 0
+               for ft in mtype.arguments do
+                       if ft.get_depth > depth then depth = ft.get_depth
+               end
+               return depth + 1
+       end
+end
+
+redef class MClass
+       var nlvt: Int = 0
+       var nlct: Int = 0
+       var live_types: Set[MType] = new HashSet[MType]
+       var cast_types: Set[MType] = new HashSet[MType]
+end
 
 # Run a runtime type analysis and print metrics
 fun compute_rta_metrics(modelbuilder: ModelBuilder, mainmodule: MModule)
 do
        var analysis = modelbuilder.do_rapid_type_analysis(mainmodule)
 
+       var nlvt = 0            # NLVT Number of Live Type
+       var nlvtg = 0           # NLVTG Number of Generic Live Type
+       var nlvtslud = 0        # NLCTSLUD Number of Live Type defined in SL and init in UD
+       var nlvtgslud = 0       # NLVTGSLUD Number of Generic Live Type defined in SL and init in UD
+       var nlvtudud = 0        # NLVTUDUD Number of Live Type defined in UD and init in UD
+       var nlvtgudud = 0       # NLVTGUDUD Number of Generic Live Type defined in UD and init in UD
+
+       var nlct = 0            # NLCT Number of Live Cast Type
+       var nlctg = 0           # NLCTG Number of Generic Live Cast Type
+       var nlctslud = 0        # NLCTSLUD Number of Live Cast Type defined in SL and init in UD
+       var nlctgslud = 0       # NLCTGSLUD Number of Generic Live Cast Type defined in SL and init in UD
+       var nlctudud = 0        # NLCTUDUD Number of Live Cast Type defined in UD and init in UD
+       var nlctgudud = 0       # NLCTGUDUD Number of Generic Live Cast Type defined in UD and init in UD
+
+       var mtypes = new HashSet[MClassType]
+
+       for mtype in analysis.live_types do
+               mtypes.add(mtype)
+               nlvt += 1
+               if mtype isa MGenericType then nlvtg += 1
+               if mtype.is_user_defined then
+                       nlvtudud += 1
+                       if mtype isa MGenericType then nlvtgudud += 1
+               else
+                       nlvtslud += 1
+                       if mtype isa MGenericType then nlvtgslud += 1
+               end
+       end
+
+       for mtype in analysis.live_cast_types do
+               mtypes.add(mtype)
+               nlct += 1
+               if mtype isa MGenericType then nlctg += 1
+               if mtype.is_user_defined then
+                       nlctudud += 1
+                       if mtype isa MGenericType then nlctgudud += 1
+               else
+                       nlctslud += 1
+                       if mtype isa MGenericType then nlctgslud += 1
+               end
+       end
+
+       # CSV generation
+       if modelbuilder.toolcontext.opt_generate_csv.value then
+               var summaryCSV = new CSVDocument(modelbuilder.toolcontext.output_dir.join_path("rta_sum_metrics.csv"))
+               summaryCSV.set_header("scope", "NLVT", "NLVTG", "NLCT", "NLVCTG")
+               summaryCSV.add_line("global", nlvt, nlvtg, nlct, nlctg)
+               summaryCSV.add_line("SLUD", nlvtslud, nlvtgslud, nlctslud, nlctgslud)
+               summaryCSV.add_line("UDUD", nlvtudud, nlvtgudud, nlctudud, nlctgudud)
+               summaryCSV.save
+
+               var scalarCSV = new CSVDocument(modelbuilder.toolcontext.output_dir.join_path("rta_scalar_metrics.csv"))
+               var udscalarCSV = new CSVDocument(modelbuilder.toolcontext.output_dir.join_path("rta_ud_scalar_metrics.csv"))
+               scalarCSV.set_header("Type", "AGS", "DGS", "NLVT", "NLCT")
+               udscalarCSV.set_header("Type", "AGS", "DGS", "NLVT", "NLCT")
+
+               for mtype in mtypes do
+                       var arity = 0
+                       if mtype isa MGenericType then arity = mtype.arguments.length
+                       if mtype.is_user_defined then
+                               udscalarCSV.add_line(mtype, arity, mtype.get_depth, mtype.nlvt, mtype.nlct)
+                       end
+                       scalarCSV.add_line(mtype, arity, mtype.get_depth, mtype.nlvt, mtype.nlct)
+               end
+               scalarCSV.save
+               udscalarCSV.save
+
+               scalarCSV = new CSVDocument(modelbuilder.toolcontext.output_dir.join_path("rta_scalar_class_metrics.csv"))
+               udscalarCSV = new CSVDocument(modelbuilder.toolcontext.output_dir.join_path("rta_ud_scalar_class_metrics.csv"))
+               scalarCSV.set_header("Class", "AGS", "NLVV", "NLVT")
+               udscalarCSV.set_header("Class", "AGS", "NLVV", "inst")
+
+               for mclass in modelbuilder.model.mclasses do
+                       if not mclass.is_class or mclass.is_abstract then continue
+                       if mclass.is_user_defined then
+                               udscalarCSV.add_line(mclass.mclass_type, mclass.arity, mclass.live_types.length, mclass.nlvt)
+                       end
+                       scalarCSV.add_line(mclass.mclass_type, mclass.arity, mclass.live_types.length, mclass.nlvt)
+               end
+               scalarCSV.save
+               udscalarCSV.save
+       end
+
        print "--- RTA metrics ---"
        print "Number of live runtime types (instantied resolved type): {analysis.live_types.length}"
        if analysis.live_types.length < 8 then print "\t{analysis.live_types.join(" ")}"