Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/make/src/classes/build/tools/deps/CheckDeps.java
32287 views
/*1* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package build.tools.deps;2627import java.nio.file.DirectoryStream;28import java.nio.file.Files;29import java.nio.file.Path;30import java.nio.file.Paths;31import java.nio.charset.StandardCharsets;32import java.util.Set;33import java.util.HashSet;34import java.util.Map;35import java.util.HashMap;36import java.util.Enumeration;37import java.util.Properties;38import java.util.jar.JarEntry;39import java.util.jar.JarFile;40import java.io.InputStream;41import java.io.InputStreamReader;42import java.net.URL;4344import com.sun.tools.classfile.ClassFile;45import com.sun.tools.classfile.Dependencies;46import com.sun.tools.classfile.Dependency;4748/**49* A simple tool to check the JAR files in a JRE image to ensure that there50* aren't any references to types that do not exist. The tool is intended to51* be used in the JDK "profiles" build to help ensure that the profile52* definitions are kept up to date.53*/5455public class CheckDeps {5657// classfile API for finding dependencies58static final Dependency.Finder finder = Dependencies.getClassDependencyFinder();5960// "known types", found in rt.jar or other JAR files61static final Set<String> knownTypes = new HashSet<>();6263// References to unknown types. The map key is the unknown type, the64// map value is the set of classes that reference it.65static final Map<String,Set<String>> unknownRefs = new HashMap<>();6667// The property name is the name of an unknown type that is allowed to be68// references. The property value is a comma separated list of the types69// that are allowed to reference it. The list also includes the names of70// the profiles that the reference is allowed.71static final Properties allowedBadRefs = new Properties();7273/**74* Returns the class name for the given class file. In the case of inner75* classes then the enclosing class is returned in order to keep the76* rules simple.77*/78static String toClassName(String s) {79int i = s.indexOf('$');80if (i > 0)81s = s.substring(0, i);82return s.replace("/", ".");83}8485/**86* Analyze the dependencies of all classes in the given JAR file. The87* method updates knownTypes and unknownRefs as part of the analysis.88*/89static void analyzeDependencies(Path jarpath) throws Exception {90System.out.format("Analyzing %s%n", jarpath);91try (JarFile jf = new JarFile(jarpath.toFile())) {92Enumeration<JarEntry> entries = jf.entries();93while (entries.hasMoreElements()) {94JarEntry e = entries.nextElement();95String name = e.getName();96if (name.endsWith(".class")) {97ClassFile cf = ClassFile.read(jf.getInputStream(e));98for (Dependency d : finder.findDependencies(cf)) {99String origin = toClassName(d.getOrigin().getName());100String target = toClassName(d.getTarget().getName());101102// origin is now known103unknownRefs.remove(origin);104knownTypes.add(origin);105106// if the target is not known then record the reference107if (!knownTypes.contains(target)) {108Set<String> refs = unknownRefs.get(target);109if (refs == null) {110// first time seeing this unknown type111refs = new HashSet<>();112unknownRefs.put(target, refs);113}114refs.add(origin);115}116}117}118}119}120}121122/**123* We have closure (no references to types that do not exist) if124* unknownRefs is empty. When unknownRefs is not empty then it should125* only contain references that are allowed to be present (these are126* loaded from the refs.allowed properties file).127*128* @param the profile that is being tested, this determines the exceptions129* in {@code allowedBadRefs} that apply.130*131* @return {@code true} if there are no missing types or the only references132* to missing types are described by {@code allowedBadRefs}.133*/134static boolean checkClosure(String profile) {135// process the references to types that do not exist.136boolean fail = false;137for (Map.Entry<String,Set<String>> entry: unknownRefs.entrySet()) {138String target = entry.getKey();139for (String origin: entry.getValue()) {140// check if origin -> target allowed141String value = allowedBadRefs.getProperty(target);142if (value == null) {143System.err.format("%s -> %s (unknown type)%n", origin, target);144fail = true;145} else {146// target is known, check if the origin is one that we147// expect and that the exception applies to the profile.148boolean found = false;149boolean applicable = false;150for (String s: value.split(",")) {151s = s.trim();152if (s.equals(origin))153found = true;154if (s.equals(profile))155applicable = true;156}157if (!found || !applicable) {158if (!found) {159System.err.format("%s -> %s (not allowed)%n", origin, target);160} else {161System.err.format("%s -> %s (reference not applicable to %s)%n",162origin, target, profile);163}164fail = true;165}166}167168}169}170171return !fail;172}173174static void fail(URL url) throws Exception {175System.err.println("One or more unexpected references encountered");176if (url != null)177System.err.format("Check %s is up to date%n", Paths.get(url.toURI()));178System.exit(-1);179}180181public static void main(String[] args) throws Exception {182// load properties file so that we know what missing types that are183// allowed to be referenced.184URL url = CheckDeps.class.getResource("refs.allowed");185if (url != null) {186try (InputStream in = url.openStream()) {187allowedBadRefs.load(new InputStreamReader(in, StandardCharsets.UTF_8));188}189}190191if (args.length != 2) {192System.err.println("Usage: java CheckDeps <image> <profile>");193System.exit(-1);194}195196String image = args[0];197String profile = args[1];198199// process JAR files on boot class path200Path lib = Paths.get(image, "lib");201try (DirectoryStream<Path> stream = Files.newDirectoryStream(lib, "*.jar")) {202for (Path jarpath: stream) {203analyzeDependencies(jarpath);204}205}206207// classes on boot class path should not reference other types208boolean okay = checkClosure(profile);209if (!okay)210fail(url);211212// process JAR files in the extensions directory213try (DirectoryStream<Path> stream = Files.newDirectoryStream(lib.resolve("ext"), "*.jar")) {214for (Path jarpath: stream) {215analyzeDependencies(jarpath);216}217}218219// re-check to ensure that the extensions doesn't reference types that220// do not exist.221okay = checkClosure(profile);222if (!okay)223fail(url);224}225}226227228