nitc :: DetectCovariancePhase :: count_cast
Common method used when static cast test is seen by a phasenitc :: DetectCovariancePhase :: count_types
Common method used when static subtype test is performed by a phasenitc $ DetectCovariancePhase :: SELF
Type of this instance, automatically specialized in every classnitc $ DetectCovariancePhase :: process_mainmodule
Display collected statisticsnitc :: Phase :: _in_hierarchy
The dependence relation of the phase with the other phasesnitc :: Phase :: _toolcontext
The toolcontext instance attached to the phasecore :: Object :: class_factory
Implementation used byget_class
to create the specific class.
nitc :: DetectCovariancePhase :: count_cast
Common method used when static cast test is seen by a phasenitc :: DetectCovariancePhase :: count_types
Common method used when static subtype test is performed by a phasecore :: Object :: defaultinit
nitc :: Phase :: defaultinit
nitc :: Phase :: in_hierarchy
The dependence relation of the phase with the other phasesnitc :: Phase :: in_hierarchy=
The dependence relation of the phase with the other phasescore :: Object :: is_same_instance
Return true ifself
and other
are the same instance (i.e. same identity).
core :: Object :: is_same_serialized
Isself
the same as other
in a serialization context?
core :: Object :: is_same_type
Return true ifself
and other
have the same dynamic type.
core :: Object :: native_class_name
The class name of the object in CString format.core :: Object :: output_class_name
Display class name on stdout (debug only).nitc :: Phase :: process_annotated_node
Specific actions to execute on annotated nodesnitc :: Phase :: process_mainmodule
Specific action to execute on the whole program.nitc :: Phase :: process_nclassdef
Specific actions to execute on the tree of a class definitionnitc :: Phase :: process_nmodule
Specific actions to execute on the whole tree of a modulenitc :: Phase :: process_nmodule_after
Specific actions to execute on the whole tree of a modulenitc :: Phase :: process_npropdef
Specific actions to execute on the tree of a propertynitc :: Phase :: toolcontext
The toolcontext instance attached to the phasenitc :: Phase :: toolcontext=
The toolcontext instance attached to the phase
private class DetectCovariancePhase
super Phase
init
do
toolcontext.option_context.add_option(toolcontext.opt_detect_covariance)
end
fun is_disabled: Bool
do
return not toolcontext.opt_detect_covariance.value and not toolcontext.opt_all.value
end
fun cpt_subtype_kinds: Counter[String] do return once new Counter[String]
fun cpt_total_variance: Counter[String] do return once new Counter[String]
fun cpt_total_classes: Counter[String] do return once new Counter[String]
fun cpt_explanations: Counter[String] do return once new Counter[String]
fun cpt_classes: Counter[MClass] do return once new Counter[MClass]
fun cpt_pattern: Counter[String] do return once new Counter[String]
fun cpt_nodes: Counter[String] do return once new Counter[String]
fun cpt_modules: Counter[String] do return once new Counter[String]
fun cpt_expression: Counter[String] do return once new Counter[String]
fun cpt_cast_kind: Counter[String] do return once new Counter[String]
fun cpt_cast_classes: Counter[String] do return once new Counter[String]
fun cpt_cast_pattern: Counter[String] do return once new Counter[String]
fun cpt_autocast: Counter[String] do return once new Counter[String]
# Display collected statistics
redef fun process_mainmodule(mainmodule, given_mmodules)
do
if is_disabled then return
print "--- Detection of the usage of covariance static type conformance ---"
print "-- Total --"
print "- Kinds of the subtype -"
cpt_subtype_kinds.print_elements(10)
print " total: {cpt_subtype_kinds.sum}"
print "- Variance -"
cpt_total_variance.print_elements(10)
print " total: {cpt_total_variance.sum}"
print "- Classes of the subtype -"
cpt_total_classes.print_elements(10)
print " total: {cpt_total_classes.sum}"
print "-- On covariance only --"
print "- Specific covariance case explanations -"
cpt_explanations.print_elements(10)
print " total: {cpt_explanations.sum}"
print "- Classes of the subtype, when covariance -"
cpt_classes.print_elements(10)
print " total: {cpt_classes.sum}"
print "- Patterns of the covariant cases -"
cpt_pattern.print_elements(10)
print " total: {cpt_pattern.sum}"
print "- Nodes of the covariance cases -"
cpt_nodes.print_elements(10)
print " total: {cpt_nodes.sum}"
print "- Modules of the covariance cases -"
cpt_modules.print_elements(10)
print " total: {cpt_modules.sum}"
print "- Kind of the expression node (when it make sense) -"
cpt_expression.print_elements(10)
print " total: {cpt_expression.sum}"
print "-- Casts --"
print "- Kind of cast target -"
cpt_cast_kind.print_elements(10)
print " total: {cpt_cast_kind.sum}"
print "- Classes of the cast -"
cpt_cast_classes.print_elements(10)
print " total: {cpt_cast_classes.sum}"
print "- Cast pattern -"
cpt_cast_pattern.print_elements(10)
print " total: {cpt_cast_pattern.sum}"
print "- Autocasts -"
cpt_autocast.print_elements(10)
print " total: {cpt_autocast.sum}"
end
# Common method used when static subtype test is performed by a phase
# Returns true if the test concern real generic covariance
fun count_types(node, elem: ANode, sub, sup: MType, mmodule: MModule, anchor: nullable MClassType): Bool
do
sub = sub.undecorate
sup = sup.undecorate
# Category of the target type
if sub isa MGenericType then
cpt_subtype_kinds.inc("generic type")
else if not sub isa MClassType then
cpt_subtype_kinds.inc("formal type")
else if sub.mclass.kind == enum_kind then
cpt_subtype_kinds.inc("primitive type")
else if sub.mclass.name == "Object" then
cpt_subtype_kinds.inc("object")
else
cpt_subtype_kinds.inc("non-generic type")
end
# Class of the subtype
if sub isa MClassType then
cpt_total_classes.inc(sub.mclass.to_s)
else
cpt_total_classes.inc(sub.to_s)
end
# Equal monomorph case
if sub == sup then
cpt_total_variance.inc("monomorph")
return false
end
# Equivalent monomorph case
if sub.is_subtype_invar(mmodule, anchor, sup) and sup.is_subtype_invar(mmodule, anchor, sub) then
cpt_total_variance.inc("monomorph equiv")
return false
end
# Formal case
if not sub isa MClassType then
cpt_total_variance.inc("polymorph & formal")
return false
end
# Non generic case
if not sub isa MGenericType then
cpt_total_variance.inc("polymorph & non-generic")
return false
end
# Invariant case
if sub.is_subtype_invar(mmodule, anchor, sup) then
cpt_total_variance.inc("polymorph & generic & invariant")
return false
end
if sub.is_subtype(mmodule, anchor, sup) then
# Covariant
cpt_total_variance.inc("polymorph & generic & covariant")
else
# Cast (explicit or autocast)
if sup.is_subtype(mmodule, anchor, sub) then
cpt_total_variance.inc("polymorph & generic & upcast!?")
cpt_pattern.inc("7.upcast")
return false
end
if not sup isa MGenericType then
cpt_total_variance.inc("polymorph & generic & lateral-cast from non-gen")
return false
end
cpt_total_variance.inc("polymorph & generic & lateral-cast from gen")
end
## ONLY covariance remains here
cpt_modules.inc(mmodule.mgroup.mpackage.name)
cpt_classes.inc(sub.mclass)
# Track if `cpt_explanations` is already decided (used to fallback on unknown)
var caseknown = false
# Detect the pattern
var n = node
while n isa AType or n isa AExprs do n = n.parent.as(not null)
cpt_nodes.inc(n.class_name)
if n isa AVarAssignExpr or n isa AAttrPropdef and elem isa AExpr then
cpt_pattern.inc("1.assign")
else if n isa ASendExpr or n isa ANewExpr then
cpt_pattern.inc("2.param")
else if n isa AReturnExpr then
cpt_pattern.inc("3.return")
else if n isa APropdef or n isa ASignature then
cpt_pattern.inc("4.redef")
else if n isa AAsCastExpr or n isa AIsaExpr then
cpt_pattern.inc("6.downcast")
if n isa AIsaExpr and n.n_expr isa ASelfExpr then
cpt_explanations.inc("downcast on self")
caseknown = true
else
node.debug("6.downcast {sup} to {sub}")
end
else if n isa ASuperPropdef then
cpt_pattern.inc("8.subclass")
else if n isa AArrayExpr then
cpt_pattern.inc("9.array element")
else
n.debug("Unknown pattern")
cpt_pattern.inc("X.unknown")
end
if not caseknown then
if false then
cpt_explanations.inc("covariant class")
else
cpt_explanations.inc("other covariance")
end
end
return true
end
# Common method used when static cast test is seen by a phase
fun count_cast(node: ANode, sub, sup: MType, mmodule: MModule, anchor: nullable MClassType)
do
var nsup = sup
sup = sup.undecorate
sub = sub.undecorate
if sub == nsup then
cpt_cast_pattern.inc("monomorphic cast!?!")
node.debug("monomorphic cast {sup} to {sub}")
else if not sub isa MClassType then
cpt_cast_pattern.inc("cast to formal")
else if not sub isa MGenericType then
cpt_cast_pattern.inc("cast to non-generic")
else if sub == sup then
cpt_cast_pattern.inc("nonullable monomorphic cast")
else if sup.is_subtype(mmodule, anchor, sub) then
cpt_cast_pattern.inc("upcast to generic")
else if not sub.is_subtype(mmodule, anchor, sup) then
cpt_cast_pattern.inc("lateral cast to generic")
else if not sub.is_subtype_invar(mmodule, anchor, sup) then
assert sup isa MGenericType
if sup.mclass != sub.mclass then
cpt_cast_pattern.inc("covariant downcast to a generic (distinct classes)")
else
cpt_cast_pattern.inc("covariant downcast to a generic (same classes)")
end
else if not sup isa MGenericType then
cpt_cast_pattern.inc("invariant downcast from non-generic to a generic")
else
assert sup.mclass != sub.mclass
cpt_cast_pattern.inc("invariant downcast from generic to generic")
end
cpt_cast_kind.inc(sub.class_name.to_s)
if sub isa MGenericType then
cpt_cast_classes.inc(sub.mclass.to_s)
else if sub isa MClassType then
# No generic class, so no covariance at runtime
else
cpt_cast_classes.inc(sub.to_s)
end
end
end
src/metrics/detect_covariance.nit:35,1--297,3