8176537: Add test to check JDK modules to have no qualifed exports to upgradeable modules
authormchung
Mon, 13 Mar 2017 10:24:16 -0700
changeset 44162 725cbaef3403
parent 44161 c0d4e4ffb0fd
child 44163 4aa8484b8eaa
child 44210 5a8499c4b32c
child 44247 b128e61f230e
8176537: Add test to check JDK modules to have no qualifed exports to upgradeable modules Reviewed-by: alanb
jdk/test/jdk/modules/etc/JdkQualifiedExportTest.java
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/jdk/modules/etc/JdkQualifiedExportTest.java	Mon Mar 13 10:24:16 2017 -0700
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8176537
+ * @summary Check JDK modules have no qualified export to any upgradeable module
+ * @modules java.base/jdk.internal.module
+ * @run main JdkQualifiedExportTest
+ */
+
+import jdk.internal.module.ModuleHashes;
+import jdk.internal.module.ModuleInfo;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleDescriptor.Exports;
+import java.lang.module.ModuleDescriptor.Opens;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+import java.lang.reflect.Module;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class JdkQualifiedExportTest {
+    public static void main(String... args) {
+        // check all system modules
+        ModuleFinder.ofSystem().findAll()
+                    .stream()
+                    .map(ModuleReference::descriptor)
+                    .sorted(Comparator.comparing(ModuleDescriptor::name))
+                    .forEach(JdkQualifiedExportTest::check);
+    }
+
+    static void check(ModuleDescriptor md) {
+        // skip checking if this is an upgradeable module
+        if (!HashedModules.contains(md.name())) {
+            return;
+        }
+
+        checkExports(md);
+        checkOpens(md);
+    }
+
+    static Set<String> KNOWN_EXCEPTIONS =
+        Set.of("java.xml/com.sun.xml.internal.stream.writers",
+               "jdk.jsobject/jdk.internal.netscape.javascript.spi");
+    static Set<String> DEPLOY_MODULES =
+        Set.of("jdk.deploy", "jdk.plugin", "jdk.javaws");
+
+    static void checkExports(ModuleDescriptor md) {
+        // build a map of upgradeable module to Exports that are qualified to it
+        // skip the qualified exports
+        Map<String, Set<Exports>> targetToExports = new HashMap<>();
+        md.exports().stream()
+          .filter(Exports::isQualified)
+          .forEach(e -> e.targets().stream()
+                         .filter(mn -> !HashedModules.contains(mn) &&
+                                           ModuleFinder.ofSystem().find(mn).isPresent())
+                         .forEach(t -> targetToExports.computeIfAbsent(t, _k -> new HashSet<>())
+                                                      .add(e)));
+
+        if (targetToExports.size() > 0) {
+            String mn = md.name();
+
+            System.err.println(mn);
+            targetToExports.entrySet().stream()
+                .sorted(Map.Entry.comparingByKey())
+                .forEach(e -> {
+                    e.getValue().stream()
+                     .forEach(exp -> System.err.format("    exports %s to %s%n",
+                                                       exp.source(), e.getKey()));
+                });
+
+            // workaround until all qualified exports to upgradeable modules
+            // are eliminated
+            if (targetToExports.entrySet().stream()
+                    .filter(e -> !DEPLOY_MODULES.contains(e.getKey()))
+                    .flatMap(e -> e.getValue().stream())
+                    .anyMatch(e -> !KNOWN_EXCEPTIONS.contains(mn + "/" + e.source()))) {
+                throw new RuntimeException(mn + " can't export package to upgradeable modules");
+            }
+        }
+    }
+
+    static void checkOpens(ModuleDescriptor md) {
+        // build a map of upgradeable module to Exports that are qualified to it
+        // skip the qualified exports
+        Map<String, Set<Opens>> targetToOpens = new HashMap<>();
+        md.opens().stream()
+            .filter(Opens::isQualified)
+            .forEach(e -> e.targets().stream()
+                           .filter(mn -> !HashedModules.contains(mn) &&
+                                            ModuleFinder.ofSystem().find(mn).isPresent())
+                           .forEach(t -> targetToOpens.computeIfAbsent(t, _k -> new HashSet<>())
+                                                      .add(e)));
+
+        if (targetToOpens.size() > 0) {
+            String mn = md.name();
+
+            System.err.println(mn);
+            targetToOpens.entrySet().stream()
+                .sorted(Map.Entry.comparingByKey())
+                .forEach(e -> {
+                    e.getValue().stream()
+                     .forEach(exp -> System.err.format("    opens %s to %s%n",
+                                                       exp.source(), e.getKey()));
+                });
+
+            throw new RuntimeException(mn + " can't open package to upgradeable modules");
+        }
+    }
+
+    private static class HashedModules {
+        static Set<String> HASHED_MODULES = hashedModules();
+
+        static Set<String> hashedModules() {
+            Module javaBase = Object.class.getModule();
+            try (InputStream in = javaBase.getResourceAsStream("module-info.class")) {
+                ModuleInfo.Attributes attrs = ModuleInfo.read(in, null);
+                ModuleHashes hashes = attrs.recordedHashes();
+                if (hashes == null)
+                    return Collections.emptySet();
+
+                Set<String> names = new HashSet<>(hashes.names());
+                names.add(javaBase.getName());
+                return names;
+            } catch (IOException e) {
+                throw new UncheckedIOException(e);
+            }
+        }
+
+        /*
+         * Returns true if the named module is tied with java.base,
+         * i.e. non-upgradeable
+         */
+        static boolean contains(String mn) {
+            return HASHED_MODULES.contains(mn);
+        }
+    }
+}