Merge: nitc: inject importation
authorJean Privat <jean@pryen.org>
Mon, 6 Feb 2017 13:39:02 +0000 (08:39 -0500)
committerJean Privat <jean@pryen.org>
Mon, 6 Feb 2017 13:39:02 +0000 (08:39 -0500)
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>

src/loader.nit
src/mixin.nit
src/model/mmodule.nit
src/modelbuilder.nit
src/phase.nit
tests/sav/nitmetrics_args1.res
tests/sav/test_test_phase_args1.res

index ac07e71..32749aa 100644 (file)
@@ -1045,7 +1045,7 @@ redef class ModelBuilder
        # (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)
@@ -1061,7 +1061,7 @@ redef class ModelBuilder
                                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
 
index 508fc67..1e39970 100644 (file)
@@ -46,10 +46,12 @@ redef class ToolContext
                        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
index 4cdd340..44eaffb 100644 (file)
@@ -251,15 +251,13 @@ class MModule
        # 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
index 671fcd0..b442704 100644 (file)
@@ -38,14 +38,17 @@ redef class ToolContext
        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
@@ -104,4 +107,22 @@ redef class ModelBuilder
                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
index c94dcdc..2d400c9 100644 (file)
@@ -86,6 +86,11 @@ redef class ToolContext
        # 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
@@ -99,7 +104,11 @@ redef class ToolContext
                        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
 
index 2eb19e5..b7fdbd8 100644 (file)
   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)
@@ -205,7 +209,7 @@ Distribution of direct smallers
   <=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%)
@@ -417,17 +421,17 @@ generating module_hierarchy.dot
          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)
@@ -479,17 +483,17 @@ generating module_hierarchy.dot
          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)
index 03cf44d..aa04d7f 100644 (file)
@@ -1,6 +1,6 @@
 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