< prev index next >

src/java.base/share/classes/java/lang/module/Resolver.java

Print this page




  30 import java.lang.module.ModuleDescriptor.Requires.Modifier;
  31 import java.lang.reflect.Layer;
  32 import java.util.ArrayDeque;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.Collection;
  36 import java.util.Deque;
  37 import java.util.HashMap;
  38 import java.util.HashSet;
  39 import java.util.LinkedHashSet;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.Objects;
  43 import java.util.Optional;
  44 import java.util.Set;
  45 import java.util.StringJoiner;
  46 import java.util.stream.Collectors;
  47 
  48 import jdk.internal.module.ModuleHashes;
  49 import jdk.internal.module.ModuleReferenceImpl;

  50 
  51 /**
  52  * The resolver used by {@link Configuration#resolve} and {@link
  53  * Configuration#resolveAndBind}.
  54  *
  55  * @implNote The resolver is used at VM startup and so deliberately avoids
  56  * using lambda and stream usages in code paths used during startup.
  57  */
  58 
  59 final class Resolver {
  60 
  61     private final ModuleFinder beforeFinder;
  62     private final List<Configuration> parents;
  63     private final ModuleFinder afterFinder;
  64     private final PrintStream traceOutput;
  65 
  66     // maps module name to module reference
  67     private final Map<String, ModuleReference> nameToReference = new HashMap<>();
  68 
  69     // module constraints on target platform
  70     private String osName;
  71     private String osArch;
  72     private String osVersion;
  73 
  74     String osName() { return osName; }
  75     String osArch() { return osArch; }
  76     String osVersion() { return osVersion; }
  77 
  78     /**
  79      * @throws IllegalArgumentException if there are more than one parent and
  80      *         the constraints on the target platform conflict
  81      */
  82     Resolver(ModuleFinder beforeFinder,
  83              List<Configuration> parents,
  84              ModuleFinder afterFinder,
  85              PrintStream traceOutput) {
  86         this.beforeFinder = beforeFinder;
  87         this.parents = parents;
  88         this.afterFinder = afterFinder;
  89         this.traceOutput = traceOutput;
  90 
  91         // record constraints on target platform, checking that they don't conflict
  92         for (Configuration parent : parents) {
  93             String value = parent.osName();
  94             if (value != null) {
  95                 if (osName == null) {
  96                     osName = value;
  97                 } else {
  98                     if (!value.equals(osName)) {
  99                         failParentConflict("Operating System", osName, value);
 100                     }
 101                 }
 102             }
 103             value = parent.osArch();
 104             if (value != null) {
 105                 if (osArch == null) {
 106                     osArch = value;
 107                 } else {
 108                     if (!value.equals(osArch)) {
 109                         failParentConflict("OS architecture", osArch, value);
 110                     }
 111                 }
 112             }
 113             value = parent.osVersion();
 114             if (value != null) {
 115                 if (osVersion == null) {
 116                     osVersion  = value;
 117                 } else {
 118                     if (!value.equals(osVersion)) {
 119                         failParentConflict("OS version", osVersion, value);
 120                     }
 121                 }
 122             }
 123         }
 124     }
 125 
 126     private void failParentConflict(String constraint, String s1, String s2) {
 127         String msg = "Parents have conflicting constraints on target "
 128                      + constraint + ": " + s1 + ", " + s2;
 129         throw new IllegalArgumentException(msg);
 130     }
 131 
 132     /**
 133      * Resolves the given named modules.
 134      *
 135      * @throws ResolutionException
 136      */
 137     Resolver resolve(Collection<String> roots) {
 138 
 139         // create the visit stack to get us started
 140         Deque<ModuleDescriptor> q = new ArrayDeque<>();
 141         for (String root : roots) {
 142 


 301                                     }
 302                                 }
 303                             }
 304                         }
 305                     }
 306                 }
 307             }
 308 
 309             candidateConsumers = resolve(q);
 310         } while (!candidateConsumers.isEmpty());
 311 
 312         return this;
 313     }
 314 
 315 
 316     /**
 317      * Add the module to the nameToReference map. Also check any constraints on
 318      * the target platform with the constraints of other modules.
 319      */
 320     private void addFoundModule(ModuleReference mref) {
 321         ModuleDescriptor descriptor = mref.descriptor();
 322         nameToReference.put(descriptor.name(), mref);





 323 
 324         if (descriptor.osName().isPresent()
 325                 || descriptor.osArch().isPresent()
 326                 || descriptor.osVersion().isPresent())
 327             checkTargetConstraints(descriptor);
 328     }
 329 
 330     /**
 331      * Check that the module's constraints on the target platform do not
 332      * conflict with the constraints of other modules resolved so far or
 333      * modules in parent configurations.
 334      */
 335     private void checkTargetConstraints(ModuleDescriptor descriptor) {
 336         String value = descriptor.osName().orElse(null);
 337         if (value != null) {
 338             if (osName == null) {
 339                 osName = value;
 340             } else {
 341                 if (!value.equals(osName)) {
 342                     failTargetConstraint(descriptor);
 343                 }
 344             }
 345         }
 346         value = descriptor.osArch().orElse(null);
 347         if (value != null) {
 348             if (osArch == null) {
 349                 osArch = value;
 350             } else {
 351                 if (!value.equals(osArch)) {
 352                     failTargetConstraint(descriptor);
 353                 }
 354             }
 355         }
 356         value = descriptor.osVersion().orElse(null);
 357         if (value != null) {
 358             if (osVersion == null) {
 359                 osVersion = value;
 360             } else {
 361                 if (!value.equals(osVersion)) {
 362                     failTargetConstraint(descriptor);
 363                 }
 364             }
 365         }
 366     }
 367 
 368     private void failTargetConstraint(ModuleDescriptor md) {
 369         String s1 = targetAsString(osName, osArch, osVersion);
 370         String s2 = targetAsString(md);
 371         findFail("Module %s has constraints on target platform that conflict" +
 372                  " with other modules: %s, %s", md.name(), s1, s2);
 373     }
 374 
 375     private String targetAsString(ModuleDescriptor descriptor) {
 376         String osName = descriptor.osName().orElse(null);
 377         String osArch = descriptor.osArch().orElse(null);
 378         String osVersion = descriptor.osVersion().orElse(null);
 379         return targetAsString(osName, osArch, osVersion);
 380     }
 381 
 382     private String targetAsString(String osName, String osArch, String osVersion) {
 383         return new StringJoiner("-")
 384                 .add(Objects.toString(osName, "*"))
 385                 .add(Objects.toString(osArch, "*"))
 386                 .add(Objects.toString(osVersion, "*"))
 387                 .toString();
 388     }
 389 
 390 
 391     /**
 392      * Execute post-resolution checks and returns the module graph of resolved
 393      * modules as {@code Map}. The resolved modules will be in the given
 394      * configuration.
 395      *
 396      * @param check {@true} to execute the post resolution checks
 397      */
 398     Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf,
 399                                                     boolean check)
 400     {
 401         if (isTracing()) {
 402             trace("Result:");
 403             Set<String> names = nameToReference.keySet();
 404             names.stream().sorted().forEach(name -> trace("  %s", name));
 405         }
 406 


 695      * Equivalent to
 696      * <pre>{@code
 697      *     map.computeIfAbsent(name, k -> new ResolvedModule(cf, mref))
 698      * </pre>}
 699      */
 700     private ResolvedModule computeIfAbsent(Map<String, ResolvedModule> map,
 701                                            String name,
 702                                            Configuration cf,
 703                                            ModuleReference mref)
 704     {
 705         ResolvedModule m = map.get(name);
 706         if (m == null) {
 707             m = new ResolvedModule(cf, mref);
 708             map.put(name, m);
 709         }
 710         return m;
 711     }
 712 
 713 
 714     /**
 715      * Checks the readability graph to ensure that no two modules export the
 716      * same package to a module. This includes the case where module M has
 717      * a local package P and M reads another module that exports P to M.
 718      * Also checks the uses/provides of module M to ensure that it reads a
 719      * module that exports the package of the service type to M.









 720      */
 721     private void checkExportSuppliers(Map<ResolvedModule, Set<ResolvedModule>> graph) {
 722 
 723         for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e : graph.entrySet()) {
 724             ModuleDescriptor descriptor1 = e.getKey().descriptor();





 725 
 726             // the map of packages that are local or exported to descriptor1
 727             Map<String, ModuleDescriptor> packageToExporter = new HashMap<>();
 728 
 729             // local packages
 730             Set<String> packages = descriptor1.packages();
 731             for (String pn : packages) {
 732                 packageToExporter.put(pn, descriptor1);
 733             }
 734 
 735             // descriptor1 reads descriptor2
 736             Set<ResolvedModule> reads = e.getValue();
 737             for (ResolvedModule endpoint : reads) {
 738                 ModuleDescriptor descriptor2 = endpoint.descriptor();
 739 











 740                 if (descriptor2.isAutomatic()) {
 741                     // automatic modules read self and export all packages
 742                     if (descriptor2 != descriptor1){
 743                         for (String source : descriptor2.packages()) {
 744                             ModuleDescriptor supplier
 745                                 = packageToExporter.putIfAbsent(source, descriptor2);
 746 
 747                             // descriptor2 and 'supplier' export source to descriptor1
 748                             if (supplier != null) {
 749                                 failTwoSuppliers(descriptor1, source, descriptor2, supplier);
 750                             }
 751                         }
 752 
 753                     }
 754                 } else {
 755                     for (ModuleDescriptor.Exports export : descriptor2.exports()) {
 756                         if (export.isQualified()) {
 757                             if (!export.targets().contains(descriptor1.name()))
 758                                 continue;
 759                         }
 760 
 761                         // source is exported by descriptor2
 762                         String source = export.source();




  30 import java.lang.module.ModuleDescriptor.Requires.Modifier;
  31 import java.lang.reflect.Layer;
  32 import java.util.ArrayDeque;
  33 import java.util.ArrayList;
  34 import java.util.Arrays;
  35 import java.util.Collection;
  36 import java.util.Deque;
  37 import java.util.HashMap;
  38 import java.util.HashSet;
  39 import java.util.LinkedHashSet;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.Objects;
  43 import java.util.Optional;
  44 import java.util.Set;
  45 import java.util.StringJoiner;
  46 import java.util.stream.Collectors;
  47 
  48 import jdk.internal.module.ModuleHashes;
  49 import jdk.internal.module.ModuleReferenceImpl;
  50 import jdk.internal.module.ModuleTarget;
  51 
  52 /**
  53  * The resolver used by {@link Configuration#resolve} and {@link
  54  * Configuration#resolveAndBind}.
  55  *
  56  * @implNote The resolver is used at VM startup and so deliberately avoids
  57  * using lambda and stream usages in code paths used during startup.
  58  */
  59 
  60 final class Resolver {
  61 
  62     private final ModuleFinder beforeFinder;
  63     private final List<Configuration> parents;
  64     private final ModuleFinder afterFinder;
  65     private final PrintStream traceOutput;
  66 
  67     // maps module name to module reference
  68     private final Map<String, ModuleReference> nameToReference = new HashMap<>();
  69 
  70     // module constraints on target platform
  71     private String osName;
  72     private String osArch;

  73 
  74     String osName() { return osName; }
  75     String osArch() { return osArch; }

  76 
  77     /**
  78      * @throws IllegalArgumentException if there are more than one parent and
  79      *         the constraints on the target platform conflict
  80      */
  81     Resolver(ModuleFinder beforeFinder,
  82              List<Configuration> parents,
  83              ModuleFinder afterFinder,
  84              PrintStream traceOutput) {
  85         this.beforeFinder = beforeFinder;
  86         this.parents = parents;
  87         this.afterFinder = afterFinder;
  88         this.traceOutput = traceOutput;
  89 
  90         // record constraints on target platform, checking that they don't conflict
  91         for (Configuration parent : parents) {
  92             String value = parent.osName();
  93             if (value != null) {
  94                 if (osName == null) {
  95                     osName = value;
  96                 } else {
  97                     if (!value.equals(osName)) {
  98                         failParentConflict("Operating System", osName, value);
  99                     }
 100                 }
 101             }
 102             value = parent.osArch();
 103             if (value != null) {
 104                 if (osArch == null) {
 105                     osArch = value;
 106                 } else {
 107                     if (!value.equals(osArch)) {
 108                         failParentConflict("OS architecture", osArch, value);
 109                     }
 110                 }
 111             }










 112         }
 113     }
 114 
 115     private void failParentConflict(String constraint, String s1, String s2) {
 116         String msg = "Parents have conflicting constraints on target "
 117                      + constraint + ": " + s1 + ", " + s2;
 118         throw new IllegalArgumentException(msg);
 119     }
 120 
 121     /**
 122      * Resolves the given named modules.
 123      *
 124      * @throws ResolutionException
 125      */
 126     Resolver resolve(Collection<String> roots) {
 127 
 128         // create the visit stack to get us started
 129         Deque<ModuleDescriptor> q = new ArrayDeque<>();
 130         for (String root : roots) {
 131 


 290                                     }
 291                                 }
 292                             }
 293                         }
 294                     }
 295                 }
 296             }
 297 
 298             candidateConsumers = resolve(q);
 299         } while (!candidateConsumers.isEmpty());
 300 
 301         return this;
 302     }
 303 
 304 
 305     /**
 306      * Add the module to the nameToReference map. Also check any constraints on
 307      * the target platform with the constraints of other modules.
 308      */
 309     private void addFoundModule(ModuleReference mref) {
 310         String mn = mref.descriptor().name();
 311 
 312         if (mref instanceof ModuleReferenceImpl) {
 313             ModuleTarget target = ((ModuleReferenceImpl)mref).moduleTarget();
 314             if (target != null)
 315                 checkTargetConstraints(mn, target);
 316         }
 317 
 318         nameToReference.put(mn, mref);



 319     }
 320 
 321     /**
 322      * Check that the module's constraints on the target platform do not
 323      * conflict with the constraints of other modules resolved so far or
 324      * modules in parent configurations.
 325      */
 326     private void checkTargetConstraints(String mn, ModuleTarget target) {
 327         String value = target.osName();
 328         if (value != null) {
 329             if (osName == null) {
 330                 osName = value;
 331             } else {
 332                 if (!value.equals(osName)) {
 333                     failTargetConstraint(mn, target);
 334                 }
 335             }
 336         }
 337         value = target.osArch();
 338         if (value != null) {
 339             if (osArch == null) {
 340                 osArch = value;
 341             } else {
 342                 if (!value.equals(osArch)) {
 343                     failTargetConstraint(mn, target);










 344                 }
 345             }
 346         }
 347     }
 348 
 349     private void failTargetConstraint(String mn, ModuleTarget target) {
 350         String s1 = targetAsString(osName, osArch);
 351         String s2 = targetAsString(target.osName(), target.osArch());
 352         findFail("Module %s has constraints on target platform (%s) that"
 353                  + " conflict with other modules: %s", mn, s1, s2);
 354     }
 355 
 356     private String targetAsString(ModuleTarget target) {
 357         return targetAsString(target.osName(), target.osArch());



 358     }
 359 
 360     private String targetAsString(String osName, String osArch) {
 361         return new StringJoiner("-")
 362                 .add(Objects.toString(osName, "*"))
 363                 .add(Objects.toString(osArch, "*"))

 364                 .toString();
 365     }
 366 
 367 
 368     /**
 369      * Execute post-resolution checks and returns the module graph of resolved
 370      * modules as {@code Map}. The resolved modules will be in the given
 371      * configuration.
 372      *
 373      * @param check {@true} to execute the post resolution checks
 374      */
 375     Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf,
 376                                                     boolean check)
 377     {
 378         if (isTracing()) {
 379             trace("Result:");
 380             Set<String> names = nameToReference.keySet();
 381             names.stream().sorted().forEach(name -> trace("  %s", name));
 382         }
 383 


 672      * Equivalent to
 673      * <pre>{@code
 674      *     map.computeIfAbsent(name, k -> new ResolvedModule(cf, mref))
 675      * </pre>}
 676      */
 677     private ResolvedModule computeIfAbsent(Map<String, ResolvedModule> map,
 678                                            String name,
 679                                            Configuration cf,
 680                                            ModuleReference mref)
 681     {
 682         ResolvedModule m = map.get(name);
 683         if (m == null) {
 684             m = new ResolvedModule(cf, mref);
 685             map.put(name, m);
 686         }
 687         return m;
 688     }
 689 
 690 
 691     /**
 692      * Checks the readability graph to ensure that
 693      * <ol>
 694      *   <li><p> A module does not read two or more modules with the same name.
 695      *   This includes the case where a module reads another another with the
 696      *   same name as itself. </p></li>
 697      *   <li><p> Two or more modules in the configuration don't export the same
 698      *   package to a module that reads both. This includes the case where a
 699      *   module {@code M} containing package {@code p} reads another module
 700      *   that exports {@code p} to {@code M}. </p></li>
 701      *   <li><p> A module {@code M} doesn't declare that it "{@code uses p.S}"
 702      *   or "{@code provides p.S with ...}" but package {@code p} is neither
 703      *   in module {@code M} nor exported to {@code M} by any module that
 704      *   {@code M} reads. </p></li>
 705      * </ol>
 706      */
 707     private void checkExportSuppliers(Map<ResolvedModule, Set<ResolvedModule>> graph) {
 708 
 709         for (Map.Entry<ResolvedModule, Set<ResolvedModule>> e : graph.entrySet()) {
 710             ModuleDescriptor descriptor1 = e.getKey().descriptor();
 711             String name1 = descriptor1.name();
 712 
 713             // the names of the modules that are read (including self)
 714             Set<String> names = new HashSet<>();
 715             names.add(name1);
 716 
 717             // the map of packages that are local or exported to descriptor1
 718             Map<String, ModuleDescriptor> packageToExporter = new HashMap<>();
 719 
 720             // local packages
 721             Set<String> packages = descriptor1.packages();
 722             for (String pn : packages) {
 723                 packageToExporter.put(pn, descriptor1);
 724             }
 725 
 726             // descriptor1 reads descriptor2
 727             Set<ResolvedModule> reads = e.getValue();
 728             for (ResolvedModule endpoint : reads) {
 729                 ModuleDescriptor descriptor2 = endpoint.descriptor();
 730 
 731                 String name2 = descriptor2.name();
 732                 if (descriptor2 != descriptor1 && !names.add(name2)) {
 733                     if (name2.equals(name1)) {
 734                         resolveFail("Module %s reads another module named %s",
 735                                     name1, name1);
 736                     } else{
 737                         resolveFail("Module %s reads more than one module named %s",
 738                                      name1, name2);
 739                     }
 740                 }
 741 
 742                 if (descriptor2.isAutomatic()) {
 743                     // automatic modules read self and export all packages
 744                     if (descriptor2 != descriptor1) {
 745                         for (String source : descriptor2.packages()) {
 746                             ModuleDescriptor supplier
 747                                 = packageToExporter.putIfAbsent(source, descriptor2);
 748 
 749                             // descriptor2 and 'supplier' export source to descriptor1
 750                             if (supplier != null) {
 751                                 failTwoSuppliers(descriptor1, source, descriptor2, supplier);
 752                             }
 753                         }
 754 
 755                     }
 756                 } else {
 757                     for (ModuleDescriptor.Exports export : descriptor2.exports()) {
 758                         if (export.isQualified()) {
 759                             if (!export.targets().contains(descriptor1.name()))
 760                                 continue;
 761                         }
 762 
 763                         // source is exported by descriptor2
 764                         String source = export.source();


< prev index next >