Path: blob/master/src/jdk.jdeps/share/classes/com/sun/tools/classfile/Dependencies.java
41161 views
/*1* Copyright (c) 2009, 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 com.sun.tools.classfile;2627import java.util.Deque;28import java.util.HashMap;29import java.util.HashSet;30import java.util.LinkedList;31import java.util.List;32import java.util.Map;33import java.util.Objects;34import java.util.Set;35import java.util.concurrent.ConcurrentHashMap;36import java.util.regex.Pattern;3738import com.sun.tools.classfile.Dependency.Filter;39import com.sun.tools.classfile.Dependency.Finder;40import com.sun.tools.classfile.Dependency.Location;41import com.sun.tools.classfile.Type.ArrayType;42import com.sun.tools.classfile.Type.ClassSigType;43import com.sun.tools.classfile.Type.ClassType;44import com.sun.tools.classfile.Type.MethodType;45import com.sun.tools.classfile.Type.SimpleType;46import com.sun.tools.classfile.Type.TypeParamType;47import com.sun.tools.classfile.Type.WildcardType;4849import static com.sun.tools.classfile.ConstantPool.*;5051/**52* A framework for determining {@link Dependency dependencies} between class files.53*54* A {@link Dependency.Finder finder} is used to identify the dependencies of55* individual classes. Some finders may return subtypes of {@code Dependency} to56* further characterize the type of dependency, such as a dependency on a57* method within a class.58*59* A {@link Dependency.Filter filter} may be used to restrict the set of60* dependencies found by a finder.61*62* Dependencies that are found may be passed to a {@link Dependencies.Recorder63* recorder} so that the dependencies can be stored in a custom data structure.64*/65public class Dependencies {66/**67* Thrown when a class file cannot be found.68*/69public static class ClassFileNotFoundException extends Exception {70private static final long serialVersionUID = 3632265927794475048L;7172public ClassFileNotFoundException(String className) {73super(className);74this.className = className;75}7677public ClassFileNotFoundException(String className, Throwable cause) {78this(className);79initCause(cause);80}8182public final String className;83}8485/**86* Thrown when an exception is found processing a class file.87*/88public static class ClassFileError extends Error {89private static final long serialVersionUID = 4111110813961313203L;9091public ClassFileError(Throwable cause) {92initCause(cause);93}94}9596/**97* Service provider interface to locate and read class files.98*/99public interface ClassFileReader {100/**101* Get the ClassFile object for a specified class.102* @param className the name of the class to be returned.103* @return the ClassFile for the given class104* @throws Dependencies.ClassFileNotFoundException if the classfile cannot be105* found106*/107public ClassFile getClassFile(String className)108throws ClassFileNotFoundException;109}110111/**112* Service provide interface to handle results.113*/114public interface Recorder {115/**116* Record a dependency that has been found.117* @param d118*/119public void addDependency(Dependency d);120}121122/**123* Get the default finder used to locate the dependencies for a class.124* @return the default finder125*/126public static Finder getDefaultFinder() {127return new APIDependencyFinder(AccessFlags.ACC_PRIVATE);128}129130/**131* Get a finder used to locate the API dependencies for a class.132* These include the superclass, superinterfaces, and classes referenced in133* the declarations of fields and methods. The fields and methods that134* are checked can be limited according to a specified access.135* The access parameter must be one of {@link AccessFlags#ACC_PUBLIC ACC_PUBLIC},136* {@link AccessFlags#ACC_PRIVATE ACC_PRIVATE},137* {@link AccessFlags#ACC_PROTECTED ACC_PROTECTED}, or 0 for138* package private access. Members with greater than or equal accessibility139* to that specified will be searched for dependencies.140* @param access the access of members to be checked141* @return an API finder142*/143public static Finder getAPIFinder(int access) {144return new APIDependencyFinder(access);145}146147/**148* Get a finder to do class dependency analysis.149*150* @return a Class dependency finder151*/152public static Finder getClassDependencyFinder() {153return new ClassDependencyFinder();154}155156/**157* Get the finder used to locate the dependencies for a class.158* @return the finder159*/160public Finder getFinder() {161if (finder == null)162finder = getDefaultFinder();163return finder;164}165166/**167* Set the finder used to locate the dependencies for a class.168* @param f the finder169*/170public void setFinder(Finder f) {171finder = Objects.requireNonNull(f);172}173174/**175* Get the default filter used to determine included when searching176* the transitive closure of all the dependencies.177* Unless overridden, the default filter accepts all dependencies.178* @return the default filter.179*/180public static Filter getDefaultFilter() {181return DefaultFilter.instance();182}183184/**185* Get a filter which uses a regular expression on the target's class name186* to determine if a dependency is of interest.187* @param pattern the pattern used to match the target's class name188* @return a filter for matching the target class name with a regular expression189*/190public static Filter getRegexFilter(Pattern pattern) {191return new TargetRegexFilter(pattern);192}193194/**195* Get a filter which checks the package of a target's class name196* to determine if a dependency is of interest. The filter checks if the197* package of the target's class matches any of a set of given package198* names. The match may optionally match subpackages of the given names as well.199* @param packageNames the package names used to match the target's class name200* @param matchSubpackages whether or not to match subpackages as well201* @return a filter for checking the target package name against a list of package names202*/203public static Filter getPackageFilter(Set<String> packageNames, boolean matchSubpackages) {204return new TargetPackageFilter(packageNames, matchSubpackages);205}206207/**208* Get the filter used to determine the dependencies included when searching209* the transitive closure of all the dependencies.210* Unless overridden, the default filter accepts all dependencies.211* @return the filter212*/213public Filter getFilter() {214if (filter == null)215filter = getDefaultFilter();216return filter;217}218219/**220* Set the filter used to determine the dependencies included when searching221* the transitive closure of all the dependencies.222* @param f the filter223*/224public void setFilter(Filter f) {225filter = Objects.requireNonNull(f);226}227228/**229* Find the dependencies of a class, using the current230* {@link Dependencies#getFinder finder} and231* {@link Dependencies#getFilter filter}.232* The search may optionally include the transitive closure of all the233* filtered dependencies, by also searching in the classes named in those234* dependencies.235* @param classFinder a finder to locate class files236* @param rootClassNames the names of the root classes from which to begin237* searching238* @param transitiveClosure whether or not to also search those classes239* named in any filtered dependencies that are found.240* @return the set of dependencies that were found241* @throws ClassFileNotFoundException if a required class file cannot be found242* @throws ClassFileError if an error occurs while processing a class file,243* such as an error in the internal class file structure.244*/245public Set<Dependency> findAllDependencies(246ClassFileReader classFinder, Set<String> rootClassNames,247boolean transitiveClosure)248throws ClassFileNotFoundException {249final Set<Dependency> results = new HashSet<>();250Recorder r = results::add;251findAllDependencies(classFinder, rootClassNames, transitiveClosure, r);252return results;253}254255/**256* Find the dependencies of a class, using the current257* {@link Dependencies#getFinder finder} and258* {@link Dependencies#getFilter filter}.259* The search may optionally include the transitive closure of all the260* filtered dependencies, by also searching in the classes named in those261* dependencies.262* @param classFinder a finder to locate class files263* @param rootClassNames the names of the root classes from which to begin264* searching265* @param transitiveClosure whether or not to also search those classes266* named in any filtered dependencies that are found.267* @param recorder a recorder for handling the results268* @throws ClassFileNotFoundException if a required class file cannot be found269* @throws ClassFileError if an error occurs while processing a class file,270* such as an error in the internal class file structure.271*/272public void findAllDependencies(273ClassFileReader classFinder, Set<String> rootClassNames,274boolean transitiveClosure, Recorder recorder)275throws ClassFileNotFoundException {276Set<String> doneClasses = new HashSet<>();277278getFinder(); // ensure initialized279getFilter(); // ensure initialized280281// Work queue of names of classfiles to be searched.282// Entries will be unique, and for classes that do not yet have283// dependencies in the results map.284Deque<String> deque = new LinkedList<>(rootClassNames);285286String className;287while ((className = deque.poll()) != null) {288assert (!doneClasses.contains(className));289doneClasses.add(className);290291ClassFile cf = classFinder.getClassFile(className);292293// The following code just applies the filter to the dependencies294// followed for the transitive closure.295for (Dependency d: finder.findDependencies(cf)) {296recorder.addDependency(d);297if (transitiveClosure && filter.accepts(d)) {298String cn = d.getTarget().getClassName();299if (!doneClasses.contains(cn))300deque.add(cn);301}302}303}304}305306private Filter filter;307private Finder finder;308309/**310* A location identifying a class.311*/312static class SimpleLocation implements Location {313public SimpleLocation(String name) {314this.name = name;315this.className = name.replace('/', '.');316}317318public String getName() {319return name;320}321322public String getClassName() {323return className;324}325326public String getPackageName() {327int i = name.lastIndexOf('/');328return (i > 0) ? name.substring(0, i).replace('/', '.') : "";329}330331@Override332public boolean equals(Object other) {333if (this == other)334return true;335if (!(other instanceof SimpleLocation))336return false;337return (name.equals(((SimpleLocation) other).name));338}339340@Override341public int hashCode() {342return name.hashCode();343}344345@Override346public String toString() {347return name;348}349350private String name;351private String className;352}353354/**355* A dependency of one class on another.356*/357static class SimpleDependency implements Dependency {358public SimpleDependency(Location origin, Location target) {359this.origin = origin;360this.target = target;361}362363public Location getOrigin() {364return origin;365}366367public Location getTarget() {368return target;369}370371@Override372public boolean equals(Object other) {373if (this == other)374return true;375if (!(other instanceof SimpleDependency))376return false;377SimpleDependency o = (SimpleDependency) other;378return (origin.equals(o.origin) && target.equals(o.target));379}380381@Override382public int hashCode() {383return origin.hashCode() * 31 + target.hashCode();384}385386@Override387public String toString() {388return origin + ":" + target;389}390391private Location origin;392private Location target;393}394395396/**397* This class accepts all dependencies.398*/399static class DefaultFilter implements Filter {400private static DefaultFilter instance;401402static DefaultFilter instance() {403if (instance == null)404instance = new DefaultFilter();405return instance;406}407408public boolean accepts(Dependency dependency) {409return true;410}411}412413/**414* This class accepts those dependencies whose target's class name matches a415* regular expression.416*/417static class TargetRegexFilter implements Filter {418TargetRegexFilter(Pattern pattern) {419this.pattern = pattern;420}421422public boolean accepts(Dependency dependency) {423return pattern.matcher(dependency.getTarget().getClassName()).matches();424}425426private final Pattern pattern;427}428429/**430* This class accepts those dependencies whose class name is in a given431* package.432*/433static class TargetPackageFilter implements Filter {434TargetPackageFilter(Set<String> packageNames, boolean matchSubpackages) {435for (String pn: packageNames) {436if (pn.length() == 0) // implies null check as well437throw new IllegalArgumentException();438}439this.packageNames = packageNames;440this.matchSubpackages = matchSubpackages;441}442443public boolean accepts(Dependency dependency) {444String pn = dependency.getTarget().getPackageName();445if (packageNames.contains(pn))446return true;447448if (matchSubpackages) {449for (String n: packageNames) {450if (pn.startsWith(n + "."))451return true;452}453}454455return false;456}457458private final Set<String> packageNames;459private final boolean matchSubpackages;460}461462/**463* This class identifies class names directly or indirectly in the constant pool.464*/465static class ClassDependencyFinder extends BasicDependencyFinder {466public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {467Visitor v = new Visitor(classfile);468for (CPInfo cpInfo: classfile.constant_pool.entries()) {469v.scan(cpInfo);470}471try {472v.addClass(classfile.super_class);473v.addClasses(classfile.interfaces);474v.scan(classfile.attributes);475476for (Field f : classfile.fields) {477v.scan(f.descriptor, f.attributes);478}479for (Method m : classfile.methods) {480v.scan(m.descriptor, m.attributes);481Exceptions_attribute e =482(Exceptions_attribute)m.attributes.get(Attribute.Exceptions);483if (e != null) {484v.addClasses(e.exception_index_table);485}486}487} catch (ConstantPoolException e) {488throw new ClassFileError(e);489}490491return v.deps;492}493}494495/**496* This class identifies class names in the signatures of classes, fields,497* and methods in a class.498*/499static class APIDependencyFinder extends BasicDependencyFinder {500APIDependencyFinder(int access) {501switch (access) {502case AccessFlags.ACC_PUBLIC:503case AccessFlags.ACC_PROTECTED:504case AccessFlags.ACC_PRIVATE:505case 0:506showAccess = access;507break;508default:509throw new IllegalArgumentException("invalid access 0x"510+ Integer.toHexString(access));511}512}513514public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {515try {516Visitor v = new Visitor(classfile);517v.addClass(classfile.super_class);518v.addClasses(classfile.interfaces);519// inner classes?520for (Field f : classfile.fields) {521if (checkAccess(f.access_flags))522v.scan(f.descriptor, f.attributes);523}524for (Method m : classfile.methods) {525if (checkAccess(m.access_flags)) {526v.scan(m.descriptor, m.attributes);527Exceptions_attribute e =528(Exceptions_attribute) m.attributes.get(Attribute.Exceptions);529if (e != null)530v.addClasses(e.exception_index_table);531}532}533return v.deps;534} catch (ConstantPoolException e) {535throw new ClassFileError(e);536}537}538539boolean checkAccess(AccessFlags flags) {540// code copied from javap.Options.checkAccess541boolean isPublic = flags.is(AccessFlags.ACC_PUBLIC);542boolean isProtected = flags.is(AccessFlags.ACC_PROTECTED);543boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE);544boolean isPackage = !(isPublic || isProtected || isPrivate);545546if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage))547return false;548else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage))549return false;550else if ((showAccess == 0) && (isPrivate))551return false;552else553return true;554}555556private int showAccess;557}558559static abstract class BasicDependencyFinder implements Finder {560private Map<String,Location> locations = new ConcurrentHashMap<>();561562Location getLocation(String className) {563return locations.computeIfAbsent(className, SimpleLocation::new);564}565566class Visitor implements ConstantPool.Visitor<Void,Void>, Type.Visitor<Void, Void> {567private ConstantPool constant_pool;568private Location origin;569Set<Dependency> deps;570571Visitor(ClassFile classFile) {572try {573constant_pool = classFile.constant_pool;574origin = getLocation(classFile.getName());575deps = new HashSet<>();576} catch (ConstantPoolException e) {577throw new ClassFileError(e);578}579}580581void scan(Descriptor d, Attributes attrs) {582try {583scan(new Signature(d.index).getType(constant_pool));584scan(attrs);585} catch (ConstantPoolException e) {586throw new ClassFileError(e);587}588}589590void scan(CPInfo cpInfo) {591cpInfo.accept(this, null);592}593594void scan(Type t) {595t.accept(this, null);596}597598void scan(Attributes attrs) {599try {600Signature_attribute sa = (Signature_attribute)attrs.get(Attribute.Signature);601if (sa != null)602scan(sa.getParsedSignature().getType(constant_pool));603604scan((RuntimeVisibleAnnotations_attribute)605attrs.get(Attribute.RuntimeVisibleAnnotations));606scan((RuntimeVisibleParameterAnnotations_attribute)607attrs.get(Attribute.RuntimeVisibleParameterAnnotations));608} catch (ConstantPoolException e) {609throw new ClassFileError(e);610}611}612613private void scan(RuntimeAnnotations_attribute attr) throws ConstantPoolException {614if (attr == null) {615return;616}617for (int i = 0; i < attr.annotations.length; i++) {618int index = attr.annotations[i].type_index;619scan(new Signature(index).getType(constant_pool));620}621}622623private void scan(RuntimeParameterAnnotations_attribute attr) throws ConstantPoolException {624if (attr == null) {625return;626}627for (int param = 0; param < attr.parameter_annotations.length; param++) {628for (int i = 0; i < attr.parameter_annotations[param].length; i++) {629int index = attr.parameter_annotations[param][i].type_index;630scan(new Signature(index).getType(constant_pool));631}632}633}634635void addClass(int index) throws ConstantPoolException {636if (index != 0) {637String name = constant_pool.getClassInfo(index).getBaseName();638if (name != null)639addDependency(name);640}641}642643void addClasses(int[] indices) throws ConstantPoolException {644for (int i: indices)645addClass(i);646}647648private void addDependency(String name) {649deps.add(new SimpleDependency(origin, getLocation(name)));650}651652// ConstantPool.Visitor methods653654public Void visitClass(CONSTANT_Class_info info, Void p) {655try {656if (info.getName().startsWith("["))657new Signature(info.name_index).getType(constant_pool).accept(this, null);658else659addDependency(info.getBaseName());660return null;661} catch (ConstantPoolException e) {662throw new ClassFileError(e);663}664}665666public Void visitDouble(CONSTANT_Double_info info, Void p) {667return null;668}669670public Void visitFieldref(CONSTANT_Fieldref_info info, Void p) {671return visitRef(info, p);672}673674public Void visitFloat(CONSTANT_Float_info info, Void p) {675return null;676}677678public Void visitInteger(CONSTANT_Integer_info info, Void p) {679return null;680}681682public Void visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) {683return visitRef(info, p);684}685686public Void visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, Void p) {687return null;688}689690@Override691public Void visitDynamicConstant(CONSTANT_Dynamic_info info, Void aVoid) {692return null;693}694695public Void visitLong(CONSTANT_Long_info info, Void p) {696return null;697}698699public Void visitMethodHandle(CONSTANT_MethodHandle_info info, Void p) {700return null;701}702703public Void visitMethodType(CONSTANT_MethodType_info info, Void p) {704return null;705}706707public Void visitMethodref(CONSTANT_Methodref_info info, Void p) {708return visitRef(info, p);709}710711public Void visitModule(CONSTANT_Module_info info, Void p) {712return null;713}714715public Void visitNameAndType(CONSTANT_NameAndType_info info, Void p) {716try {717new Signature(info.type_index).getType(constant_pool).accept(this, null);718return null;719} catch (ConstantPoolException e) {720throw new ClassFileError(e);721}722}723724public Void visitPackage(CONSTANT_Package_info info, Void p) {725return null;726}727728public Void visitString(CONSTANT_String_info info, Void p) {729return null;730}731732public Void visitUtf8(CONSTANT_Utf8_info info, Void p) {733return null;734}735736private Void visitRef(CPRefInfo info, Void p) {737try {738visitClass(info.getClassInfo(), p);739return null;740} catch (ConstantPoolException e) {741throw new ClassFileError(e);742}743}744745// Type.Visitor methods746747private void findDependencies(Type t) {748if (t != null)749t.accept(this, null);750}751752private void findDependencies(List<? extends Type> ts) {753if (ts != null) {754for (Type t: ts)755t.accept(this, null);756}757}758759public Void visitSimpleType(SimpleType type, Void p) {760return null;761}762763public Void visitArrayType(ArrayType type, Void p) {764findDependencies(type.elemType);765return null;766}767768public Void visitMethodType(MethodType type, Void p) {769findDependencies(type.paramTypes);770findDependencies(type.returnType);771findDependencies(type.throwsTypes);772findDependencies(type.typeParamTypes);773return null;774}775776public Void visitClassSigType(ClassSigType type, Void p) {777findDependencies(type.superclassType);778findDependencies(type.superinterfaceTypes);779return null;780}781782public Void visitClassType(ClassType type, Void p) {783findDependencies(type.outerType);784addDependency(type.getBinaryName());785findDependencies(type.typeArgs);786return null;787}788789public Void visitTypeParamType(TypeParamType type, Void p) {790findDependencies(type.classBound);791findDependencies(type.interfaceBounds);792return null;793}794795public Void visitWildcardType(WildcardType type, Void p) {796findDependencies(type.boundType);797return null;798}799}800}801}802803804