1 /*
   2  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /**
  25  * @test
  26  * @bug 8176537
  27  * @summary Check JDK modules have no qualified export to any upgradeable module
  28  * @modules java.base/jdk.internal.module
  29  * @run main JdkQualifiedExportTest
  30  */
  31 
  32 import jdk.internal.module.ModuleHashes;
  33 import jdk.internal.module.ModuleInfo;
  34 
  35 import java.io.IOException;
  36 import java.io.InputStream;
  37 import java.io.UncheckedIOException;

  38 import java.lang.module.ModuleDescriptor;
  39 import java.lang.module.ModuleDescriptor.Exports;
  40 import java.lang.module.ModuleDescriptor.Opens;
  41 import java.lang.module.ModuleFinder;
  42 import java.lang.module.ModuleReference;
  43 import java.util.Collections;
  44 import java.util.Comparator;
  45 import java.util.HashMap;
  46 import java.util.HashSet;
  47 import java.util.Map;
  48 import java.util.Set;
  49 
  50 public class JdkQualifiedExportTest {
  51     public static void main(String... args) {
  52         // check all system modules
  53         ModuleFinder.ofSystem().findAll()
  54                     .stream()
  55                     .map(ModuleReference::descriptor)
  56                     .sorted(Comparator.comparing(ModuleDescriptor::name))
  57                     .forEach(JdkQualifiedExportTest::check);
  58     }
  59 
  60     static void check(ModuleDescriptor md) {
  61         // skip checking if this is an upgradeable module
  62         if (!HashedModules.contains(md.name())) {
  63             return;
  64         }
  65 
  66         checkExports(md);
  67         checkOpens(md);
  68     }
  69 
  70     static Set<String> KNOWN_EXCEPTIONS =
  71         Set.of("java.xml/com.sun.xml.internal.stream.writers",

  72                "jdk.jsobject/jdk.internal.netscape.javascript.spi");
  73     static Set<String> DEPLOY_MODULES =
  74         Set.of("jdk.deploy", "jdk.plugin", "jdk.javaws");
  75 
  76     static void checkExports(ModuleDescriptor md) {
  77         // build a map of upgradeable module to Exports that are qualified to it
  78         // skip the qualified exports
  79         Map<String, Set<Exports>> targetToExports = new HashMap<>();
  80         md.exports().stream()
  81           .filter(Exports::isQualified)
  82           .forEach(e -> e.targets().stream()
  83                          .filter(mn -> !HashedModules.contains(mn) &&
  84                                            ModuleFinder.ofSystem().find(mn).isPresent())
  85                          .forEach(t -> targetToExports.computeIfAbsent(t, _k -> new HashSet<>())
  86                                                       .add(e)));
  87 
  88         if (targetToExports.size() > 0) {
  89             String mn = md.name();
  90 
  91             System.err.println(mn);
  92             targetToExports.entrySet().stream()
  93                 .sorted(Map.Entry.comparingByKey())
  94                 .forEach(e -> {
  95                     e.getValue().stream()
  96                      .forEach(exp -> System.err.format("    exports %s to %s%n",
  97                                                        exp.source(), e.getKey()));
  98                 });
  99 
 100             // workaround until all qualified exports to upgradeable modules
 101             // are eliminated
 102             if (targetToExports.entrySet().stream()
 103                     .filter(e -> !DEPLOY_MODULES.contains(e.getKey()))
 104                     .flatMap(e -> e.getValue().stream())
 105                     .anyMatch(e -> !KNOWN_EXCEPTIONS.contains(mn + "/" + e.source()))) {
 106                 throw new RuntimeException(mn + " can't export package to upgradeable modules");
 107             }
 108         }
 109     }
 110 
 111     static void checkOpens(ModuleDescriptor md) {
 112         // build a map of upgradeable module to Exports that are qualified to it
 113         // skip the qualified exports
 114         Map<String, Set<Opens>> targetToOpens = new HashMap<>();
 115         md.opens().stream()
 116             .filter(Opens::isQualified)
 117             .forEach(e -> e.targets().stream()
 118                            .filter(mn -> !HashedModules.contains(mn) &&
 119                                             ModuleFinder.ofSystem().find(mn).isPresent())
 120                            .forEach(t -> targetToOpens.computeIfAbsent(t, _k -> new HashSet<>())
 121                                                       .add(e)));
 122 
 123         if (targetToOpens.size() > 0) {
 124             String mn = md.name();
 125 
 126             System.err.println(mn);
 127             targetToOpens.entrySet().stream()
 128                 .sorted(Map.Entry.comparingByKey())
 129                 .forEach(e -> {
 130                     e.getValue().stream()
 131                      .forEach(exp -> System.err.format("    opens %s to %s%n",
 132                                                        exp.source(), e.getKey()));
 133                 });
 134 
 135             throw new RuntimeException(mn + " can't open package to upgradeable modules");
 136         }
 137     }
 138 

















 139     private static class HashedModules {
 140         static Set<String> HASHED_MODULES = hashedModules();
 141 
 142         static Set<String> hashedModules() {
 143             Module javaBase = Object.class.getModule();
 144             try (InputStream in = javaBase.getResourceAsStream("module-info.class")) {
 145                 ModuleInfo.Attributes attrs = ModuleInfo.read(in, null);
 146                 ModuleHashes hashes = attrs.recordedHashes();
 147                 if (hashes == null)
 148                     return Collections.emptySet();
 149 
 150                 Set<String> names = new HashSet<>(hashes.names());
 151                 names.add(javaBase.getName());
 152                 return names;
 153             } catch (IOException e) {
 154                 throw new UncheckedIOException(e);
 155             }
 156         }
 157 
 158         /*
 159          * Returns true if the named module is tied with java.base,
 160          * i.e. non-upgradeable
 161          */
 162         static boolean contains(String mn) {
 163             return HASHED_MODULES.contains(mn);
 164         }
 165     }
 166 }
--- EOF ---