nitc :: MProperty :: defaultinit
# A service (global property) that generalize method, attribute, etc.
#
# `MProperty` are global to the model; it means that a `MProperty` is not bound
# to a specific `MModule` nor a specific `MClass`.
#
# A MProperty gather definitions (see `mpropdefs`) ; one for the introduction
# and the other in subclasses and in refinements.
#
# A `MProperty` is used to denotes services in polymorphic way (ie. independent
# of any dynamic type).
# For instance, a call site "x.foo" is associated to a `MProperty`.
abstract class MProperty
super MEntity
# The associated MPropDef subclass.
# The two specialization hierarchy are symmetric.
type MPROPDEF: MPropDef
# The classdef that introduce the property
# While a property is not bound to a specific module, or class,
# the introducing mclassdef is used for naming and visibility
var intro_mclassdef: MClassDef
# The (short) name of the property
redef var name
redef var location
redef fun mdoc_or_fallback
do
# Don’t use `intro.mdoc_or_fallback` because it would create an infinite
# recursion.
return intro.mdoc
end
# The canonical name of the property.
#
# It is currently the short-`name` prefixed by the short-name of the class and the full-name of the module.
# Example: "my_package::my_module::MyClass::my_method"
#
# The full-name of the module is needed because two distinct modules of the same package can
# still refine the same class and introduce homonym properties.
#
# For public properties not introduced by refinement, the module name is not used.
#
# Example: `my_package::MyClass::My_method`
redef var full_name is lazy do
if intro_mclassdef.is_intro then
return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}"
else
return "{intro_mclassdef.mmodule.full_name}::{intro_mclassdef.mclass.name}::{name}"
end
end
redef var c_name is lazy do
# FIXME use `namespace_for`
return "{intro_mclassdef.mmodule.c_name}__{intro_mclassdef.mclass.name.to_cmangle}__{name.to_cmangle}"
end
# The visibility of the property
redef var visibility
# Is the property usable as an initializer?
var is_autoinit = false is writable
init
do
intro_mclassdef.intro_mproperties.add(self)
var model = intro_mclassdef.mmodule.model
model.mproperties_by_name.add_one(name, self)
model.mproperties.add(self)
end
# All definitions of the property.
# The first is the introduction,
# The other are redefinitions (in refinements and in subclasses)
var mpropdefs = new Array[MPROPDEF]
# The definition that introduces the property.
#
# Warning: such a definition may not exist in the early life of the object.
# In this case, the method will abort.
var intro: MPROPDEF is noinit
redef fun model do return intro.model
# Alias for `name`
redef fun to_s do return name
# Return the most specific property definitions defined or inherited by a type.
# The selection knows that refinement is stronger than specialization;
# however, in case of conflict more than one property are returned.
# If mtype does not know mproperty then an empty array is returned.
#
# If you want the really most specific property, then look at `lookup_first_definition`
#
# REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter)
# ENSURE: `not mtype.has_mproperty(mmodule, self) == result.is_empty`
fun lookup_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF]
do
assert not mtype.need_anchor
mtype = mtype.undecorate
var cache = self.lookup_definitions_cache[mmodule, mtype]
if cache != null then return cache
#print "select prop {mproperty} for {mtype} in {self}"
# First, select all candidates
var candidates = new Array[MPROPDEF]
# Here we have two strategies: iterate propdefs or iterate classdefs.
var mpropdefs = self.mpropdefs
if mpropdefs.length <= 1 or mpropdefs.length < mtype.collect_mclassdefs(mmodule).length then
# Iterate on all definitions of `self`, keep only those inherited by `mtype` in `mmodule`
for mpropdef in mpropdefs do
# If the definition is not imported by the module, then skip
if not mmodule.in_importation <= mpropdef.mclassdef.mmodule then continue
# If the definition is not inherited by the type, then skip
if not mtype.is_subtype(mmodule, null, mpropdef.mclassdef.bound_mtype) then continue
# Else, we keep it
candidates.add(mpropdef)
end
else
# Iterate on all super-classdefs of `mtype`, keep only the definitions of `self`, if any.
for mclassdef in mtype.collect_mclassdefs(mmodule) do
var p = mclassdef.mpropdefs_by_property.get_or_null(self)
if p != null then candidates.add p
end
end
# Fast track for only one candidate
if candidates.length <= 1 then
self.lookup_definitions_cache[mmodule, mtype] = candidates
return candidates
end
# Second, filter the most specific ones
return select_most_specific(mmodule, candidates)
end
private var lookup_definitions_cache = new HashMap2[MModule, MType, Array[MPROPDEF]]
# Return the most specific property definitions inherited by a type.
# The selection knows that refinement is stronger than specialization;
# however, in case of conflict more than one property are returned.
# If mtype does not know mproperty then an empty array is returned.
#
# If you want the really most specific property, then look at `lookup_next_definition`
#
# REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter)
# ENSURE: `not mtype.has_mproperty(mmodule, self) implies result.is_empty`
fun lookup_super_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF]
do
assert not mtype.need_anchor
mtype = mtype.undecorate
# First, select all candidates
var candidates = new Array[MPROPDEF]
for mpropdef in self.mpropdefs do
# If the definition is not imported by the module, then skip
if not mmodule.in_importation <= mpropdef.mclassdef.mmodule then continue
# If the definition is not inherited by the type, then skip
if not mtype.is_subtype(mmodule, null, mpropdef.mclassdef.bound_mtype) then continue
# If the definition is defined by the type, then skip (we want the super, so e skip the current)
if mtype == mpropdef.mclassdef.bound_mtype and mmodule == mpropdef.mclassdef.mmodule then continue
# Else, we keep it
candidates.add(mpropdef)
end
# Fast track for only one candidate
if candidates.length <= 1 then return candidates
# Second, filter the most specific ones
return select_most_specific(mmodule, candidates)
end
# Return an array containing olny the most specific property definitions
# This is an helper function for `lookup_definitions` and `lookup_super_definitions`
private fun select_most_specific(mmodule: MModule, candidates: Array[MPROPDEF]): Array[MPROPDEF]
do
var res = new Array[MPROPDEF]
for pd1 in candidates do
var cd1 = pd1.mclassdef
var c1 = cd1.mclass
var keep = true
for pd2 in candidates do
if pd2 == pd1 then continue # do not compare with self!
var cd2 = pd2.mclassdef
var c2 = cd2.mclass
if c2.mclass_type == c1.mclass_type then
if cd2.mmodule.in_importation < cd1.mmodule then
# cd2 refines cd1; therefore we skip pd1
keep = false
break
end
else if cd2.bound_mtype.is_subtype(mmodule, null, cd1.bound_mtype) and cd2.bound_mtype != cd1.bound_mtype then
# cd2 < cd1; therefore we skip pd1
keep = false
break
end
end
if keep then
res.add(pd1)
end
end
if res.is_empty then
print_error "All lost! {candidates.join(", ")}"
# FIXME: should be abort!
end
return res
end
# Return the most specific definition in the linearization of `mtype`.
#
# If you want to know the next properties in the linearization,
# look at `MPropDef::lookup_next_definition`.
#
# FIXME: the linearization is still unspecified
#
# REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter)
# REQUIRE: `mtype.has_mproperty(mmodule, self)`
fun lookup_first_definition(mmodule: MModule, mtype: MType): MPROPDEF
do
return lookup_all_definitions(mmodule, mtype).first
end
# Return all definitions in a linearization order
# Most specific first, most general last
#
# REQUIRE: `not mtype.need_anchor` to simplify the API (no `anchor` parameter)
# REQUIRE: `mtype.has_mproperty(mmodule, self)`
fun lookup_all_definitions(mmodule: MModule, mtype: MType): Array[MPROPDEF]
do
mtype = mtype.undecorate
var cache = self.lookup_all_definitions_cache[mmodule, mtype]
if cache != null then return cache
assert not mtype.need_anchor
assert mtype.has_mproperty(mmodule, self)
#print "select prop {mproperty} for {mtype} in {self}"
# First, select all candidates
var candidates = new Array[MPROPDEF]
for mpropdef in self.mpropdefs do
# If the definition is not imported by the module, then skip
if not mmodule.in_importation <= mpropdef.mclassdef.mmodule then continue
# If the definition is not inherited by the type, then skip
if not mtype.is_subtype(mmodule, null, mpropdef.mclassdef.bound_mtype) then continue
# Else, we keep it
candidates.add(mpropdef)
end
# Fast track for only one candidate
if candidates.length <= 1 then
self.lookup_all_definitions_cache[mmodule, mtype] = candidates
return candidates
end
mmodule.linearize_mpropdefs(candidates)
candidates = candidates.reversed
self.lookup_all_definitions_cache[mmodule, mtype] = candidates
return candidates
end
private var lookup_all_definitions_cache = new HashMap2[MModule, MType, Array[MPROPDEF]]
redef var is_test is lazy do return intro.is_test
# Does self have the `before` annotation?
var is_before: Bool is lazy do return intro.is_before
# Does self have the `before_all` annotation?
var is_before_all: Bool is lazy do return intro.is_before_all
# Does self have the `after` annotation?
var is_after: Bool is lazy do return intro.is_after
# Does self have the `after_all` annotation?
var is_after_all: Bool is lazy do return intro.is_after_all
end
src/model/model.nit:2131,1--2409,3