jjg@451: /* jjg@1357: * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. jjg@451: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@451: * jjg@451: * This code is free software; you can redistribute it and/or modify it jjg@451: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this jjg@451: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. jjg@451: * jjg@451: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@451: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@451: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@451: * version 2 for more details (a copy is included in the LICENSE file that jjg@451: * accompanied this code). jjg@451: * jjg@451: * You should have received a copy of the GNU General Public License version jjg@451: * 2 along with this work; if not, write to the Free Software Foundation, jjg@451: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@451: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. jjg@451: */ jjg@451: package com.sun.tools.classfile; jjg@451: jjg@451: import java.util.Deque; jjg@451: import java.util.HashMap; jjg@451: import java.util.HashSet; jjg@451: import java.util.LinkedList; jjg@451: import java.util.List; jjg@451: import java.util.Map; jjg@451: import java.util.Set; jjg@451: import java.util.regex.Pattern; jjg@451: jjg@1357: import com.sun.tools.classfile.Dependency.Filter; jjg@451: import com.sun.tools.classfile.Dependency.Finder; jjg@451: import com.sun.tools.classfile.Dependency.Location; jjg@451: import com.sun.tools.classfile.Type.ArrayType; jjg@451: import com.sun.tools.classfile.Type.ClassSigType; jjg@451: import com.sun.tools.classfile.Type.ClassType; jjg@451: import com.sun.tools.classfile.Type.MethodType; jjg@451: import com.sun.tools.classfile.Type.SimpleType; jjg@451: import com.sun.tools.classfile.Type.TypeParamType; jjg@451: import com.sun.tools.classfile.Type.WildcardType; jjg@451: import static com.sun.tools.classfile.ConstantPool.*; jjg@451: jjg@451: /** jjg@451: * A framework for determining {@link Dependency dependencies} between class files. jjg@451: * jjg@451: * A {@link Dependency.Finder finder} is used to identify the dependencies of jjg@451: * individual classes. Some finders may return subtypes of {@code Dependency} to jjg@451: * further characterize the type of dependency, such as a dependency on a jjg@451: * method within a class. jjg@451: * jjg@451: * A {@link Dependency.Filter filter} may be used to restrict the set of jjg@451: * dependencies found by a finder. jjg@451: * jjg@451: * Dependencies that are found may be passed to a {@link Dependencies.Recorder jjg@451: * recorder} so that the dependencies can be stored in a custom data structure. jjg@451: */ jjg@451: public class Dependencies { jjg@451: /** jjg@451: * Thrown when a class file cannot be found. jjg@451: */ jjg@451: public static class ClassFileNotFoundException extends Exception { jjg@451: private static final long serialVersionUID = 3632265927794475048L; jjg@451: jjg@451: public ClassFileNotFoundException(String className) { jjg@451: super(className); jjg@451: this.className = className; jjg@451: } jjg@451: jjg@451: public ClassFileNotFoundException(String className, Throwable cause) { jjg@451: this(className); jjg@451: initCause(cause); jjg@451: } jjg@451: jjg@451: public final String className; jjg@451: } jjg@451: jjg@451: /** jjg@451: * Thrown when an exception is found processing a class file. jjg@451: */ jjg@451: public static class ClassFileError extends Error { jjg@451: private static final long serialVersionUID = 4111110813961313203L; jjg@451: jjg@451: public ClassFileError(Throwable cause) { jjg@451: initCause(cause); jjg@451: } jjg@451: } jjg@451: jjg@451: /** jjg@451: * Service provider interface to locate and read class files. jjg@451: */ jjg@451: public interface ClassFileReader { jjg@451: /** jjg@451: * Get the ClassFile object for a specified class. jjg@451: * @param className the name of the class to be returned. jjg@451: * @return the ClassFile for the given class jjg@1358: * @throws Dependencies.ClassFileNotFoundException if the classfile cannot be jjg@451: * found jjg@451: */ jjg@451: public ClassFile getClassFile(String className) jjg@451: throws ClassFileNotFoundException; jjg@451: } jjg@451: jjg@451: /** jjg@451: * Service provide interface to handle results. jjg@451: */ jjg@451: public interface Recorder { jjg@451: /** jjg@451: * Record a dependency that has been found. jjg@451: * @param d jjg@451: */ jjg@451: public void addDependency(Dependency d); jjg@451: } jjg@451: jjg@451: /** jjg@451: * Get the default finder used to locate the dependencies for a class. jjg@451: * @return the default finder jjg@451: */ jjg@451: public static Finder getDefaultFinder() { jjg@451: return new APIDependencyFinder(AccessFlags.ACC_PRIVATE); jjg@451: } jjg@451: jjg@451: /** jjg@451: * Get a finder used to locate the API dependencies for a class. jjg@451: * These include the superclass, superinterfaces, and classes referenced in jjg@451: * the declarations of fields and methods. The fields and methods that jjg@451: * are checked can be limited according to a specified access. jjg@451: * The access parameter must be one of {@link AccessFlags#ACC_PUBLIC ACC_PUBLIC}, jjg@451: * {@link AccessFlags#ACC_PRIVATE ACC_PRIVATE}, jjg@451: * {@link AccessFlags#ACC_PROTECTED ACC_PROTECTED}, or 0 for jjg@451: * package private access. Members with greater than or equal accessibility jjg@451: * to that specified will be searched for dependencies. jjg@451: * @param access the access of members to be checked jjg@451: * @return an API finder jjg@451: */ jjg@451: public static Finder getAPIFinder(int access) { jjg@451: return new APIDependencyFinder(access); jjg@451: } jjg@451: jjg@451: /** mchung@1472: * Get a finder to do class dependency analysis. mchung@1472: * mchung@1472: * @return a Class dependency finder mchung@1472: */ mchung@1472: public static Finder getClassDependencyFinder() { mchung@1472: return new ClassDependencyFinder(); mchung@1472: } mchung@1472: mchung@1472: /** jjg@451: * Get the finder used to locate the dependencies for a class. jjg@451: * @return the finder jjg@451: */ jjg@451: public Finder getFinder() { jjg@451: if (finder == null) jjg@451: finder = getDefaultFinder(); jjg@451: return finder; jjg@451: } jjg@451: jjg@451: /** jjg@451: * Set the finder used to locate the dependencies for a class. jjg@451: * @param f the finder jjg@451: */ jjg@451: public void setFinder(Finder f) { jjg@451: f.getClass(); // null check jjg@451: finder = f; jjg@451: } jjg@451: jjg@451: /** jjg@451: * Get the default filter used to determine included when searching jjg@451: * the transitive closure of all the dependencies. jjg@451: * Unless overridden, the default filter accepts all dependencies. jjg@451: * @return the default filter. jjg@451: */ jjg@451: public static Filter getDefaultFilter() { jjg@451: return DefaultFilter.instance(); jjg@451: } jjg@451: jjg@451: /** jjg@451: * Get a filter which uses a regular expression on the target's class name jjg@451: * to determine if a dependency is of interest. jjg@451: * @param pattern the pattern used to match the target's class name jjg@451: * @return a filter for matching the target class name with a regular expression jjg@451: */ jjg@451: public static Filter getRegexFilter(Pattern pattern) { jjg@451: return new TargetRegexFilter(pattern); jjg@451: } jjg@451: jjg@451: /** jjg@451: * Get a filter which checks the package of a target's class name jjg@451: * to determine if a dependency is of interest. The filter checks if the jjg@451: * package of the target's class matches any of a set of given package jjg@451: * names. The match may optionally match subpackages of the given names as well. jjg@451: * @param packageNames the package names used to match the target's class name jjg@451: * @param matchSubpackages whether or not to match subpackages as well jjg@451: * @return a filter for checking the target package name against a list of package names jjg@451: */ jjg@451: public static Filter getPackageFilter(Set packageNames, boolean matchSubpackages) { jjg@451: return new TargetPackageFilter(packageNames, matchSubpackages); jjg@451: } jjg@451: jjg@451: /** jjg@451: * Get the filter used to determine the dependencies included when searching jjg@451: * the transitive closure of all the dependencies. jjg@451: * Unless overridden, the default filter accepts all dependencies. jjg@451: * @return the filter jjg@451: */ jjg@451: public Filter getFilter() { jjg@451: if (filter == null) jjg@451: filter = getDefaultFilter(); jjg@451: return filter; jjg@451: } jjg@451: jjg@451: /** jjg@451: * Set the filter used to determine the dependencies included when searching jjg@451: * the transitive closure of all the dependencies. jjg@451: * @param f the filter jjg@451: */ jjg@451: public void setFilter(Filter f) { jjg@451: f.getClass(); // null check jjg@451: filter = f; jjg@451: } jjg@451: jjg@451: /** jjg@451: * Find the dependencies of a class, using the current jjg@451: * {@link Dependencies#getFinder finder} and jjg@451: * {@link Dependencies#getFilter filter}. jjg@451: * The search may optionally include the transitive closure of all the jjg@451: * filtered dependencies, by also searching in the classes named in those jjg@451: * dependencies. jjg@451: * @param classFinder a finder to locate class files jjg@451: * @param rootClassNames the names of the root classes from which to begin jjg@451: * searching jjg@451: * @param transitiveClosure whether or not to also search those classes jjg@451: * named in any filtered dependencies that are found. jjg@451: * @return the set of dependencies that were found jjg@451: * @throws ClassFileNotFoundException if a required class file cannot be found jjg@451: * @throws ClassFileError if an error occurs while processing a class file, jjg@451: * such as an error in the internal class file structure. jjg@451: */ jjg@451: public Set findAllDependencies( jjg@451: ClassFileReader classFinder, Set rootClassNames, jjg@451: boolean transitiveClosure) jjg@451: throws ClassFileNotFoundException { jjg@451: final Set results = new HashSet(); jjg@451: Recorder r = new Recorder() { jjg@451: public void addDependency(Dependency d) { jjg@451: results.add(d); jjg@451: } jjg@451: }; jjg@451: findAllDependencies(classFinder, rootClassNames, transitiveClosure, r); jjg@451: return results; jjg@451: } jjg@451: jjg@451: /** jjg@451: * Find the dependencies of a class, using the current jjg@451: * {@link Dependencies#getFinder finder} and jjg@451: * {@link Dependencies#getFilter filter}. jjg@451: * The search may optionally include the transitive closure of all the jjg@451: * filtered dependencies, by also searching in the classes named in those jjg@451: * dependencies. jjg@451: * @param classFinder a finder to locate class files jjg@451: * @param rootClassNames the names of the root classes from which to begin jjg@451: * searching jjg@451: * @param transitiveClosure whether or not to also search those classes jjg@451: * named in any filtered dependencies that are found. jjg@451: * @param recorder a recorder for handling the results jjg@451: * @throws ClassFileNotFoundException if a required class file cannot be found jjg@451: * @throws ClassFileError if an error occurs while processing a class file, jjg@451: * such as an error in the internal class file structure. jjg@451: */ jjg@451: public void findAllDependencies( jjg@451: ClassFileReader classFinder, Set rootClassNames, jjg@451: boolean transitiveClosure, Recorder recorder) jjg@451: throws ClassFileNotFoundException { jjg@451: Set doneClasses = new HashSet(); jjg@451: jjg@451: getFinder(); // ensure initialized jjg@451: getFilter(); // ensure initialized jjg@451: jjg@451: // Work queue of names of classfiles to be searched. jjg@451: // Entries will be unique, and for classes that do not yet have jjg@451: // dependencies in the results map. jjg@451: Deque deque = new LinkedList(rootClassNames); jjg@451: jjg@451: String className; jjg@451: while ((className = deque.poll()) != null) { jjg@451: assert (!doneClasses.contains(className)); jjg@451: doneClasses.add(className); jjg@451: jjg@451: ClassFile cf = classFinder.getClassFile(className); jjg@451: jjg@451: // The following code just applies the filter to the dependencies jjg@451: // followed for the transitive closure. jjg@451: for (Dependency d: finder.findDependencies(cf)) { jjg@451: recorder.addDependency(d); jjg@451: if (transitiveClosure && filter.accepts(d)) { jjg@451: String cn = d.getTarget().getClassName(); jjg@451: if (!doneClasses.contains(cn)) jjg@451: deque.add(cn); jjg@451: } jjg@451: } jjg@451: } jjg@451: } jjg@451: jjg@451: private Filter filter; jjg@451: private Finder finder; jjg@451: jjg@451: /** jjg@451: * A location identifying a class. jjg@451: */ jjg@451: static class SimpleLocation implements Location { mchung@1472: public SimpleLocation(String name) { mchung@1472: this.name = name; mchung@1472: this.className = name.replace('/', '.').replace('$', '.'); jjg@451: } jjg@451: mchung@1472: public String getName() { mchung@1472: return name; mchung@1472: } mchung@1472: jjg@451: public String getClassName() { jjg@451: return className; jjg@451: } jjg@451: mchung@1472: public String getPackageName() { mchung@1472: int i = name.lastIndexOf('/'); mchung@1472: return (i > 0) ? name.substring(0, i).replace('/', '.') : ""; mchung@1472: } mchung@1472: jjg@451: @Override jjg@451: public boolean equals(Object other) { jjg@451: if (this == other) jjg@451: return true; jjg@451: if (!(other instanceof SimpleLocation)) jjg@451: return false; mchung@1472: return (name.equals(((SimpleLocation) other).name)); jjg@451: } jjg@451: jjg@451: @Override jjg@451: public int hashCode() { mchung@1472: return name.hashCode(); jjg@451: } jjg@451: jjg@451: @Override jjg@451: public String toString() { mchung@1472: return name; jjg@451: } jjg@451: mchung@1472: private String name; jjg@451: private String className; jjg@451: } jjg@451: jjg@451: /** jjg@451: * A dependency of one class on another. jjg@451: */ jjg@451: static class SimpleDependency implements Dependency { jjg@451: public SimpleDependency(Location origin, Location target) { jjg@451: this.origin = origin; jjg@451: this.target = target; jjg@451: } jjg@451: jjg@451: public Location getOrigin() { jjg@451: return origin; jjg@451: } jjg@451: jjg@451: public Location getTarget() { jjg@451: return target; jjg@451: } jjg@451: jjg@451: @Override jjg@451: public boolean equals(Object other) { jjg@451: if (this == other) jjg@451: return true; jjg@451: if (!(other instanceof SimpleDependency)) jjg@451: return false; jjg@451: SimpleDependency o = (SimpleDependency) other; jjg@451: return (origin.equals(o.origin) && target.equals(o.target)); jjg@451: } jjg@451: jjg@451: @Override jjg@451: public int hashCode() { jjg@451: return origin.hashCode() * 31 + target.hashCode(); jjg@451: } jjg@451: jjg@451: @Override jjg@451: public String toString() { jjg@451: return origin + ":" + target; jjg@451: } jjg@451: jjg@451: private Location origin; jjg@451: private Location target; jjg@451: } jjg@451: jjg@451: jjg@451: /** jjg@451: * This class accepts all dependencies. jjg@451: */ jjg@451: static class DefaultFilter implements Filter { jjg@451: private static DefaultFilter instance; jjg@451: jjg@451: static DefaultFilter instance() { jjg@451: if (instance == null) jjg@451: instance = new DefaultFilter(); jjg@451: return instance; jjg@451: } jjg@451: jjg@451: public boolean accepts(Dependency dependency) { jjg@451: return true; jjg@451: } jjg@451: } jjg@451: jjg@451: /** jjg@451: * This class accepts those dependencies whose target's class name matches a jjg@451: * regular expression. jjg@451: */ jjg@451: static class TargetRegexFilter implements Filter { jjg@451: TargetRegexFilter(Pattern pattern) { jjg@451: this.pattern = pattern; jjg@451: } jjg@451: jjg@451: public boolean accepts(Dependency dependency) { jjg@451: return pattern.matcher(dependency.getTarget().getClassName()).matches(); jjg@451: } jjg@451: jjg@452: private final Pattern pattern; jjg@451: } jjg@451: jjg@451: /** jjg@451: * This class accepts those dependencies whose class name is in a given jjg@451: * package. jjg@451: */ jjg@451: static class TargetPackageFilter implements Filter { jjg@451: TargetPackageFilter(Set packageNames, boolean matchSubpackages) { jjg@451: for (String pn: packageNames) { jjg@451: if (pn.length() == 0) // implies null check as well jjg@451: throw new IllegalArgumentException(); jjg@451: } jjg@451: this.packageNames = packageNames; jjg@451: this.matchSubpackages = matchSubpackages; jjg@451: } jjg@451: jjg@451: public boolean accepts(Dependency dependency) { mchung@1472: String pn = dependency.getTarget().getPackageName(); jjg@451: if (packageNames.contains(pn)) jjg@451: return true; jjg@451: jjg@451: if (matchSubpackages) { jjg@451: for (String n: packageNames) { jjg@451: if (pn.startsWith(n + ".")) jjg@451: return true; jjg@451: } jjg@451: } jjg@451: jjg@451: return false; jjg@451: } jjg@451: jjg@452: private final Set packageNames; jjg@452: private final boolean matchSubpackages; jjg@451: } jjg@451: jjg@451: /** jjg@451: * This class identifies class names directly or indirectly in the constant pool. jjg@451: */ jjg@451: static class ClassDependencyFinder extends BasicDependencyFinder { jjg@451: public Iterable findDependencies(ClassFile classfile) { jjg@451: Visitor v = new Visitor(classfile); jjg@451: for (CPInfo cpInfo: classfile.constant_pool.entries()) { jjg@451: v.scan(cpInfo); jjg@451: } mchung@1472: try { mchung@1472: v.addClass(classfile.super_class); mchung@1472: v.addClasses(classfile.interfaces); mchung@1472: v.scan(classfile.attributes); mchung@1472: mchung@1472: for (Field f : classfile.fields) { mchung@1472: v.scan(f.descriptor, f.attributes); mchung@1472: } mchung@1472: for (Method m : classfile.methods) { mchung@1472: v.scan(m.descriptor, m.attributes); mchung@1472: Exceptions_attribute e = mchung@1472: (Exceptions_attribute)m.attributes.get(Attribute.Exceptions); mchung@1472: if (e != null) { mchung@1472: v.addClasses(e.exception_index_table); mchung@1472: } mchung@1472: } mchung@1472: } catch (ConstantPoolException e) { mchung@1472: throw new ClassFileError(e); mchung@1472: } mchung@1472: jjg@451: return v.deps; jjg@451: } jjg@451: } jjg@451: jjg@451: /** jjg@451: * This class identifies class names in the signatures of classes, fields, jjg@451: * and methods in a class. jjg@451: */ jjg@451: static class APIDependencyFinder extends BasicDependencyFinder { jjg@451: APIDependencyFinder(int access) { jjg@451: switch (access) { jjg@451: case AccessFlags.ACC_PUBLIC: jjg@451: case AccessFlags.ACC_PROTECTED: jjg@451: case AccessFlags.ACC_PRIVATE: jjg@451: case 0: jjg@451: showAccess = access; jjg@451: break; jjg@451: default: jjg@451: throw new IllegalArgumentException("invalid access 0x" jjg@451: + Integer.toHexString(access)); jjg@451: } jjg@451: } jjg@451: jjg@451: public Iterable findDependencies(ClassFile classfile) { jjg@451: try { jjg@451: Visitor v = new Visitor(classfile); jjg@451: v.addClass(classfile.super_class); jjg@451: v.addClasses(classfile.interfaces); jjg@451: // inner classes? jjg@451: for (Field f : classfile.fields) { jjg@451: if (checkAccess(f.access_flags)) jjg@451: v.scan(f.descriptor, f.attributes); jjg@451: } jjg@451: for (Method m : classfile.methods) { jjg@451: if (checkAccess(m.access_flags)) { jjg@451: v.scan(m.descriptor, m.attributes); jjg@451: Exceptions_attribute e = jjg@451: (Exceptions_attribute) m.attributes.get(Attribute.Exceptions); jjg@451: if (e != null) jjg@451: v.addClasses(e.exception_index_table); jjg@451: } jjg@451: } jjg@451: return v.deps; jjg@451: } catch (ConstantPoolException e) { jjg@451: throw new ClassFileError(e); jjg@451: } jjg@451: } jjg@451: jjg@451: boolean checkAccess(AccessFlags flags) { jjg@451: // code copied from javap.Options.checkAccess jjg@451: boolean isPublic = flags.is(AccessFlags.ACC_PUBLIC); jjg@451: boolean isProtected = flags.is(AccessFlags.ACC_PROTECTED); jjg@451: boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE); jjg@451: boolean isPackage = !(isPublic || isProtected || isPrivate); jjg@451: jjg@451: if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage)) jjg@451: return false; jjg@451: else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage)) jjg@451: return false; jjg@451: else if ((showAccess == 0) && (isPrivate)) jjg@451: return false; jjg@451: else jjg@451: return true; jjg@451: } jjg@451: jjg@451: private int showAccess; jjg@451: } jjg@451: jjg@451: static abstract class BasicDependencyFinder implements Finder { jjg@451: private Map locations = new HashMap(); jjg@451: jjg@451: Location getLocation(String className) { jjg@451: Location l = locations.get(className); jjg@451: if (l == null) jjg@451: locations.put(className, l = new SimpleLocation(className)); jjg@451: return l; jjg@451: } jjg@451: jjg@451: class Visitor implements ConstantPool.Visitor, Type.Visitor { jjg@451: private ConstantPool constant_pool; jjg@451: private Location origin; jjg@452: Set deps; jjg@451: jjg@451: Visitor(ClassFile classFile) { jjg@451: try { jjg@451: constant_pool = classFile.constant_pool; jjg@451: origin = getLocation(classFile.getName()); jjg@451: deps = new HashSet(); jjg@451: } catch (ConstantPoolException e) { jjg@451: throw new ClassFileError(e); jjg@451: } jjg@451: } jjg@451: jjg@451: void scan(Descriptor d, Attributes attrs) { jjg@451: try { jjg@451: scan(new Signature(d.index).getType(constant_pool)); mchung@1472: scan(attrs); jjg@451: } catch (ConstantPoolException e) { jjg@451: throw new ClassFileError(e); jjg@451: } jjg@451: } jjg@451: jjg@451: void scan(CPInfo cpInfo) { jjg@451: cpInfo.accept(this, null); jjg@451: } jjg@451: jjg@451: void scan(Type t) { jjg@451: t.accept(this, null); jjg@451: } jjg@451: mchung@1472: void scan(Attributes attrs) { mchung@1472: try { mchung@1472: Signature_attribute sa = (Signature_attribute)attrs.get(Attribute.Signature); mchung@1472: if (sa != null) mchung@1472: scan(sa.getParsedSignature().getType(constant_pool)); mchung@1472: mchung@1472: scan((RuntimeVisibleAnnotations_attribute) mchung@1472: attrs.get(Attribute.RuntimeVisibleAnnotations)); mchung@1472: scan((RuntimeVisibleParameterAnnotations_attribute) mchung@1472: attrs.get(Attribute.RuntimeVisibleParameterAnnotations)); mchung@1472: } catch (ConstantPoolException e) { mchung@1472: throw new ClassFileError(e); mchung@1472: } mchung@1472: } mchung@1472: mchung@1472: private void scan(RuntimeAnnotations_attribute attr) throws ConstantPoolException { mchung@1472: if (attr == null) { mchung@1472: return; mchung@1472: } mchung@1472: for (int i = 0; i < attr.annotations.length; i++) { mchung@1472: int index = attr.annotations[i].type_index; mchung@1472: scan(new Signature(index).getType(constant_pool)); mchung@1472: } mchung@1472: } mchung@1472: mchung@1472: private void scan(RuntimeParameterAnnotations_attribute attr) throws ConstantPoolException { mchung@1472: if (attr == null) { mchung@1472: return; mchung@1472: } mchung@1472: for (int param = 0; param < attr.parameter_annotations.length; param++) { mchung@1472: for (int i = 0; i < attr.parameter_annotations[param].length; i++) { mchung@1472: int index = attr.parameter_annotations[param][i].type_index; mchung@1472: scan(new Signature(index).getType(constant_pool)); mchung@1472: } mchung@1472: } mchung@1472: } mchung@1472: jjg@451: void addClass(int index) throws ConstantPoolException { jjg@451: if (index != 0) { jjg@451: String name = constant_pool.getClassInfo(index).getBaseName(); jjg@451: if (name != null) jjg@451: addDependency(name); jjg@451: } jjg@451: } jjg@451: jjg@451: void addClasses(int[] indices) throws ConstantPoolException { jjg@451: for (int i: indices) jjg@451: addClass(i); jjg@451: } jjg@451: jjg@451: private void addDependency(String name) { jjg@451: deps.add(new SimpleDependency(origin, getLocation(name))); jjg@451: } jjg@451: jjg@451: // ConstantPool.Visitor methods jjg@451: jjg@451: public Void visitClass(CONSTANT_Class_info info, Void p) { jjg@451: try { jjg@451: if (info.getName().startsWith("[")) jjg@451: new Signature(info.name_index).getType(constant_pool).accept(this, null); jjg@451: else jjg@451: addDependency(info.getBaseName()); jjg@451: return null; jjg@451: } catch (ConstantPoolException e) { jjg@451: throw new ClassFileError(e); jjg@451: } jjg@451: } jjg@451: jjg@451: public Void visitDouble(CONSTANT_Double_info info, Void p) { jjg@451: return null; jjg@451: } jjg@451: jjg@451: public Void visitFieldref(CONSTANT_Fieldref_info info, Void p) { jjg@451: return visitRef(info, p); jjg@451: } jjg@451: jjg@451: public Void visitFloat(CONSTANT_Float_info info, Void p) { jjg@451: return null; jjg@451: } jjg@451: jjg@451: public Void visitInteger(CONSTANT_Integer_info info, Void p) { jjg@451: return null; jjg@451: } jjg@451: jjg@451: public Void visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) { jjg@451: return visitRef(info, p); jjg@451: } jjg@451: ksrini@826: public Void visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, Void p) { ksrini@826: return null; ksrini@826: } ksrini@826: jjg@451: public Void visitLong(CONSTANT_Long_info info, Void p) { jjg@451: return null; jjg@451: } jjg@451: ksrini@826: public Void visitMethodHandle(CONSTANT_MethodHandle_info info, Void p) { ksrini@826: return null; ksrini@826: } ksrini@826: ksrini@826: public Void visitMethodType(CONSTANT_MethodType_info info, Void p) { ksrini@826: return null; ksrini@826: } ksrini@826: ksrini@826: public Void visitMethodref(CONSTANT_Methodref_info info, Void p) { ksrini@826: return visitRef(info, p); ksrini@826: } ksrini@826: jjg@451: public Void visitNameAndType(CONSTANT_NameAndType_info info, Void p) { jjg@451: try { jjg@451: new Signature(info.type_index).getType(constant_pool).accept(this, null); jjg@451: return null; jjg@451: } catch (ConstantPoolException e) { jjg@451: throw new ClassFileError(e); jjg@451: } jjg@451: } jjg@451: jjg@451: public Void visitString(CONSTANT_String_info info, Void p) { jjg@451: return null; jjg@451: } jjg@451: jjg@451: public Void visitUtf8(CONSTANT_Utf8_info info, Void p) { jjg@451: return null; jjg@451: } jjg@451: jjg@451: private Void visitRef(CPRefInfo info, Void p) { jjg@451: try { jjg@451: visitClass(info.getClassInfo(), p); jjg@451: return null; jjg@451: } catch (ConstantPoolException e) { jjg@451: throw new ClassFileError(e); jjg@451: } jjg@451: } jjg@451: jjg@451: // Type.Visitor methods jjg@451: jjg@451: private void findDependencies(Type t) { jjg@451: if (t != null) jjg@451: t.accept(this, null); jjg@451: } jjg@451: jjg@451: private void findDependencies(List ts) { jjg@451: if (ts != null) { jjg@451: for (Type t: ts) jjg@451: t.accept(this, null); jjg@451: } jjg@451: } jjg@451: jjg@451: public Void visitSimpleType(SimpleType type, Void p) { jjg@451: return null; jjg@451: } jjg@451: jjg@451: public Void visitArrayType(ArrayType type, Void p) { jjg@451: findDependencies(type.elemType); jjg@451: return null; jjg@451: } jjg@451: jjg@451: public Void visitMethodType(MethodType type, Void p) { jjg@451: findDependencies(type.paramTypes); jjg@451: findDependencies(type.returnType); jjg@451: findDependencies(type.throwsTypes); mchung@1472: findDependencies(type.typeParamTypes); jjg@451: return null; jjg@451: } jjg@451: jjg@451: public Void visitClassSigType(ClassSigType type, Void p) { jjg@451: findDependencies(type.superclassType); jjg@451: findDependencies(type.superinterfaceTypes); jjg@451: return null; jjg@451: } jjg@451: jjg@451: public Void visitClassType(ClassType type, Void p) { jjg@451: findDependencies(type.outerType); mchung@1472: addDependency(type.getBinaryName()); jjg@451: findDependencies(type.typeArgs); jjg@451: return null; jjg@451: } jjg@451: jjg@451: public Void visitTypeParamType(TypeParamType type, Void p) { jjg@451: findDependencies(type.classBound); jjg@451: findDependencies(type.interfaceBounds); jjg@451: return null; jjg@451: } jjg@451: jjg@451: public Void visitWildcardType(WildcardType type, Void p) { jjg@451: findDependencies(type.boundType); jjg@451: return null; jjg@451: } jjg@451: } jjg@451: } jjg@451: }