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
# 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)
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]
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]
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
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
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.
# * -> *
var ditsum = 0
+ var ditudsum = 0
+
var dui_count = 0
var ccdui_count = 0
var cidui_count = 0
for mclass in model.mclasses do
ditsum += mclass.dit
+ ditudsum += mclass.ditud
# * -> *
if mclass.is_dui_eligible then dui_count += 1
# * -> *
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)
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)
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]
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(" ")}"