diff --git a/src/share/classes/com/sun/tools/classfile/Dependencies.java b/src/share/classes/com/sun/tools/classfile/Dependencies.java new file mode 100644 index 0000000000000000000000000000000000000000..4daa6ac8d15992e0f1ddc7b474ca16a651839a2c --- /dev/null +++ b/src/share/classes/com/sun/tools/classfile/Dependencies.java @@ -0,0 +1,718 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.tools.classfile; + +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +import com.sun.tools.classfile.Dependency.Finder; +import com.sun.tools.classfile.Dependency.Filter; +import com.sun.tools.classfile.Dependency.Location; +import com.sun.tools.classfile.Type.ArrayType; +import com.sun.tools.classfile.Type.ClassSigType; +import com.sun.tools.classfile.Type.ClassType; +import com.sun.tools.classfile.Type.MethodType; +import com.sun.tools.classfile.Type.SimpleType; +import com.sun.tools.classfile.Type.TypeParamType; +import com.sun.tools.classfile.Type.WildcardType; + +import static com.sun.tools.classfile.ConstantPool.*; + +/** + * A framework for determining {@link Dependency dependencies} between class files. + * + * A {@link Dependency.Finder finder} is used to identify the dependencies of + * individual classes. Some finders may return subtypes of {@code Dependency} to + * further characterize the type of dependency, such as a dependency on a + * method within a class. + * + * A {@link Dependency.Filter filter} may be used to restrict the set of + * dependencies found by a finder. + * + * Dependencies that are found may be passed to a {@link Dependencies.Recorder + * recorder} so that the dependencies can be stored in a custom data structure. + */ +public class Dependencies { + /** + * Thrown when a class file cannot be found. + */ + public static class ClassFileNotFoundException extends Exception { + private static final long serialVersionUID = 3632265927794475048L; + + public ClassFileNotFoundException(String className) { + super(className); + this.className = className; + } + + public ClassFileNotFoundException(String className, Throwable cause) { + this(className); + initCause(cause); + } + + public final String className; + } + + /** + * Thrown when an exception is found processing a class file. + */ + public static class ClassFileError extends Error { + private static final long serialVersionUID = 4111110813961313203L; + + public ClassFileError(Throwable cause) { + initCause(cause); + } + } + + /** + * Service provider interface to locate and read class files. + */ + public interface ClassFileReader { + /** + * Get the ClassFile object for a specified class. + * @param className the name of the class to be returned. + * @return the ClassFile for the given class + * @throws Dependencies#ClassFileNotFoundException if the classfile cannot be + * found + */ + public ClassFile getClassFile(String className) + throws ClassFileNotFoundException; + } + + /** + * Service provide interface to handle results. + */ + public interface Recorder { + /** + * Record a dependency that has been found. + * @param d + */ + public void addDependency(Dependency d); + } + + /** + * Get the default finder used to locate the dependencies for a class. + * @return the default finder + */ + public static Finder getDefaultFinder() { + return new APIDependencyFinder(AccessFlags.ACC_PRIVATE); + } + + /** + * Get a finder used to locate the API dependencies for a class. + * These include the superclass, superinterfaces, and classes referenced in + * the declarations of fields and methods. The fields and methods that + * are checked can be limited according to a specified access. + * The access parameter must be one of {@link AccessFlags#ACC_PUBLIC ACC_PUBLIC}, + * {@link AccessFlags#ACC_PRIVATE ACC_PRIVATE}, + * {@link AccessFlags#ACC_PROTECTED ACC_PROTECTED}, or 0 for + * package private access. Members with greater than or equal accessibility + * to that specified will be searched for dependencies. + * @param access the access of members to be checked + * @return an API finder + */ + public static Finder getAPIFinder(int access) { + return new APIDependencyFinder(access); + } + + /** + * Get the finder used to locate the dependencies for a class. + * @return the finder + */ + public Finder getFinder() { + if (finder == null) + finder = getDefaultFinder(); + return finder; + } + + /** + * Set the finder used to locate the dependencies for a class. + * @param f the finder + */ + public void setFinder(Finder f) { + f.getClass(); // null check + finder = f; + } + + /** + * Get the default filter used to determine included when searching + * the transitive closure of all the dependencies. + * Unless overridden, the default filter accepts all dependencies. + * @return the default filter. + */ + public static Filter getDefaultFilter() { + return DefaultFilter.instance(); + } + + /** + * Get a filter which uses a regular expression on the target's class name + * to determine if a dependency is of interest. + * @param pattern the pattern used to match the target's class name + * @return a filter for matching the target class name with a regular expression + */ + public static Filter getRegexFilter(Pattern pattern) { + return new TargetRegexFilter(pattern); + } + + /** + * Get a filter which checks the package of a target's class name + * to determine if a dependency is of interest. The filter checks if the + * package of the target's class matches any of a set of given package + * names. The match may optionally match subpackages of the given names as well. + * @param packageNames the package names used to match the target's class name + * @param matchSubpackages whether or not to match subpackages as well + * @return a filter for checking the target package name against a list of package names + */ + public static Filter getPackageFilter(Set packageNames, boolean matchSubpackages) { + return new TargetPackageFilter(packageNames, matchSubpackages); + } + + /** + * Get the filter used to determine the dependencies included when searching + * the transitive closure of all the dependencies. + * Unless overridden, the default filter accepts all dependencies. + * @return the filter + */ + public Filter getFilter() { + if (filter == null) + filter = getDefaultFilter(); + return filter; + } + + /** + * Set the filter used to determine the dependencies included when searching + * the transitive closure of all the dependencies. + * @param f the filter + */ + public void setFilter(Filter f) { + f.getClass(); // null check + filter = f; + } + + /** + * Find the dependencies of a class, using the current + * {@link Dependencies#getFinder finder} and + * {@link Dependencies#getFilter filter}. + * The search may optionally include the transitive closure of all the + * filtered dependencies, by also searching in the classes named in those + * dependencies. + * @param classFinder a finder to locate class files + * @param rootClassNames the names of the root classes from which to begin + * searching + * @param transitiveClosure whether or not to also search those classes + * named in any filtered dependencies that are found. + * @return the set of dependencies that were found + * @throws ClassFileNotFoundException if a required class file cannot be found + * @throws ClassFileError if an error occurs while processing a class file, + * such as an error in the internal class file structure. + */ + public Set findAllDependencies( + ClassFileReader classFinder, Set rootClassNames, + boolean transitiveClosure) + throws ClassFileNotFoundException { + final Set results = new HashSet(); + Recorder r = new Recorder() { + public void addDependency(Dependency d) { + results.add(d); + } + }; + findAllDependencies(classFinder, rootClassNames, transitiveClosure, r); + return results; + } + + + + /** + * Find the dependencies of a class, using the current + * {@link Dependencies#getFinder finder} and + * {@link Dependencies#getFilter filter}. + * The search may optionally include the transitive closure of all the + * filtered dependencies, by also searching in the classes named in those + * dependencies. + * @param classFinder a finder to locate class files + * @param rootClassNames the names of the root classes from which to begin + * searching + * @param transitiveClosure whether or not to also search those classes + * named in any filtered dependencies that are found. + * @param recorder a recorder for handling the results + * @throws ClassFileNotFoundException if a required class file cannot be found + * @throws ClassFileError if an error occurs while processing a class file, + * such as an error in the internal class file structure. + */ + public void findAllDependencies( + ClassFileReader classFinder, Set rootClassNames, + boolean transitiveClosure, Recorder recorder) + throws ClassFileNotFoundException { + Set doneClasses = new HashSet(); + + getFinder(); // ensure initialized + getFilter(); // ensure initialized + + // Work queue of names of classfiles to be searched. + // Entries will be unique, and for classes that do not yet have + // dependencies in the results map. + Deque deque = new LinkedList(rootClassNames); + + String className; + while ((className = deque.poll()) != null) { + assert (!doneClasses.contains(className)); + doneClasses.add(className); + + ClassFile cf = classFinder.getClassFile(className); + + // The following code just applies the filter to the dependencies + // followed for the transitive closure. + for (Dependency d: finder.findDependencies(cf)) { + recorder.addDependency(d); + if (transitiveClosure && filter.accepts(d)) { + String cn = d.getTarget().getClassName(); + if (!doneClasses.contains(cn)) + deque.add(cn); + } + } + } + } + + private Filter filter; + private Finder finder; + + /** + * A location identifying a class. + */ + static class SimpleLocation implements Location { + public SimpleLocation(String className) { + this.className = className; + } + + /** + * Get the name of the class being depended on. This name will be used to + * locate the class file for transitive dependency analysis. + * @return the name of the class being depended on + */ + public String getClassName() { + return className; + } + + @Override + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof SimpleLocation)) + return false; + return (className.equals(((SimpleLocation) other).className)); + } + + @Override + public int hashCode() { + return className.hashCode(); + } + + @Override + public String toString() { + return className; + } + + private String className; + } + + /** + * A dependency of one class on another. + */ + static class SimpleDependency implements Dependency { + public SimpleDependency(Location origin, Location target) { + this.origin = origin; + this.target = target; + } + + public Location getOrigin() { + return origin; + } + + public Location getTarget() { + return target; + } + + @Override + public boolean equals(Object other) { + if (this == other) + return true; + if (!(other instanceof SimpleDependency)) + return false; + SimpleDependency o = (SimpleDependency) other; + return (origin.equals(o.origin) && target.equals(o.target)); + } + + @Override + public int hashCode() { + return origin.hashCode() * 31 + target.hashCode(); + } + + @Override + public String toString() { + return origin + ":" + target; + } + + private Location origin; + private Location target; + } + + + /** + * This class accepts all dependencies. + */ + static class DefaultFilter implements Filter { + private static DefaultFilter instance; + + static DefaultFilter instance() { + if (instance == null) + instance = new DefaultFilter(); + return instance; + } + + public boolean accepts(Dependency dependency) { + return true; + } + } + + /** + * This class accepts those dependencies whose target's class name matches a + * regular expression. + */ + static class TargetRegexFilter implements Filter { + TargetRegexFilter(Pattern pattern) { + this.pattern = pattern; + } + + public boolean accepts(Dependency dependency) { + return pattern.matcher(dependency.getTarget().getClassName()).matches(); + } + + Pattern pattern; + } + + /** + * This class accepts those dependencies whose class name is in a given + * package. + */ + static class TargetPackageFilter implements Filter { + TargetPackageFilter(Set packageNames, boolean matchSubpackages) { + for (String pn: packageNames) { + if (pn.length() == 0) // implies null check as well + throw new IllegalArgumentException(); + } + this.packageNames = packageNames; + this.matchSubpackages = matchSubpackages; + } + + public boolean accepts(Dependency dependency) { + String cn = dependency.getTarget().getClassName(); + int lastSep = cn.lastIndexOf("/"); + String pn = (lastSep == -1 ? "" : cn.substring(0, lastSep)); + if (packageNames.contains(pn)) + return true; + + if (matchSubpackages) { + for (String n: packageNames) { + if (pn.startsWith(n + ".")) + return true; + } + } + + return false; + } + + Set packageNames; + boolean matchSubpackages; + } + + + + /** + * This class identifies class names directly or indirectly in the constant pool. + */ + static class ClassDependencyFinder extends BasicDependencyFinder { + public Iterable findDependencies(ClassFile classfile) { + Visitor v = new Visitor(classfile); + for (CPInfo cpInfo: classfile.constant_pool.entries()) { + v.scan(cpInfo); + } + return v.deps; + } + } + + /** + * This class identifies class names in the signatures of classes, fields, + * and methods in a class. + */ + static class APIDependencyFinder extends BasicDependencyFinder { + APIDependencyFinder(int access) { + switch (access) { + case AccessFlags.ACC_PUBLIC: + case AccessFlags.ACC_PROTECTED: + case AccessFlags.ACC_PRIVATE: + case 0: + showAccess = access; + break; + default: + throw new IllegalArgumentException("invalid access 0x" + + Integer.toHexString(access)); + } + } + + public Iterable findDependencies(ClassFile classfile) { + try { + Visitor v = new Visitor(classfile); + v.addClass(classfile.super_class); + v.addClasses(classfile.interfaces); + // inner classes? + for (Field f : classfile.fields) { + if (checkAccess(f.access_flags)) + v.scan(f.descriptor, f.attributes); + } + for (Method m : classfile.methods) { + if (checkAccess(m.access_flags)) { + v.scan(m.descriptor, m.attributes); + Exceptions_attribute e = + (Exceptions_attribute) m.attributes.get(Attribute.Exceptions); + if (e != null) + v.addClasses(e.exception_index_table); + } + } + return v.deps; + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + boolean checkAccess(AccessFlags flags) { + // code copied from javap.Options.checkAccess + boolean isPublic = flags.is(AccessFlags.ACC_PUBLIC); + boolean isProtected = flags.is(AccessFlags.ACC_PROTECTED); + boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE); + boolean isPackage = !(isPublic || isProtected || isPrivate); + + if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage)) + return false; + else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage)) + return false; + else if ((showAccess == 0) && (isPrivate)) + return false; + else + return true; + } + + private int showAccess; + } + + static abstract class BasicDependencyFinder implements Finder { + private Map locations = new HashMap(); + + Location getLocation(String className) { + Location l = locations.get(className); + if (l == null) + locations.put(className, l = new SimpleLocation(className)); + return l; + } + + class Visitor implements ConstantPool.Visitor, Type.Visitor { + private ConstantPool constant_pool; + private Set deps; + private Location origin; + + Visitor(ClassFile classFile) { + try { + constant_pool = classFile.constant_pool; + origin = getLocation(classFile.getName()); + deps = new HashSet(); + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + void scan(Descriptor d, Attributes attrs) { + try { + scan(new Signature(d.index).getType(constant_pool)); + Signature_attribute sa = (Signature_attribute) attrs.get(Attribute.Signature); + if (sa != null) + scan(new Signature(sa.signature_index).getType(constant_pool)); + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + void scan(CPInfo cpInfo) { + cpInfo.accept(this, null); + } + + void scan(Type t) { + t.accept(this, null); + } + + void addClass(int index) throws ConstantPoolException { + if (index != 0) { + String name = constant_pool.getClassInfo(index).getBaseName(); + if (name != null) + addDependency(name); + } + } + + void addClasses(int[] indices) throws ConstantPoolException { + for (int i: indices) + addClass(i); + } + + private void addDependency(String name) { + deps.add(new SimpleDependency(origin, getLocation(name))); + } + + // ConstantPool.Visitor methods + + public Void visitClass(CONSTANT_Class_info info, Void p) { + try { + if (info.getName().startsWith("[")) + new Signature(info.name_index).getType(constant_pool).accept(this, null); + else + addDependency(info.getBaseName()); + return null; + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + public Void visitDouble(CONSTANT_Double_info info, Void p) { + return null; + } + + public Void visitFieldref(CONSTANT_Fieldref_info info, Void p) { + return visitRef(info, p); + } + + public Void visitFloat(CONSTANT_Float_info info, Void p) { + return null; + } + + public Void visitInteger(CONSTANT_Integer_info info, Void p) { + return null; + } + + public Void visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) { + return visitRef(info, p); + } + + public Void visitLong(CONSTANT_Long_info info, Void p) { + return null; + } + + public Void visitNameAndType(CONSTANT_NameAndType_info info, Void p) { + try { + new Signature(info.type_index).getType(constant_pool).accept(this, null); + return null; + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + public Void visitMethodref(CONSTANT_Methodref_info info, Void p) { + return visitRef(info, p); + } + + public Void visitString(CONSTANT_String_info info, Void p) { + return null; + } + + public Void visitUtf8(CONSTANT_Utf8_info info, Void p) { + return null; + } + + private Void visitRef(CPRefInfo info, Void p) { + try { + visitClass(info.getClassInfo(), p); + return null; + } catch (ConstantPoolException e) { + throw new ClassFileError(e); + } + } + + // Type.Visitor methods + + private void findDependencies(Type t) { + if (t != null) + t.accept(this, null); + } + + private void findDependencies(List ts) { + if (ts != null) { + for (Type t: ts) + t.accept(this, null); + } + } + + public Void visitSimpleType(SimpleType type, Void p) { + return null; + } + + public Void visitArrayType(ArrayType type, Void p) { + findDependencies(type.elemType); + return null; + } + + public Void visitMethodType(MethodType type, Void p) { + findDependencies(type.paramTypes); + findDependencies(type.returnType); + findDependencies(type.throwsTypes); + return null; + } + + public Void visitClassSigType(ClassSigType type, Void p) { + findDependencies(type.superclassType); + findDependencies(type.superinterfaceTypes); + return null; + } + + public Void visitClassType(ClassType type, Void p) { + findDependencies(type.outerType); + addDependency(type.name); + findDependencies(type.typeArgs); + return null; + } + + public Void visitTypeParamType(TypeParamType type, Void p) { + findDependencies(type.classBound); + findDependencies(type.interfaceBounds); + return null; + } + + public Void visitWildcardType(WildcardType type, Void p) { + findDependencies(type.boundType); + return null; + } + } + } +} diff --git a/src/share/classes/com/sun/tools/classfile/Dependency.java b/src/share/classes/com/sun/tools/classfile/Dependency.java new file mode 100644 index 0000000000000000000000000000000000000000..b145f8081b9562d089c215a1de4c869ef174991f --- /dev/null +++ b/src/share/classes/com/sun/tools/classfile/Dependency.java @@ -0,0 +1,90 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.tools.classfile; + + +/** + * A directed relationship between two {@link Dependency.Location Location}s. + * Subtypes of {@code Dependency} may provide additional detail about the dependency. + * + * @see Dependency.Finder + * @see Dependency.Filter + * @see Dependencies + */ +public interface Dependency { + /** + * A filter used to select dependencies of interest, and to discard others. + */ + public interface Filter { + /** + * Return true if the dependency is of interest. + * @param dependency the dependency to be considered + * @return true if and only if the dependency is of interest. + */ + boolean accepts(Dependency dependency); + } + + /** + * An interface for finding the immediate dependencies of a given class file. + */ + public interface Finder { + /** + * Find the immediate dependencies of a given class file. + * @param classfile the class file to be examined + * @return the set of dependencies located in the given class file. + */ + public Iterable findDependencies(ClassFile classfile); + } + + + /** + * A location somewhere within a class. Subtypes of {@code Location} + * may be used to provide additional detail about the location. + */ + public interface Location { + /** + * Get the name of the class containing the location. + * This name will be used to locate the class file for transitive + * dependency analysis. + * @return the name of the class containing the location. + */ + String getClassName(); + } + + + /** + * Get the location that has the dependency. + * @return the location that has the dependency. + */ + Location getOrigin(); + + /** + * Get the location that is being depended upon. + * @return the location that is being depended upon. + */ + Location getTarget(); +} + diff --git a/test/tools/javap/classfile/deps/GetDeps.java b/test/tools/javap/classfile/deps/GetDeps.java new file mode 100644 index 0000000000000000000000000000000000000000..f5db9770ce60986823af26039befd11c79ef2938 --- /dev/null +++ b/test/tools/javap/classfile/deps/GetDeps.java @@ -0,0 +1,211 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.io.*; +import java.util.*; +import java.util.regex.Pattern; +import javax.tools.*; + +import com.sun.tools.classfile.*; +import com.sun.tools.classfile.Dependencies.*; +import com.sun.tools.classfile.Dependency.Location; +import com.sun.tools.javac.file.JavacFileManager; +import com.sun.tools.javac.util.Context; + +/** + * Demo utility for using the classfile dependency analysis API framework. + * + * Usage: + * getdeps [options] classes + * where options include: + * -classpath path where to find classes to analyze + * -p package-name restrict analysis to classes in this package + * (may be given multiple times) + * -r regex restrict analysis to packages matching pattern + * (-p and -r are exclusive) + * -rev invert the dependencies in the output + * -t transitive closure of dependencies + */ +public class GetDeps { + public static void main(String... args) throws Exception { + new GetDeps().run(args); + } + + void run(String... args) throws IOException, ClassFileNotFoundException { + PrintWriter pw = new PrintWriter(System.out); + try { + run(pw, args); + } finally { + pw.flush(); + } + } + + void run(PrintWriter out, String... args) throws IOException, ClassFileNotFoundException { + decodeArgs(args); + + final StandardJavaFileManager fm = new JavacFileManager(new Context(), false, null); + if (classpath != null) + fm.setLocation(StandardLocation.CLASS_PATH, classpath); + + ClassFileReader reader = new ClassFileReader(fm); + + Dependencies d = new Dependencies(); + + if (regex != null) + d.setFilter(Dependencies.getRegexFilter(Pattern.compile(regex))); + + if (packageNames.size() > 0) + d.setFilter(Dependencies.getPackageFilter(packageNames, false)); + + SortedRecorder r = new SortedRecorder(reverse); + + d.findAllDependencies(reader, rootClassNames, transitiveClosure, r); + + SortedMap> deps = r.getMap(); + for (Map.Entry> e: deps.entrySet()) { + out.println(e.getKey()); + for (Dependency dep: e.getValue()) { + out.println(" " + dep.getTarget()); + } + } + } + + void decodeArgs(String... args) { + rootClassNames = new TreeSet(); + packageNames = new TreeSet(); + + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + if (arg.equals("-classpath") && (i + 1 < args.length)) + classpath = getPathFiles(args[++i]); + else if (arg.equals("-p") && (i + 1 < args.length)) + packageNames.add(args[++i]); + else if (arg.equals("-r") && (i + 1 < args.length)) + regex = args[++i]; + else if (arg.equals("-rev")) + reverse = true; + else if (arg.equals("-t")) + transitiveClosure = true; + else if (arg.startsWith("-")) + throw new Error(arg); + else { + for ( ; i < args.length; i++) + rootClassNames.add(args[i]); + } + } + } + + List getPathFiles(String path) { + List files = new ArrayList(); + for (String p: path.split(File.pathSeparator)) { + if (p.length() > 0) + files.add(new File(p)); + } + return files; + } + + boolean transitiveClosure; + List classpath; + Set rootClassNames; + Set packageNames; + String regex; + boolean reverse; + + + static class ClassFileReader implements Dependencies.ClassFileReader { + private JavaFileManager fm; + + ClassFileReader(JavaFileManager fm) { + this.fm = fm; + } + + @Override + public ClassFile getClassFile(String className) throws ClassFileNotFoundException { + try { + JavaFileObject fo = fm.getJavaFileForInput( + StandardLocation.CLASS_PATH, className, JavaFileObject.Kind.CLASS); + if (fo == null) + fo = fm.getJavaFileForInput( + StandardLocation.PLATFORM_CLASS_PATH, className, JavaFileObject.Kind.CLASS); + if (fo == null) + throw new ClassFileNotFoundException(className); + InputStream in = fo.openInputStream(); + try { + return ClassFile.read(in); + } finally { + in.close(); + } + } catch (ConstantPoolException e) { + throw new ClassFileNotFoundException(className, e); + } catch (IOException e) { + throw new ClassFileNotFoundException(className, e); + } + } + }; + + static class SortedRecorder implements Recorder { + public SortedRecorder(boolean reverse) { + this.reverse = reverse; + } + + public void addDependency(Dependency d) { + Location o = (reverse ? d.getTarget() : d.getOrigin()); + SortedSet odeps = map.get(o); + if (odeps == null) { + Comparator c = (reverse ? originComparator : targetComparator); + map.put(o, odeps = new TreeSet(c)); + } + odeps.add(d); + } + + public SortedMap> getMap() { + return map; + } + + private Comparator originComparator = new Comparator() { + public int compare(Dependency o1, Dependency o2) { + return o1.getTarget().toString().compareTo(o2.getOrigin().toString()); + } + }; + + private Comparator targetComparator = new Comparator() { + public int compare(Dependency o1, Dependency o2) { + return o1.getTarget().toString().compareTo(o2.getTarget().toString()); + } + }; + + private Comparator locationComparator = new Comparator() { + public int compare(Location o1, Location o2) { + return o1.toString().compareTo(o2.toString()); + } + }; + + private final SortedMap> map = + new TreeMap>(locationComparator); + + boolean reverse; + } + +} diff --git a/test/tools/javap/classfile/deps/T6907575.java b/test/tools/javap/classfile/deps/T6907575.java new file mode 100644 index 0000000000000000000000000000000000000000..23983ec79288e640b691b8f9012b9b044d03cda4 --- /dev/null +++ b/test/tools/javap/classfile/deps/T6907575.java @@ -0,0 +1,71 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6907575 + * @build GetDeps p.C1 + * @run main T6907575 + */ + +import java.io.*; + +public class T6907575 { + public static void main(String... args) throws Exception { + new T6907575().run(); + } + + void run() throws Exception { + String testSrc = System.getProperty("test.src"); + String testClasses = System.getProperty("test.classes"); + + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + GetDeps gd = new GetDeps(); + gd.run(pw, "-classpath", testClasses, "-t", "-p", "p", "p/C1"); + pw.close(); + System.out.println(sw); + + String ref = readFile(new File(testSrc, "T6907575.out")); + diff(sw.toString().replaceAll("[\r\n]+", "\n"), ref); + } + + void diff(String actual, String ref) throws Exception { + System.out.println("EXPECT:>>>" + ref + "<<<"); + System.out.println("ACTUAL:>>>" + actual + "<<<"); + if (!actual.equals(ref)) + throw new Exception("output not as expected"); + } + + String readFile(File f) throws IOException { + Reader r = new FileReader(f); + char[] buf = new char[(int) f.length()]; + int offset = 0; + int n; + while (offset < buf.length && (n = r.read(buf, offset, buf.length - offset)) != -1) + offset += n; + return new String(buf, 0, offset); + } +} diff --git a/test/tools/javap/classfile/deps/T6907575.out b/test/tools/javap/classfile/deps/T6907575.out new file mode 100644 index 0000000000000000000000000000000000000000..f1315914eb1925a39d35afb2bb9cd62b9b39b80e --- /dev/null +++ b/test/tools/javap/classfile/deps/T6907575.out @@ -0,0 +1,8 @@ +p/C1 + java/lang/Object + p/C2 +p/C2 + java/lang/Object + p/C3 +p/C3 + java/lang/Object diff --git a/test/tools/javap/classfile/deps/p/C1.java b/test/tools/javap/classfile/deps/p/C1.java new file mode 100644 index 0000000000000000000000000000000000000000..71f253e128b47ff83d77fbd8dda8c2a1d72e296b --- /dev/null +++ b/test/tools/javap/classfile/deps/p/C1.java @@ -0,0 +1,37 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package p; + +public class C1 { + C2 c2; +} + +class C2 { + C3 c3; +} + +class C3 { +}