jdk/src/java.base/share/classes/java/lang/module/Resolver.java
changeset 42338 a60f280f803c
parent 39050 9de41b79ec7e
child 42703 20c39ea4a507
equal deleted inserted replaced
42148:7a4a59859ac0 42338:a60f280f803c
    24  */
    24  */
    25 
    25 
    26 package java.lang.module;
    26 package java.lang.module;
    27 
    27 
    28 import java.io.PrintStream;
    28 import java.io.PrintStream;
       
    29 import java.lang.module.ModuleDescriptor.Provides;
    29 import java.lang.module.ModuleDescriptor.Requires.Modifier;
    30 import java.lang.module.ModuleDescriptor.Requires.Modifier;
       
    31 import java.lang.reflect.Layer;
    30 import java.util.ArrayDeque;
    32 import java.util.ArrayDeque;
    31 import java.util.ArrayList;
    33 import java.util.ArrayList;
       
    34 import java.util.Arrays;
    32 import java.util.Collection;
    35 import java.util.Collection;
    33 import java.util.Deque;
    36 import java.util.Deque;
    34 import java.util.HashMap;
    37 import java.util.HashMap;
    35 import java.util.HashSet;
    38 import java.util.HashSet;
    36 import java.util.LinkedHashSet;
    39 import java.util.LinkedHashSet;
    45 import jdk.internal.module.ModuleHashes;
    48 import jdk.internal.module.ModuleHashes;
    46 
    49 
    47 /**
    50 /**
    48  * The resolver used by {@link Configuration#resolveRequires} and
    51  * The resolver used by {@link Configuration#resolveRequires} and
    49  * {@link Configuration#resolveRequiresAndUses}.
    52  * {@link Configuration#resolveRequiresAndUses}.
       
    53  *
       
    54  * @implNote The resolver is used at VM startup and so deliberately avoids
       
    55  * using lambda and stream usages in code paths used during startup.
    50  */
    56  */
    51 
    57 
    52 final class Resolver {
    58 final class Resolver {
    53 
    59 
    54     private final ModuleFinder beforeFinder;
    60     private final ModuleFinder beforeFinder;
    55     private final Configuration parent;
    61     private final List<Configuration> parents;
    56     private final ModuleFinder afterFinder;
    62     private final ModuleFinder afterFinder;
    57     private final PrintStream traceOutput;
    63     private final PrintStream traceOutput;
    58 
    64 
    59     // maps module name to module reference
    65     // maps module name to module reference
    60     private final Map<String, ModuleReference> nameToReference = new HashMap<>();
    66     private final Map<String, ModuleReference> nameToReference = new HashMap<>();
    61 
    67 
    62 
    68 
    63     Resolver(ModuleFinder beforeFinder,
    69     Resolver(ModuleFinder beforeFinder,
    64              Configuration parent,
    70              List<Configuration> parents,
    65              ModuleFinder afterFinder,
    71              ModuleFinder afterFinder,
    66              PrintStream traceOutput) {
    72              PrintStream traceOutput) {
    67         this.beforeFinder = beforeFinder;
    73         this.beforeFinder = beforeFinder;
    68         this.parent = parent;
    74         this.parents = parents;
    69         this.afterFinder = afterFinder;
    75         this.afterFinder = afterFinder;
    70         this.traceOutput = traceOutput;
    76         this.traceOutput = traceOutput;
    71     }
    77     }
    72 
    78 
    73 
    79 
    83         for (String root : roots) {
    89         for (String root : roots) {
    84 
    90 
    85             // find root module
    91             // find root module
    86             ModuleReference mref = findWithBeforeFinder(root);
    92             ModuleReference mref = findWithBeforeFinder(root);
    87             if (mref == null) {
    93             if (mref == null) {
    88                 if (parent.findModule(root).isPresent()) {
    94 
       
    95                 if (findInParent(root) != null) {
    89                     // in parent, nothing to do
    96                     // in parent, nothing to do
    90                     continue;
    97                     continue;
    91                 }
    98                 }
       
    99 
    92                 mref = findWithAfterFinder(root);
   100                 mref = findWithAfterFinder(root);
    93                 if (mref == null) {
   101                 if (mref == null) {
    94                     fail("Module %s not found", root);
   102                     fail("Module %s not found", root);
    95                 }
   103                 }
    96             }
   104             }
   123             ModuleDescriptor descriptor = q.poll();
   131             ModuleDescriptor descriptor = q.poll();
   124             assert nameToReference.containsKey(descriptor.name());
   132             assert nameToReference.containsKey(descriptor.name());
   125 
   133 
   126             // process dependences
   134             // process dependences
   127             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
   135             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
       
   136 
       
   137                 // only required at compile-time
       
   138                 if (requires.modifiers().contains(Modifier.STATIC))
       
   139                     continue;
       
   140 
   128                 String dn = requires.name();
   141                 String dn = requires.name();
   129 
   142 
   130                 // find dependence
   143                 // find dependence
   131                 ModuleReference mref = findWithBeforeFinder(dn);
   144                 ModuleReference mref = findWithBeforeFinder(dn);
   132                 if (mref == null) {
   145                 if (mref == null) {
   133                     if (parent.findModule(dn).isPresent())
   146 
       
   147                     if (findInParent(dn) != null) {
       
   148                         // dependence is in parent
   134                         continue;
   149                         continue;
       
   150                     }
   135 
   151 
   136                     mref = findWithAfterFinder(dn);
   152                     mref = findWithAfterFinder(dn);
   137                     if (mref == null) {
   153                     if (mref == null) {
   138                         fail("Module %s not found, required by %s",
   154                         fail("Module %s not found, required by %s",
   139                                 dn, descriptor.name());
   155                                 dn, descriptor.name());
   172         Map<String, Set<ModuleReference>> availableProviders = new HashMap<>();
   188         Map<String, Set<ModuleReference>> availableProviders = new HashMap<>();
   173         for (ModuleReference mref : findAll()) {
   189         for (ModuleReference mref : findAll()) {
   174             ModuleDescriptor descriptor = mref.descriptor();
   190             ModuleDescriptor descriptor = mref.descriptor();
   175             if (!descriptor.provides().isEmpty()) {
   191             if (!descriptor.provides().isEmpty()) {
   176 
   192 
   177                 for (String sn : descriptor.provides().keySet()) {
   193                 for (Provides provides :  descriptor.provides()) {
       
   194                     String sn = provides.service();
       
   195 
   178                     // computeIfAbsent
   196                     // computeIfAbsent
   179                     Set<ModuleReference> providers = availableProviders.get(sn);
   197                     Set<ModuleReference> providers = availableProviders.get(sn);
   180                     if (providers == null) {
   198                     if (providers == null) {
   181                         providers = new HashSet<>();
   199                         providers = new HashSet<>();
   182                         availableProviders.put(sn, providers);
   200                         availableProviders.put(sn, providers);
   189 
   207 
   190         // create the visit stack
   208         // create the visit stack
   191         Deque<ModuleDescriptor> q = new ArrayDeque<>();
   209         Deque<ModuleDescriptor> q = new ArrayDeque<>();
   192 
   210 
   193         // the initial set of modules that may use services
   211         // the initial set of modules that may use services
   194         Set<ModuleDescriptor> candidateConsumers = new HashSet<>();
   212         Set<ModuleDescriptor> initialConsumers;
   195         Configuration p = parent;
   213         if (Layer.boot() == null) {
   196         while (p != null) {
   214             initialConsumers = new HashSet<>();
   197             candidateConsumers.addAll(p.descriptors());
   215         } else {
   198             p = p.parent().orElse(null);
   216             initialConsumers = parents.stream()
       
   217                     .flatMap(Configuration::configurations)
       
   218                     .distinct()
       
   219                     .flatMap(c -> c.descriptors().stream())
       
   220                     .collect(Collectors.toSet());
   199         }
   221         }
   200         for (ModuleReference mref : nameToReference.values()) {
   222         for (ModuleReference mref : nameToReference.values()) {
   201             candidateConsumers.add(mref.descriptor());
   223             initialConsumers.add(mref.descriptor());
   202         }
   224         }
   203 
       
   204 
   225 
   205         // Where there is a consumer of a service then resolve all modules
   226         // Where there is a consumer of a service then resolve all modules
   206         // that provide an implementation of that service
   227         // that provide an implementation of that service
       
   228         Set<ModuleDescriptor> candidateConsumers = initialConsumers;
   207         do {
   229         do {
   208             for (ModuleDescriptor descriptor : candidateConsumers) {
   230             for (ModuleDescriptor descriptor : candidateConsumers) {
   209                 if (!descriptor.uses().isEmpty()) {
   231                 if (!descriptor.uses().isEmpty()) {
   210                     for (String service : descriptor.uses()) {
   232                     for (String service : descriptor.uses()) {
   211                         Set<ModuleReference> mrefs = availableProviders.get(service);
   233                         Set<ModuleReference> mrefs = availableProviders.get(service);
   232                     }
   254                     }
   233                 }
   255                 }
   234             }
   256             }
   235 
   257 
   236             candidateConsumers = resolve(q);
   258             candidateConsumers = resolve(q);
   237 
       
   238         } while (!candidateConsumers.isEmpty());
   259         } while (!candidateConsumers.isEmpty());
   239 
   260 
   240         return this;
   261         return this;
   241     }
   262     }
   242 
   263 
   427 
   448 
   428             String algorithm = hashes.algorithm();
   449             String algorithm = hashes.algorithm();
   429             for (String dn : hashes.names()) {
   450             for (String dn : hashes.names()) {
   430                 ModuleReference other = nameToReference.get(dn);
   451                 ModuleReference other = nameToReference.get(dn);
   431                 if (other == null) {
   452                 if (other == null) {
   432                     other = parent.findModule(dn)
   453                     ResolvedModule resolvedModule = findInParent(dn);
   433                             .map(ResolvedModule::reference)
   454                     if (resolvedModule != null)
   434                             .orElse(null);
   455                         other = resolvedModule.reference();
   435                 }
   456                 }
   436 
   457 
   437                 // skip checking the hash if the module has been patched
   458                 // skip checking the hash if the module has been patched
   438                 if (other != null && !other.isPatched()) {
   459                 if (other != null && !other.isPatched()) {
   439                     String recordedHash = hashes.hashFor(dn);
   460                     byte[] recordedHash = hashes.hashFor(dn);
   440                     String actualHash = other.computeHash(algorithm);
   461                     byte[] actualHash = other.computeHash(algorithm);
   441                     if (actualHash == null)
   462                     if (actualHash == null)
   442                         fail("Unable to compute the hash of module %s", dn);
   463                         fail("Unable to compute the hash of module %s", dn);
   443                     if (!recordedHash.equals(actualHash)) {
   464                     if (!Arrays.equals(recordedHash, actualHash)) {
   444                         fail("Hash of %s (%s) differs to expected hash (%s)" +
   465                         fail("Hash of %s (%s) differs to expected hash (%s)" +
   445                              " recorded in %s", dn, actualHash, recordedHash,
   466                              " recorded in %s", dn, toHexString(actualHash),
   446                              descriptor.name());
   467                              toHexString(recordedHash), descriptor.name());
   447                     }
   468                     }
   448                 }
   469                 }
   449             }
   470             }
   450 
   471 
   451         }
   472         }
       
   473     }
       
   474 
       
   475     private static String toHexString(byte[] ba) {
       
   476         StringBuilder sb = new StringBuilder(ba.length * 2);
       
   477         for (byte b: ba) {
       
   478             sb.append(String.format("%02x", b & 0xff));
       
   479         }
       
   480         return sb.toString();
   452     }
   481     }
   453 
   482 
   454 
   483 
   455     /**
   484     /**
   456      * Computes the readability graph for the modules in the given Configuration.
   485      * Computes the readability graph for the modules in the given Configuration.
   457      *
   486      *
   458      * The readability graph is created by propagating "requires" through the
   487      * The readability graph is created by propagating "requires" through the
   459      * "public requires" edges of the module dependence graph. So if the module
   488      * "requires transitive" edges of the module dependence graph. So if the
   460      * dependence graph has m1 requires m2 && m2 requires public m3 then the
   489      * module dependence graph has m1 requires m2 && m2 requires transitive m3
   461      * resulting readability graph will contain m1 reads m2, m1 reads m3, and
   490      * then the resulting readability graph will contain m1 reads m2, m1 reads m3,
   462      * m2 reads m3.
   491      * and m2 reads m3.
   463      */
   492      */
   464     private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) {
   493     private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) {
       
   494 
       
   495         // initial capacity of maps to avoid resizing
       
   496         int capacity = 1 + (4 * nameToReference.size())/ 3;
   465 
   497 
   466         // the "reads" graph starts as a module dependence graph and
   498         // the "reads" graph starts as a module dependence graph and
   467         // is iteratively updated to be the readability graph
   499         // is iteratively updated to be the readability graph
   468         Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>();
   500         Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>(capacity);
   469 
   501 
   470         // the "requires public" graph, contains requires public edges only
   502         // the "requires transitive" graph, contains requires transitive edges only
   471         Map<ResolvedModule, Set<ResolvedModule>> g2 = new HashMap<>();
   503         Map<ResolvedModule, Set<ResolvedModule>> g2;
   472 
   504 
   473 
   505         // need "requires transitive" from the modules in parent configurations
   474         // need "requires public" from the modules in parent configurations as
   506         // as there may be selected modules that have a dependency on modules in
   475         // there may be selected modules that have a dependency on modules in
       
   476         // the parent configuration.
   507         // the parent configuration.
   477 
   508         if (Layer.boot() == null) {
   478         Configuration p = parent;
   509             g2 = new HashMap<>(capacity);
   479         while (p != null) {
   510         } else {
   480             for (ModuleDescriptor descriptor : p.descriptors()) {
   511             g2 = parents.stream()
   481                 String name = descriptor.name();
   512                 .flatMap(Configuration::configurations)
   482                 ResolvedModule m1 = p.findModule(name)
   513                 .distinct()
   483                     .orElseThrow(() -> new InternalError(name + " not found"));
   514                 .flatMap(c ->
   484                 for (ModuleDescriptor.Requires requires : descriptor.requires()) {
   515                     c.modules().stream().flatMap(m1 ->
   485                     if (requires.modifiers().contains(Modifier.PUBLIC)) {
   516                         m1.descriptor().requires().stream()
   486                         String dn = requires.name();
   517                             .filter(r -> r.modifiers().contains(Modifier.TRANSITIVE))
   487                         ResolvedModule m2 = p.findModule(dn)
   518                             .flatMap(r -> {
   488                             .orElseThrow(() -> new InternalError(dn + " not found"));
   519                                 Optional<ResolvedModule> m2 = c.findModule(r.name());
   489                         g2.computeIfAbsent(m1, k -> new HashSet<>()).add(m2);
   520                                 assert m2.isPresent()
   490                     }
   521                                         || r.modifiers().contains(Modifier.STATIC);
   491                 }
   522                                 return m2.stream();
   492             }
   523                             })
   493 
   524                             .map(m2 -> Map.entry(m1, m2))
   494             p = p.parent().orElse(null);
   525                     )
       
   526                 )
       
   527                 // stream of m1->m2
       
   528                 .collect(Collectors.groupingBy(Map.Entry::getKey,
       
   529                         HashMap::new,
       
   530                         Collectors.mapping(Map.Entry::getValue, Collectors.toSet())
       
   531             ));
   495         }
   532         }
   496 
   533 
   497         // populate g1 and g2 with the dependences from the selected modules
   534         // populate g1 and g2 with the dependences from the selected modules
   498 
   535 
   499         Map<String, ResolvedModule> nameToResolved = new HashMap<>();
   536         Map<String, ResolvedModule> nameToResolved = new HashMap<>(capacity);
   500 
   537 
   501         for (ModuleReference mref : nameToReference.values()) {
   538         for (ModuleReference mref : nameToReference.values()) {
   502             ModuleDescriptor descriptor = mref.descriptor();
   539             ModuleDescriptor descriptor = mref.descriptor();
   503             String name = descriptor.name();
   540             String name = descriptor.name();
   504 
   541 
   505             ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref);
   542             ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref);
   506 
   543 
   507             Set<ResolvedModule> reads = new HashSet<>();
   544             Set<ResolvedModule> reads = new HashSet<>();
   508             Set<ResolvedModule> requiresPublic = new HashSet<>();
   545             Set<ResolvedModule> requiresTransitive = new HashSet<>();
   509 
   546 
   510             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
   547             for (ModuleDescriptor.Requires requires : descriptor.requires()) {
   511                 String dn = requires.name();
   548                 String dn = requires.name();
   512 
   549 
   513                 ResolvedModule m2;
   550                 ResolvedModule m2 = null;
   514                 ModuleReference mref2 = nameToReference.get(dn);
   551                 ModuleReference mref2 = nameToReference.get(dn);
   515                 if (mref2 != null) {
   552                 if (mref2 != null) {
   516                     // same configuration
   553                     // same configuration
   517                     m2 = computeIfAbsent(nameToResolved, dn, cf, mref2);
   554                     m2 = computeIfAbsent(nameToResolved, dn, cf, mref2);
   518                 } else {
   555                 } else {
   519                     // parent configuration
   556                     // parent configuration
   520                     m2 = parent.findModule(dn).orElse(null);
   557                     m2 = findInParent(dn);
   521                     if (m2 == null) {
   558                     if (m2 == null) {
       
   559                         assert requires.modifiers().contains(Modifier.STATIC);
   522                         continue;
   560                         continue;
   523                     }
   561                     }
   524                 }
   562                 }
   525 
   563 
   526                 // m1 requires m2 => m1 reads m2
   564                 // m1 requires m2 => m1 reads m2
   527                 reads.add(m2);
   565                 reads.add(m2);
   528 
   566 
   529                 // m1 requires public m2
   567                 // m1 requires transitive m2
   530                 if (requires.modifiers().contains(Modifier.PUBLIC)) {
   568                 if (requires.modifiers().contains(Modifier.TRANSITIVE)) {
   531                     requiresPublic.add(m2);
   569                     requiresTransitive.add(m2);
   532                 }
   570                 }
   533 
   571 
   534             }
   572             }
   535 
   573 
   536             // automatic modules read all selected modules and all modules
   574             // automatic modules read all selected modules and all modules
   537             // in parent configurations
   575             // in parent configurations
   538             if (descriptor.isAutomatic()) {
   576             if (descriptor.isAutomatic()) {
   539 
   577 
   540                 // reads all selected modules
   578                 // reads all selected modules
   541                 // `requires public` all selected automatic modules
   579                 // `requires transitive` all selected automatic modules
   542                 for (ModuleReference mref2 : nameToReference.values()) {
   580                 for (ModuleReference mref2 : nameToReference.values()) {
   543                     ModuleDescriptor descriptor2 = mref2.descriptor();
   581                     ModuleDescriptor descriptor2 = mref2.descriptor();
   544                     String name2 = descriptor2.name();
   582                     String name2 = descriptor2.name();
   545 
   583 
   546                     if (!name.equals(name2)) {
   584                     if (!name.equals(name2)) {
   547                         ResolvedModule m2
   585                         ResolvedModule m2
   548                             = computeIfAbsent(nameToResolved, name2, cf, mref2);
   586                             = computeIfAbsent(nameToResolved, name2, cf, mref2);
   549                         reads.add(m2);
   587                         reads.add(m2);
   550                         if (descriptor2.isAutomatic())
   588                         if (descriptor2.isAutomatic())
   551                             requiresPublic.add(m2);
   589                             requiresTransitive.add(m2);
   552                     }
   590                     }
   553                 }
   591                 }
   554 
   592 
   555                 // reads all modules in parent configurations
   593                 // reads all modules in parent configurations
   556                 // `requires public` all automatic modules in parent configurations
   594                 // `requires transitive` all automatic modules in parent
   557                 p = parent;
   595                 // configurations
   558                 while (p != null) {
   596                 for (Configuration parent : parents) {
   559                     for (ResolvedModule m : p.modules()) {
   597                     parent.configurations()
   560                         reads.add(m);
   598                             .map(Configuration::modules)
   561                         if (m.reference().descriptor().isAutomatic())
   599                             .flatMap(Set::stream)
   562                             requiresPublic.add(m);
   600                             .forEach(m -> {
   563                     }
   601                                 reads.add(m);
   564                     p = p.parent().orElse(null);
   602                                 if (m.reference().descriptor().isAutomatic())
   565                 }
   603                                     requiresTransitive.add(m);
   566 
   604                             });
       
   605                 }
   567             }
   606             }
   568 
   607 
   569             g1.put(m1, reads);
   608             g1.put(m1, reads);
   570             g2.put(m1, requiresPublic);
   609             g2.put(m1, requiresTransitive);
   571         }
   610         }
   572 
   611 
   573         // Iteratively update g1 until there are no more requires public to propagate
   612         // Iteratively update g1 until there are no more requires transitive
       
   613         // to propagate
   574         boolean changed;
   614         boolean changed;
   575         Set<ResolvedModule> toAdd = new HashSet<>();
   615         List<ResolvedModule> toAdd = new ArrayList<>();
   576         do {
   616         do {
   577             changed = false;
   617             changed = false;
   578             for (Set<ResolvedModule> m1Reads : g1.values()) {
   618             for (Set<ResolvedModule> m1Reads : g1.values()) {
   579                 for (ResolvedModule m2 : m1Reads) {
   619                 for (ResolvedModule m2 : m1Reads) {
   580                     Set<ResolvedModule> m2RequiresPublic = g2.get(m2);
   620                     Set<ResolvedModule> m2RequiresTransitive = g2.get(m2);
   581                     if (m2RequiresPublic != null) {
   621                     if (m2RequiresTransitive != null) {
   582                         for (ResolvedModule m3 : m2RequiresPublic) {
   622                         for (ResolvedModule m3 : m2RequiresTransitive) {
   583                             if (!m1Reads.contains(m3)) {
   623                             if (!m1Reads.contains(m3)) {
   584                                 // m1 reads m2, m2 requires public m3
   624                                 // m1 reads m2, m2 requires transitive m3
   585                                 // => need to add m1 reads m3
   625                                 // => need to add m1 reads m3
   586                                 toAdd.add(m3);
   626                                 toAdd.add(m3);
   587                             }
   627                             }
   588                         }
   628                         }
   589                     }
   629                     }
   653                     }
   693                     }
   654 
   694 
   655                     // source is exported to descriptor2
   695                     // source is exported to descriptor2
   656                     String source = export.source();
   696                     String source = export.source();
   657                     ModuleDescriptor other
   697                     ModuleDescriptor other
   658                         = packageToExporter.put(source, descriptor2);
   698                         = packageToExporter.putIfAbsent(source, descriptor2);
   659 
   699 
   660                     if (other != null && other != descriptor2) {
   700                     if (other != null && other != descriptor2) {
   661                         // package might be local to descriptor1
   701                         // package might be local to descriptor1
   662                         if (other == descriptor1) {
   702                         if (other == descriptor1) {
   663                             fail("Module %s contains package %s"
   703                             fail("Module %s contains package %s"
   690                              descriptor1.name(), pn);
   730                              descriptor1.name(), pn);
   691                     }
   731                     }
   692                 }
   732                 }
   693 
   733 
   694                 // provides S
   734                 // provides S
   695                 for (Map.Entry<String, ModuleDescriptor.Provides> entry :
   735                 for (ModuleDescriptor.Provides provides : descriptor1.provides()) {
   696                         descriptor1.provides().entrySet()) {
   736                     String pn = packageName(provides.service());
   697                     String service = entry.getKey();
       
   698                     ModuleDescriptor.Provides provides = entry.getValue();
       
   699 
       
   700                     String pn = packageName(service);
       
   701                     if (!packageToExporter.containsKey(pn)) {
   737                     if (!packageToExporter.containsKey(pn)) {
   702                         fail("Module %s does not read a module that exports %s",
   738                         fail("Module %s does not read a module that exports %s",
   703                              descriptor1.name(), pn);
   739                              descriptor1.name(), pn);
   704                     }
   740                     }
   705 
   741 
   713 
   749 
   714             }
   750             }
   715 
   751 
   716         }
   752         }
   717 
   753 
       
   754     }
       
   755 
       
   756     /**
       
   757      * Find a module of the given name in the parent configurations
       
   758      */
       
   759     private ResolvedModule findInParent(String mn) {
       
   760         for (Configuration parent : parents) {
       
   761             Optional<ResolvedModule> om = parent.findModule(mn);
       
   762             if (om.isPresent())
       
   763                 return om.get();
       
   764         }
       
   765         return null;
   718     }
   766     }
   719 
   767 
   720 
   768 
   721     /**
   769     /**
   722      * Invokes the beforeFinder to find method to find the given module.
   770      * Invokes the beforeFinder to find method to find the given module.
   753             Set<ModuleReference> afterModules = afterFinder.findAll();
   801             Set<ModuleReference> afterModules = afterFinder.findAll();
   754 
   802 
   755             if (afterModules.isEmpty())
   803             if (afterModules.isEmpty())
   756                 return beforeModules;
   804                 return beforeModules;
   757 
   805 
   758             if (beforeModules.isEmpty() && parent == Configuration.empty())
   806             if (beforeModules.isEmpty()
       
   807                     && parents.size() == 1
       
   808                     && parents.get(0) == Configuration.empty())
   759                 return afterModules;
   809                 return afterModules;
   760 
   810 
   761             Set<ModuleReference> result = new HashSet<>(beforeModules);
   811             Set<ModuleReference> result = new HashSet<>(beforeModules);
   762             for (ModuleReference mref : afterModules) {
   812             for (ModuleReference mref : afterModules) {
   763                 String name = mref.descriptor().name();
   813                 String name = mref.descriptor().name();
   764                 if (!beforeFinder.find(name).isPresent()
   814                 if (!beforeFinder.find(name).isPresent()
   765                         && !parent.findModule(name).isPresent())
   815                         && findInParent(name) == null) {
   766                     result.add(mref);
   816                     result.add(mref);
       
   817                 }
   767             }
   818             }
   768 
   819 
   769             return result;
   820             return result;
   770 
   821 
   771         } catch (FindException e) {
   822         } catch (FindException e) {