/* * 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.Configuration; 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.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 KNOWN_EXCEPTIONS = Set.of("jdk.internal.vm.ci/jdk.vm.ci.services", "jdk.jsobject/jdk.internal.netscape.javascript.spi"); static void checkExports(ModuleDescriptor md) { // build a map of upgradeable module to Exports that are qualified to it // skip the qualified exports Map> targetToExports = new HashMap<>(); md.exports().stream() .filter(Exports::isQualified) .forEach(e -> e.targets().stream() .filter(mn -> accept(md, mn)) .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())); }); // no qualified exports to upgradeable modules are expected // except the known exception cases if (targetToExports.entrySet().stream() .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> targetToOpens = new HashMap<>(); md.opens().stream() .filter(Opens::isQualified) .forEach(e -> e.targets().stream() .filter(mn -> accept(md, mn)) .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"); } } /** * Returns true if target is an upgradeable module but not required * by the source module directly and indirectly. */ private static boolean accept(ModuleDescriptor source, String target) { if (HashedModules.contains(target)) return false; if (!ModuleFinder.ofSystem().find(target).isPresent()) return false; Configuration cf = Configuration.empty().resolve(ModuleFinder.of(), ModuleFinder.ofSystem(), Set.of(source.name())); return !cf.findModule(target).isPresent(); } private static class HashedModules { static Set HASHED_MODULES = hashedModules(); static Set 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 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); } } }