Extends the nitc code so a phase can inject new submodules.
The main new method the this PR is `ModelBuilder::inject_module_subimportation` that must be used during the analysis on a module.
Up to now, the module hierarchy was fixed and built by the loader before any phases are run.
The basic way was: 1. load the main module, 2. load its imported modules recursively and build the hierarchy, 3. run the phases on all the loaded modules from the most general to the most specific (top-down)
Now the phases can also extends the module hierarchy while running some phases.
This cause the following changes:
* `run_phase` use a work-list (instead of a simple loop)
* new modules can pop up even for the main module of a program, so a fictive main module is always created
* conditional_importations is extended to be used to by inject_module_subimportation.
Pull-Request: #2357
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Romain Chanoir <romain.chanoir@viacesi.fr>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>
Reviewed-by: Jean-Christophe Beaupré <jcbrinfo.public@gmail.com>
# (and `build_module_importation` that calls it).
#
# TODO (when the loader will be rewritten): use a better representation and move up rules in the model.
- private var conditional_importations = new Array[SequenceRead[MModule]]
+ var conditional_importations = new Array[SequenceRead[MModule]]
# Extends the current importations according to imported rules about conditional importation
fun apply_conditional_importations(mmodule: MModule)
for i in [1..ci.length[ do
var m = ci[i]
# Is imported?
- if not mmodule.in_importation.greaters.has(m) then continue label
+ if mmodule == m or not mmodule.in_importation.greaters.has(m) then continue label
end
# Still here? It means that all conditions modules are loaded and imported
var location = mainmodule.location
var model = mainmodule.model
+ # Create a fictive module if needed
if mainmodule == mmodules.first then
mainmodule = new MModule(model, null, mainmodule.name + "-d", location)
mainmodule.set_imported_mmodules(mmodules)
mainmodule.is_fictive = true
+ mainmodule.first_real_mmodule = mmodules.first
end
var recv = mainmodule.sys_type
# Is `self` a unit test module used by `nitunit`?
var is_test_suite: Bool = false is writable
- # Get the first non `is_fictive` module greater than self
- fun first_real_mmodule: MModule
- do
- var mmodule = self
- while mmodule.is_fictive do
- mmodule = mmodule.in_importation.direct_greaters.first
- end
- return mmodule
- end
+ # Get the non-`is_fictive` module on which `self` is based on.
+ #
+ # On non-fictive module, this returns `self`.
+ # On fictive modules, this is used to refer the module which `self` is based on.
+ #
+ # This attribute should be set when a fictive module is created. See `is_fictive`.
+ var first_real_mmodule: MModule = self is writable
redef fun parent_concern do return mgroup
end
do
assert not mmodules.is_empty
var mainmodule
- if mmodules.length == 1 then
+ # We need a main module, so we build it by importing all modules
+ mainmodule = new MModule(modelbuilder.model, null, mmodules.first.name + "-m", new Location(mmodules.first.location.file, 0, 0, 0, 0))
+ mainmodule.is_fictive = true
+ mainmodule.first_real_mmodule = mmodules.first.first_real_mmodule
+ mainmodule.set_imported_mmodules(mmodules)
+ modelbuilder.apply_conditional_importations(mainmodule)
+ if mainmodule.in_importation.direct_greaters.length == 1 and mainmodule.in_importation.direct_greaters.first == mmodules.first then
+ # Drop the fictive module if not needed
mainmodule = mmodules.first
else
- # We need a main module, so we build it by importing all modules
- mainmodule = new MModule(modelbuilder.model, null, mmodules.first.name + "-m", new Location(mmodules.first.location.file, 0, 0, 0, 0))
- mainmodule.is_fictive = true
- mainmodule.set_imported_mmodules(mmodules)
- modelbuilder.apply_conditional_importations(mainmodule)
+ # Or else run phases on it
modelbuilder.run_phases
end
return mainmodule
end
end
+ # Load module `filename` and add it as a conditional importation of `mmodule`.
+ #
+ # This means that current (and future) submodules of `module` will also import `filename`.
+ fun inject_module_subimportation(mmodule: MModule, filename: String)
+ do
+ var am = load_module(filename)
+ if am == null then return # forward error
+ var mm = am.mmodule
+ if mm == null then return # forward error
+ # Add the new module before the existing submodules in the hierarchy
+ for subm in mmodule.in_importation.direct_smallers do
+ subm.set_imported_mmodules([mm])
+ end
+ # Register the new module as a conditional_importations for future submodules
+ conditional_importations.add([mm, mmodule])
+ # Register the new amodule to be processed by `run_phases`
+ toolcontext.todo_nmodules.unshift am
+ end
end
# Set of already analyzed modules.
private var phased_modules = new HashSet[AModule]
+ # List of module to process according to `run_phases`
+ #
+ # This allow some new modules to be found and added while analysing the code.
+ var todo_nmodules: Sequence[AModule]
+
# Run all registered phases on a set of modules
fun run_phases(nmodules: Collection[AModule])
do
self.info(" registered phases: {phase}", 2)
end
- for nmodule in nmodules do
+ var todo_nmodules = nmodules.to_a
+ self.todo_nmodules = todo_nmodules
+
+ while not todo_nmodules.is_empty do
+ var nmodule = todo_nmodules.shift
if phased_modules.has(nmodule) then continue
phased_modules.add nmodule
total: 0
--- Poset metrics ---
## Module importation hierarchy
-Number of nodes: 1
-Number of edges: 1 (1.00 per node)
-Number of direct edges: 0 (0.00 per node)
+Number of nodes: 2
+Number of edges: 3 (1.50 per node)
+Number of direct edges: 1 (0.50 per node)
Distribution of greaters
- population: 1
+ population: 2
minimum value: 1
- maximum value: 1
- total value: 1
- average value: 1.00
+ maximum value: 2
+ total value: 3
+ average value: 1.50
distribution:
- <=1: sub-population=1 (100.00%); cumulated value=1 (100.00%)
+ <=1: sub-population=1 (50.00%); cumulated value=1 (33.33%)
+ <=2: sub-population=1 (50.00%); cumulated value=2 (66.66%)
Distribution of direct greaters
- population: 1
+ population: 2
minimum value: 0
- maximum value: 0
- total value: 0
- average value: 0.00
+ maximum value: 1
+ total value: 1
+ average value: 0.50
distribution:
- <=0: sub-population=1 (100.00%); cumulated value=0 (na%)
+ <=0: sub-population=1 (50.00%); cumulated value=0 (0.00%)
+ <=1: sub-population=1 (50.00%); cumulated value=1 (100.00%)
Distribution of smallers
- population: 1
+ population: 2
minimum value: 1
- maximum value: 1
- total value: 1
- average value: 1.00
+ maximum value: 2
+ total value: 3
+ average value: 1.50
distribution:
- <=1: sub-population=1 (100.00%); cumulated value=1 (100.00%)
+ <=1: sub-population=1 (50.00%); cumulated value=1 (33.33%)
+ <=2: sub-population=1 (50.00%); cumulated value=2 (66.66%)
Distribution of direct smallers
- population: 1
+ population: 2
minimum value: 0
- maximum value: 0
- total value: 0
- average value: 0.00
+ maximum value: 1
+ total value: 1
+ average value: 0.50
distribution:
- <=0: sub-population=1 (100.00%); cumulated value=0 (na%)
+ <=0: sub-population=1 (50.00%); cumulated value=0 (0.00%)
+ <=1: sub-population=1 (50.00%); cumulated value=1 (100.00%)
## Classdef hierarchy
Number of nodes: 7
Number of edges: 13 (1.85 per node)
<=0: sub-population=6 (85.71%); cumulated value=0 (0.00%)
<=8: sub-population=1 (14.28%); cumulated value=6 (100.00%)
--- Metrics of refinement usage ---
-Number of modules: 1
+Number of modules: 2
Number of classes: 7
Number of interface kind: 1 (14.28%)
std: 0.0
sum: 0
mnoc: number of child modules
- avg: 0.0
- max: base_simple3 (0)
- min: base_simple3 (0)
+ avg: 1.0
+ max: base_simple3 (1)
+ min: base_simple3 (1)
std: 0.0
- sum: 0
+ sum: 1
mnod: number of descendant modules
- avg: 0.0
- max: base_simple3 (0)
- min: base_simple3 (0)
+ avg: 1.0
+ max: base_simple3 (1)
+ min: base_simple3 (1)
std: 0.0
- sum: 0
+ sum: 1
mdit: depth in module tree
avg: 0.0
max: base_simple3 (0)
std: 0.0
sum: 0
mnoc: number of child modules
- avg: 0.0
- max: base_simple3 (0)
- min: base_simple3 (0)
+ avg: 1.0
+ max: base_simple3 (1)
+ min: base_simple3 (1)
std: 0.0
- sum: 0
+ sum: 1
mnod: number of descendant modules
- avg: 0.0
- max: base_simple3 (0)
- min: base_simple3 (0)
+ avg: 1.0
+ max: base_simple3 (1)
+ min: base_simple3 (1)
std: 0.0
- sum: 0
+ sum: 1
mdit: depth in module tree
avg: 0.0
max: base_simple3 (0)
It works
I have 1 packages
-I have 1 modules
+I have 2 modules
I have 7 classes
For 7 definitions of classes
I have 15 methods