提交 ce95d32c 编写于 作者: M mchung

8003562: Provide a CLI tool to analyze class dependencies

Reviewed-by: jjg, alanb, ulfzibis, erikj
上级 b5ba8c3e
......@@ -153,6 +153,7 @@ javah.tests = \
javap.includes = \
com/sun/tools/classfile/ \
com/sun/tools/javap/ \
com/sun/tools/jdeps/ \
sun/tools/javap/
javap.tests = \
......
......@@ -75,6 +75,7 @@ $(LANGTOOLS_OUTPUTDIR)/gensrc/_the_props.d : $(PROPSOURCES) $(BUILD_TOOLS)
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javah/resources/version.properties
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javap/resources/version.properties
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javac/resources/version.properties
printf "jdk=$(JDK_VERSION)\nfull=$(FULL_VERSION)\nrelease=$(RELEASE)\n" > $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/jdeps/resources/version.properties
echo Compiling $(words $(PROPSOURCES) v1 v2 v3) properties into resource bundles
$(TOOL_COMPILEPROPS_CMD) $(PROPCMDLINE) \
-compile $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javah/resources/version.properties \
......@@ -85,6 +86,9 @@ $(LANGTOOLS_OUTPUTDIR)/gensrc/_the_props.d : $(PROPSOURCES) $(BUILD_TOOLS)
java.util.ListResourceBundle \
-compile $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javac/resources/version.properties \
$(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/javac/resources/version.java \
java.util.ListResourceBundle \
-compile $(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/jdeps/resources/version.properties \
$(LANGTOOLS_OUTPUTDIR)/gensrc/com/sun/tools/jdeps/resources/version.java \
java.util.ListResourceBundle
echo PROPS_ARE_CREATED=yes > $@
......
......@@ -141,6 +141,15 @@ public class Dependencies {
return new APIDependencyFinder(access);
}
/**
* Get a finder to do class dependency analysis.
*
* @return a Class dependency finder
*/
public static Finder getClassDependencyFinder() {
return new ClassDependencyFinder();
}
/**
* Get the finder used to locate the dependencies for a class.
* @return the finder
......@@ -246,8 +255,6 @@ public class Dependencies {
return results;
}
/**
* Find the dependencies of a class, using the current
* {@link Dependencies#getFinder finder} and
......@@ -306,38 +313,44 @@ public class Dependencies {
* A location identifying a class.
*/
static class SimpleLocation implements Location {
public SimpleLocation(String className) {
this.className = className;
public SimpleLocation(String name) {
this.name = name;
this.className = name.replace('/', '.').replace('$', '.');
}
public String getName() {
return name;
}
/**
* 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;
}
public String getPackageName() {
int i = name.lastIndexOf('/');
return (i > 0) ? name.substring(0, i).replace('/', '.') : "";
}
@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (!(other instanceof SimpleLocation))
return false;
return (className.equals(((SimpleLocation) other).className));
return (name.equals(((SimpleLocation) other).name));
}
@Override
public int hashCode() {
return className.hashCode();
return name.hashCode();
}
@Override
public String toString() {
return className;
return name;
}
private String name;
private String className;
}
......@@ -431,9 +444,7 @@ public class Dependencies {
}
public boolean accepts(Dependency dependency) {
String cn = dependency.getTarget().getClassName();
int lastSep = cn.lastIndexOf("/");
String pn = (lastSep == -1 ? "" : cn.substring(0, lastSep));
String pn = dependency.getTarget().getPackageName();
if (packageNames.contains(pn))
return true;
......@@ -451,8 +462,6 @@ public class Dependencies {
private final boolean matchSubpackages;
}
/**
* This class identifies class names directly or indirectly in the constant pool.
*/
......@@ -462,6 +471,26 @@ public class Dependencies {
for (CPInfo cpInfo: classfile.constant_pool.entries()) {
v.scan(cpInfo);
}
try {
v.addClass(classfile.super_class);
v.addClasses(classfile.interfaces);
v.scan(classfile.attributes);
for (Field f : classfile.fields) {
v.scan(f.descriptor, f.attributes);
}
for (Method m : classfile.methods) {
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);
}
}
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
return v.deps;
}
}
......@@ -558,9 +587,7 @@ public class Dependencies {
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));
scan(attrs);
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
......@@ -574,6 +601,43 @@ public class Dependencies {
t.accept(this, null);
}
void scan(Attributes attrs) {
try {
Signature_attribute sa = (Signature_attribute)attrs.get(Attribute.Signature);
if (sa != null)
scan(sa.getParsedSignature().getType(constant_pool));
scan((RuntimeVisibleAnnotations_attribute)
attrs.get(Attribute.RuntimeVisibleAnnotations));
scan((RuntimeVisibleParameterAnnotations_attribute)
attrs.get(Attribute.RuntimeVisibleParameterAnnotations));
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
}
private void scan(RuntimeAnnotations_attribute attr) throws ConstantPoolException {
if (attr == null) {
return;
}
for (int i = 0; i < attr.annotations.length; i++) {
int index = attr.annotations[i].type_index;
scan(new Signature(index).getType(constant_pool));
}
}
private void scan(RuntimeParameterAnnotations_attribute attr) throws ConstantPoolException {
if (attr == null) {
return;
}
for (int param = 0; param < attr.parameter_annotations.length; param++) {
for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
int index = attr.parameter_annotations[param][i].type_index;
scan(new Signature(index).getType(constant_pool));
}
}
}
void addClass(int index) throws ConstantPoolException {
if (index != 0) {
String name = constant_pool.getClassInfo(index).getBaseName();
......@@ -698,6 +762,7 @@ public class Dependencies {
findDependencies(type.paramTypes);
findDependencies(type.returnType);
findDependencies(type.throwsTypes);
findDependencies(type.typeParamTypes);
return null;
}
......@@ -709,7 +774,7 @@ public class Dependencies {
public Void visitClassType(ClassType type, Void p) {
findDependencies(type.outerType);
addDependency(type.name);
addDependency(type.getBinaryName());
findDependencies(type.typeArgs);
return null;
}
......
......@@ -71,7 +71,19 @@ public interface Dependency {
* dependency analysis.
* @return the name of the class containing the location.
*/
String getName();
/**
* Get the fully-qualified name of the class containing the location.
* @return the fully-qualified name of the class containing the location.
*/
String getClassName();
/**
* Get the package name of the class containing the location.
* @return the package name of the class containing the location.
*/
String getPackageName();
}
......
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.jdeps;
import com.sun.tools.classfile.Dependency;
import com.sun.tools.classfile.Dependency.Location;
import java.io.File;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* Represents the source of the class files.
*/
public class Archive {
private static Map<String,Archive> archiveForClass = new HashMap<String,Archive>();
public static Archive find(Location loc) {
return archiveForClass.get(loc.getName());
}
private final File file;
private final String filename;
private final DependencyRecorder recorder;
private final ClassFileReader reader;
public Archive(String name) {
this.file = null;
this.filename = name;
this.recorder = new DependencyRecorder();
this.reader = null;
}
public Archive(File f, ClassFileReader reader) {
this.file = f;
this.filename = f.getName();
this.recorder = new DependencyRecorder();
this.reader = reader;
}
public ClassFileReader reader() {
return reader;
}
public String getFileName() {
return filename;
}
public void addClass(String classFileName) {
Archive a = archiveForClass.get(classFileName);
assert(a == null || a == this); // ## issue warning?
if (!archiveForClass.containsKey(classFileName)) {
archiveForClass.put(classFileName, this);
}
}
public void addDependency(Dependency d) {
recorder.addDependency(d);
}
/**
* Returns a sorted map of a class to its dependencies.
*/
public SortedMap<Location, SortedSet<Location>> getDependencies() {
DependencyRecorder.Filter filter = new DependencyRecorder.Filter() {
public boolean accept(Location origin, Location target) {
return (archiveForClass.get(origin.getName()) !=
archiveForClass.get(target.getName()));
}};
SortedMap<Location, SortedSet<Location>> result =
new TreeMap<Location, SortedSet<Location>>(locationComparator);
for (Map.Entry<Location, Set<Location>> e : recorder.dependencies().entrySet()) {
Location o = e.getKey();
for (Location t : e.getValue()) {
if (filter.accept(o, t)) {
SortedSet<Location> odeps = result.get(o);
if (odeps == null) {
odeps = new TreeSet<Location>(locationComparator);
result.put(o, odeps);
}
odeps.add(t);
}
}
}
return result;
}
/**
* Returns the set of archives this archive requires.
*/
public Set<Archive> getRequiredArchives() {
SortedSet<Archive> deps = new TreeSet<Archive>(new Comparator<Archive>() {
public int compare(Archive a1, Archive a2) {
return a1.toString().compareTo(a2.toString());
}
});
for (Map.Entry<Location, Set<Location>> e : recorder.dependencies().entrySet()) {
Location o = e.getKey();
Archive origin = Archive.find(o);
for (Location t : e.getValue()) {
Archive target = Archive.find(t);
assert(origin != null && target != null);
if (origin != target) {
if (!deps.contains(target)) {
deps.add(target);
}
}
}
}
return deps;
}
public String toString() {
return file != null ? file.getPath() : filename;
}
private static class DependencyRecorder {
static interface Filter {
boolean accept(Location origin, Location target);
}
public void addDependency(Dependency d) {
Set<Location> odeps = map.get(d.getOrigin());
if (odeps == null) {
odeps = new HashSet<Location>();
map.put(d.getOrigin(), odeps);
}
odeps.add(d.getTarget());
}
public Map<Location, Set<Location>> dependencies() {
return map;
}
private final Map<Location, Set<Location>> map =
new HashMap<Location, Set<Location>>();
}
private static Comparator<Location> locationComparator =
new Comparator<Location>() {
public int compare(Location o1, Location o2) {
return o1.toString().compareTo(o2.toString());
}
};
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.jdeps;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.Dependencies.ClassFileError;
import java.io.*;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* ClassFileReader reads ClassFile(s) of a given path that can be
* a .class file, a directory, or a JAR file.
*/
public class ClassFileReader {
/**
* Returns a ClassFileReader instance of a given path.
*/
public static ClassFileReader newInstance(File path) throws IOException {
if (!path.exists()) {
throw new FileNotFoundException(path.getAbsolutePath());
}
if (path.isDirectory()) {
return new DirectoryReader(path.toPath());
} else if (path.getName().endsWith(".jar")) {
return new JarFileReader(path.toPath());
} else {
return new ClassFileReader(path.toPath());
}
}
protected final Path path;
protected final String baseFileName;
private ClassFileReader(Path path) {
this.path = path;
this.baseFileName = path.getFileName() != null
? path.getFileName().toString()
: path.toString();
}
public String getFileName() {
return baseFileName;
}
/**
* Returns the ClassFile matching the given binary name
* or a fully-qualified class name.
*/
public ClassFile getClassFile(String name) throws IOException {
if (name.indexOf('.') > 0) {
int i = name.lastIndexOf('.');
String pathname = name.replace('.', File.separatorChar) + ".class";
if (baseFileName.equals(pathname) ||
baseFileName.equals(pathname.substring(0, i) + "$" +
pathname.substring(i+1, pathname.length()))) {
return readClassFile(path);
}
} else {
if (baseFileName.equals(name.replace('/', File.separatorChar) + ".class")) {
return readClassFile(path);
}
}
return null;
}
public Iterable<ClassFile> getClassFiles() throws IOException {
return new Iterable<ClassFile>() {
public Iterator<ClassFile> iterator() {
return new FileIterator();
}
};
}
protected ClassFile readClassFile(Path p) throws IOException {
InputStream is = null;
try {
is = Files.newInputStream(p);
return ClassFile.read(is);
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
} finally {
if (is != null) {
is.close();
}
}
}
class FileIterator implements Iterator<ClassFile> {
int count;
FileIterator() {
this.count = 0;
}
public boolean hasNext() {
return count == 0 && baseFileName.endsWith(".class");
}
public ClassFile next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
try {
ClassFile cf = readClassFile(path);
count++;
return cf;
} catch (IOException e) {
throw new ClassFileError(e);
}
}
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
public String toString() {
return path.toString();
}
private static class DirectoryReader extends ClassFileReader {
DirectoryReader(Path path) throws IOException {
super(path);
}
public ClassFile getClassFile(String name) throws IOException {
if (name.indexOf('.') > 0) {
int i = name.lastIndexOf('.');
String pathname = name.replace('.', File.separatorChar) + ".class";
Path p = path.resolve(pathname);
if (!p.toFile().exists()) {
p = path.resolve(pathname.substring(0, i) + "$" +
pathname.substring(i+1, pathname.length()));
}
if (p.toFile().exists()) {
return readClassFile(p);
}
} else {
Path p = path.resolve(name + ".class");
if (p.toFile().exists()) {
return readClassFile(p);
}
}
return null;
}
public Iterable<ClassFile> getClassFiles() throws IOException {
final Iterator<ClassFile> iter = new DirectoryIterator();
return new Iterable<ClassFile>() {
public Iterator<ClassFile> iterator() {
return iter;
}
};
}
private List<Path> walkTree(Path dir) throws IOException {
final List<Path> files = new ArrayList<Path>();
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
if (file.toFile().getName().endsWith(".class")) {
files.add(file);
}
return FileVisitResult.CONTINUE;
}
});
return files;
}
class DirectoryIterator implements Iterator<ClassFile> {
private List<Path> entries;
private int index = 0;
DirectoryIterator() throws IOException {
entries = walkTree(path);
index = 0;
}
public boolean hasNext() {
return index != entries.size();
}
public ClassFile next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
Path path = entries.get(index++);
try {
return readClassFile(path);
} catch (IOException e) {
throw new ClassFileError(e);
}
}
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
}
private static class JarFileReader extends ClassFileReader {
final JarFile jarfile;
JarFileReader(Path path) throws IOException {
super(path);
this.jarfile = new JarFile(path.toFile());
}
public ClassFile getClassFile(String name) throws IOException {
if (name.indexOf('.') > 0) {
int i = name.lastIndexOf('.');
String entryName = name.replace('.', '/') + ".class";
JarEntry e = jarfile.getJarEntry(entryName);
if (e == null) {
e = jarfile.getJarEntry(entryName.substring(0, i) + "$"
+ entryName.substring(i + 1, entryName.length()));
}
if (e != null) {
return readClassFile(e);
}
} else {
JarEntry e = jarfile.getJarEntry(name + ".class");
if (e != null) {
return readClassFile(e);
}
}
return null;
}
private ClassFile readClassFile(JarEntry e) throws IOException {
InputStream is = null;
try {
is = jarfile.getInputStream(e);
return ClassFile.read(is);
} catch (ConstantPoolException ex) {
throw new ClassFileError(ex);
} finally {
if (is != null)
is.close();
}
}
public Iterable<ClassFile> getClassFiles() throws IOException {
final Iterator<ClassFile> iter = new JarFileIterator();
return new Iterable<ClassFile>() {
public Iterator<ClassFile> iterator() {
return iter;
}
};
}
class JarFileIterator implements Iterator<ClassFile> {
private Enumeration<JarEntry> entries;
private JarEntry nextEntry;
JarFileIterator() {
this.entries = jarfile.entries();
while (entries.hasMoreElements()) {
JarEntry e = entries.nextElement();
String name = e.getName();
if (name.endsWith(".class")) {
this.nextEntry = e;
break;
}
}
}
public boolean hasNext() {
return nextEntry != null;
}
public ClassFile next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
ClassFile cf;
try {
cf = readClassFile(nextEntry);
} catch (IOException ex) {
throw new ClassFileError(ex);
}
JarEntry entry = nextEntry;
nextEntry = null;
while (entries.hasMoreElements()) {
JarEntry e = entries.nextElement();
String name = e.getName();
if (name.endsWith(".class")) {
nextEntry = e;
break;
}
}
return cf;
}
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
}
}
}
}
此差异已折叠。
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.jdeps;
import java.io.*;
/**
*
* Usage:
* jdeps [options] files ...
* where options include:
* -p package-name restrict analysis to classes in this package
* (may be given multiple times)
* -e regex restrict analysis to packages matching pattern
* (-p and -e are exclusive)
* -v show class-level dependencies
* default: package-level dependencies
* -r --recursive transitive dependencies analysis
* -classpath paths Classpath to locate class files
* -all process all class files in the given classpath
*/
public class Main {
public static void main(String... args) throws Exception {
JdepsTask t = new JdepsTask();
int rc = t.run(args);
System.exit(rc);
}
/**
* Entry point that does <i>not</i> call System.exit.
*
* @param args command line arguments
* @param out output stream
* @return an exit code. 0 means success, non-zero means an error occurred.
*/
public static int run(String[] args, PrintWriter out) {
JdepsTask t = new JdepsTask();
t.setLog(out);
return t.run(args);
}
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.jdeps;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
/**
* ClassPath for Java SE and JDK
*/
class PlatformClassPath {
/*
* Profiles for Java SE
*
* This is a temporary workaround until a common API is defined for langtools
* to determine which profile a given classname belongs to. The list of
* packages and profile names are hardcoded in jdk.properties and
* split packages are not supported.
*/
static class Profile {
final String name;
final Set<String> packages;
Profile(String name) {
this.name = name;
this.packages = new HashSet<String>();
}
}
private final static String JAVAFX = "javafx";
private final static Map<String,Profile> map = getProfilePackages();
static String getProfileName(String packageName) {
Profile profile = map.get(packageName);
if (packageName.startsWith(JAVAFX + ".")) {
profile = map.get(JAVAFX);
}
return profile != null ? profile.name : "";
}
private final static List<Archive> javaHomeArchives = init();
static List<Archive> getArchives() {
return javaHomeArchives;
}
static boolean contains(Archive archive) {
return javaHomeArchives.contains(archive);
}
private static List<Archive> init() {
List<Archive> result = new ArrayList<Archive>();
String javaHome = System.getProperty("java.home");
List<File> files = new ArrayList<File>();
File jre = new File(javaHome, "jre");
File lib = new File(javaHome, "lib");
try {
if (jre.exists() && jre.isDirectory()) {
result.addAll(addJarFiles(new File(jre, "lib")));
result.addAll(addJarFiles(lib));
} else if (lib.exists() && lib.isDirectory()) {
// either a JRE or a jdk build image
File classes = new File(javaHome, "classes");
if (classes.exists() && classes.isDirectory()) {
// jdk build outputdir
result.add(new Archive(classes, ClassFileReader.newInstance(classes)));
}
// add other JAR files
result.addAll(addJarFiles(lib));
} else {
throw new RuntimeException("\"" + javaHome + "\" not a JDK home");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
// add a JavaFX profile if there is jfxrt.jar
for (Archive archive : result) {
if (archive.getFileName().equals("jfxrt.jar")) {
map.put(JAVAFX, new Profile("jfxrt.jar"));
}
}
return result;
}
private static List<Archive> addJarFiles(File f) throws IOException {
final List<Archive> result = new ArrayList<Archive>();
final Path root = f.toPath();
final Path ext = root.resolve("ext");
Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException
{
if (dir.equals(root) || dir.equals(ext)) {
return FileVisitResult.CONTINUE;
} else {
// skip other cobundled JAR files
return FileVisitResult.SKIP_SUBTREE;
}
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException
{
File f = file.toFile();
String fn = f.getName();
if (fn.endsWith(".jar") && !fn.equals("alt-rt.jar")) {
result.add(new Archive(f, ClassFileReader.newInstance(f)));
}
return FileVisitResult.CONTINUE;
}
});
return result;
}
private static Map<String,Profile> getProfilePackages() {
Map<String,Profile> map = new HashMap<String,Profile>();
// read the properties as a ResourceBundle as the build compiles
// the properties file into Java class. Another alternative is
// to load it as Properties and fix the build to exclude this file.
ResourceBundle profileBundle =
ResourceBundle.getBundle("com.sun.tools.jdeps.resources.jdk");
int i=1;
String key;
while (profileBundle.containsKey((key = "profile." + i + ".name"))) {
Profile profile = new Profile(profileBundle.getString(key));
String n = profileBundle.getString("profile." + i + ".packages");
String[] pkgs = n.split("\\s+");
for (String p : pkgs) {
if (p.isEmpty()) continue;
assert(map.containsKey(p) == false);
profile.packages.add(p);
map.put(p, profile);
}
i++;
}
return map;
}
}
main.usage.summary=\
Usage: {0} <options> <classes...>\n\
use -h, -? or --help for a list of possible options
main.usage=\
Usage: {0} <options> <classes...>\n\
where <classes> can be a pathname to a .class file, a directory, a JAR file,\n\
or a fully-qualified classname or wildcard "*". Possible options include:
error.prefix=Error:
warn.prefix=Warning:
main.opt.h=\
\ -h -? --help Print this usage message
main.opt.version=\
\ --version Version information
main.opt.V=\
\ -V <level> --verbose-level=<level> Print package-level or class-level dependencies\n\
\ Valid levels are: "package" and "class"
main.opt.v=\
\ -v --verbose Print additional information
main.opt.s=\
\ -s --summary Print dependency summary only
main.opt.p=\
\ -p <pkg name> --package=<pkg name> Restrict analysis to classes in this package\n\
\ (may be given multiple times)
main.opt.e=\
\ -e <regex> --regex=<regex> Restrict analysis to packages matching pattern\n\
\ (-p and -e are exclusive)
main.opt.P=\
\ -P --profile Show profile or the file containing a package
main.opt.c=\
\ -c <path> --classpath=<path> Specify where to find class files
main.opt.R=\
\ -R --recursive Recursively traverse all dependencies
main.opt.d=\
\ -d <depth> --depth=<depth> Specify the depth of the transitive dependency analysis
err.unknown.option=unknown option: {0}
err.missing.arg=no value given for {0}
err.internal.error=internal error: {0} {1} {2}
err.invalid.arg.for.option=invalid argument for option: {0}
err.option.after.class=option must be specified before classes: {0}
warn.invalid.arg=Invalid classname or pathname not exist: {0}
warn.split.package=package {0} defined in {1} {2}
artifact.not.found=not found
# This properties file does not need localization.
profile.1.name = compact1
profile.1.packages = \
java.io \
java.lang \
java.lang.annotation \
java.lang.invoke \
java.lang.ref \
java.lang.reflect \
java.math \
java.net \
java.nio \
java.nio.channels \
java.nio.channels.spi \
java.nio.charset \
java.nio.charset.spi \
java.nio.file \
java.nio.file.attribute \
java.nio.file.spi \
java.security \
java.security.cert \
java.security.interfaces \
java.security.spec \
java.text \
java.text.spi \
java.util \
java.util.concurrent \
java.util.concurrent.atomic \
java.util.concurrent.locks \
java.util.jar \
java.util.logging \
java.util.regex \
java.util.spi \
java.util.zip \
javax.crypto \
javax.crypto.interfaces \
javax.crypto.spec \
javax.security.auth \
javax.security.auth.callback \
javax.security.auth.login \
javax.security.auth.spi \
javax.security.auth.x500 \
javax.net \
javax.net.ssl \
javax.security.cert \
\
com.sun.net.ssl \
com.sun.nio.file \
com.sun.nio.sctp \
com.sun.security.auth \
com.sun.security.auth.login
profile.2.name = compact2
profile.2.packages = \
java.sql \
javax.sql \
javax.xml \
javax.xml.datatype \
javax.xml.namespace \
javax.xml.parsers \
javax.xml.stream \
javax.xml.stream.events \
javax.xml.stream.util \
javax.xml.transform \
javax.xml.transform.dom \
javax.xml.transform.sax \
javax.xml.transform.stax \
javax.xml.transform.stream \
javax.xml.validation \
javax.xml.xpath \
org.w3c.dom \
org.w3c.dom.bootstrap \
org.w3c.dom.events \
org.w3c.dom.ls \
org.xml.sax \
org.xml.sax.ext \
org.xml.sax.helpers \
java.rmi \
java.rmi.activation \
java.rmi.dgc \
java.rmi.registry \
java.rmi.server \
javax.rmi.ssl \
javax.transaction \
javax.transaction.xa \
\
com.sun.net.httpserver \
com.sun.net.httpserver.spi
profile.3.name = compact3
profile.3.packages = \
java.lang.instrument \
java.lang.management \
java.security.acl \
java.util.prefs \
javax.management \
javax.management.loading \
javax.management.modelmbean \
javax.management.monitor \
javax.management.openmbean \
javax.management.relation \
javax.management.remote \
javax.management.remote.rmi \
javax.management.timer \
javax.naming \
javax.naming.directory \
javax.naming.event \
javax.naming.ldap \
javax.naming.spi \
javax.sql.rowset \
javax.sql.rowset.serial \
javax.sql.rowset.spi \
javax.security.auth.kerberos \
javax.security.sasl \
javax.script \
javax.smartcardio \
javax.xml.crypto \
javax.xml.crypto.dom \
javax.xml.crypto.dsig \
javax.xml.crypto.dsig.dom \
javax.xml.crypto.dsig.keyinfo \
javax.xml.crypto.dsig.spec \
javax.annotation.processing \
javax.lang.model \
javax.lang.model.element \
javax.lang.model.type \
javax.lang.model.util \
javax.tools \
javax.tools.annotation \
org.ietf.jgss \
\
com.sun.management \
com.sun.security.auth.callback \
com.sun.security.auth.module \
com.sun.security.jgss
profile.4.name = Full JRE
profile.4.packages = \
java.applet \
java.awt \
java.awt.color \
java.awt.datatransfer \
java.awt.dnd \
java.awt.dnd.peer \
java.awt.event \
java.awt.font \
java.awt.geom \
java.awt.im \
java.awt.im.spi \
java.awt.image \
java.awt.image.renderable \
java.awt.peer \
java.awt.print \
java.beans \
java.beans.beancontext \
javax.accessibility \
javax.imageio \
javax.imageio.event \
javax.imageio.metadata \
javax.imageio.plugins.bmp \
javax.imageio.plugins.jpeg \
javax.imageio.spi \
javax.imageio.stream \
javax.print \
javax.print.attribute \
javax.print.attribute.standard \
javax.print.event \
javax.sound.midi \
javax.sound.midi.spi \
javax.sound.sampled \
javax.sound.sampled.spi \
javax.swing \
javax.swing.border \
javax.swing.colorchooser \
javax.swing.event \
javax.swing.filechooser \
javax.swing.plaf \
javax.swing.plaf.basic \
javax.swing.plaf.metal \
javax.swing.plaf.multi \
javax.swing.plaf.nimbus \
javax.swing.plaf.synth \
javax.swing.table \
javax.swing.text \
javax.swing.text.html \
javax.swing.text.html.parser \
javax.swing.text.rtf \
javax.swing.tree \
javax.swing.undo \
javax.activation \
javax.jws \
javax.jws.soap \
javax.rmi \
javax.rmi.CORBA \
javax.xml.bind \
javax.xml.bind.annotation \
javax.xml.bind.annotation.adapters \
javax.xml.bind.attachment \
javax.xml.bind.helpers \
javax.xml.bind.util \
javax.xml.soap \
javax.xml.ws \
javax.xml.ws.handler \
javax.xml.ws.handler.soap \
javax.xml.ws.http \
javax.xml.ws.soap \
javax.xml.ws.spi \
javax.xml.ws.spi.http \
javax.xml.ws.wsaddressing \
javax.annotation \
org.omg.CORBA \
org.omg.CORBA.DynAnyPackage \
org.omg.CORBA.ORBPackage \
org.omg.CORBA.TypeCodePackage \
org.omg.CORBA.portable \
org.omg.CORBA_2_3 \
org.omg.CORBA_2_3.portable \
org.omg.CosNaming \
org.omg.CosNaming.NamingContextExtPackage \
org.omg.CosNaming.NamingContextPackage \
org.omg.Dynamic \
org.omg.DynamicAny \
org.omg.DynamicAny.DynAnyFactoryPackage \
org.omg.DynamicAny.DynAnyPackage \
org.omg.IOP \
org.omg.IOP.CodecFactoryPackage \
org.omg.IOP.CodecPackage \
org.omg.Messaging \
org.omg.PortableInterceptor \
org.omg.PortableInterceptor.ORBInitInfoPackage \
org.omg.PortableServer \
org.omg.PortableServer.CurrentPackage \
org.omg.PortableServer.POAManagerPackage \
org.omg.PortableServer.POAPackage \
org.omg.PortableServer.ServantLocatorPackage \
org.omg.PortableServer.portable \
org.omg.SendingContext \
org.omg.stub.java.rmi \
org.omg.stub.javax.management.remote.rmi
# Remaining JDK supported API
profile.5.name = JDK tools
profile.5.packages = \
com.sun.jdi \
com.sun.jdi.connect \
com.sun.jdi.connect.spi \
com.sun.jdi.event \
com.sun.jdi.request \
com.sun.javadoc \
com.sun.tools.doclets \
com.sun.tools.doctree \
com.sun.source.tree \
com.sun.source.util \
com.sun.tools.attach \
com.sun.tools.attach.spi \
com.sun.tools.jconsole \
com.sun.tools.javac \
com.sun.tools.javah \
com.sun.tools.javap \
com.sun.tools.javadoc \
com.sun.servicetag
#
# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# This code is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 only, as
# published by the Free Software Foundation. Oracle designates this
# particular file as subject to the "Classpath" exception as provided
# by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
# or visit www.oracle.com if you need additional information or have any
# questions.
#
jdk=$(JDK_VERSION)
full=$(FULL_VERSION)
release=$(RELEASE)
......@@ -229,7 +229,7 @@ JCK_RUNTIME_OUTPUT_DIR = $(ABS_TEST_OUTPUT_DIR)/jck-runtime-Xcompile
all: $(JPRT_CLEAN) jtreg-tests jck-compiler-tests jck-runtime-tests $(JPRT_ARCHIVE_BUNDLE) all-summary
@echo "Testing completed successfully"
jtreg apt javac javadoc javah javap: $(JPRT_CLEAN) jtreg-tests $(JPRT_ARCHIVE_BUNDLE) jtreg-summary
jtreg apt javac javadoc javah javap jdeps: $(JPRT_CLEAN) jtreg-tests $(JPRT_ARCHIVE_BUNDLE) jtreg-summary
@echo "Testing completed successfully"
jck-compiler: $(JPRT_CLEAN) jck-compiler-tests $(JPRT_ARCHIVE_BUNDLE) jck-compiler-summary
......@@ -246,6 +246,7 @@ javac: JTREG_TESTDIRS = tools/javac
javadoc: JTREG_TESTDIRS = tools/javadoc com/sun/javadoc
javah: JTREG_TESTDIRS = tools/javah
javap: JTREG_TESTDIRS = tools/javap
jdeps: JTREG_TESTDIRS = tools/jdeps
# Run jtreg tests
#
......@@ -426,7 +427,7 @@ FRC:
# Phony targets (e.g. these are not filenames)
.PHONY: all clean \
jtreg javac javadoc javah javap jtreg-tests jtreg-summary check-jtreg \
jtreg javac javadoc javah javap jdeps jtreg-tests jtreg-summary check-jtreg \
jck-compiler jck-compiler-tests jck-compiler-summary \
jck-runtime jck-runtime-tests jck-runtime-summary check-jck
......
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8003562
* @summary Basic tests for jdeps tool
* @build Test p.Foo
* @run main Basic
*/
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import java.util.regex.*;
public class Basic {
public static void main(String... args) throws Exception {
int errors = 0;
errors += new Basic().run();
if (errors > 0)
throw new Exception(errors + " errors found");
}
int run() throws IOException {
File testDir = new File(System.getProperty("test.classes", "."));
// test a .class file
test(new File(testDir, "Test.class"),
new String[] {"java.lang", "p"});
// test a directory
test(new File(testDir, "p"),
new String[] {"java.lang", "java.util"});
// test class-level dependency output
test(new File(testDir, "Test.class"),
new String[] {"java.lang.Object", "p.Foo"},
new String[] {"-V", "class"});
// test -p option
test(new File(testDir, "Test.class"),
new String[] {"p.Foo"},
new String[] {"--verbose-level=class", "-p", "p"});
// test -e option
test(new File(testDir, "Test.class"),
new String[] {"p.Foo"},
new String[] {"-V", "class", "-e", "p\\..*"});
test(new File(testDir, "Test.class"),
new String[] {"java.lang"},
new String[] {"-V", "package", "-e", "java\\.lang\\..*"});
// test -classpath and -all options
test(null,
new String[] {"com.sun.tools.jdeps", "java.lang", "java.util",
"java.util.regex", "java.io", "p"},
new String[] {"--classpath", testDir.getPath(), "*"});
return errors;
}
void test(File file, String[] expect) {
test(file, expect, new String[0]);
}
void test(File file, String[] expect, String[] options) {
String[] args;
if (file != null) {
args = Arrays.copyOf(options, options.length+1);
args[options.length] = file.getPath();
} else {
args = options;
}
String[] deps = jdeps(args);
checkEqual("dependencies", expect, deps);
}
String[] jdeps(String... args) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
System.err.println("jdeps " + Arrays.toString(args));
int rc = com.sun.tools.jdeps.Main.run(args, pw);
pw.close();
String out = sw.toString();
if (!out.isEmpty())
System.err.println(out);
if (rc != 0)
throw new Error("jdeps failed: rc=" + rc);
return findDeps(out);
}
// Pattern used to parse lines
private static Pattern linePattern = Pattern.compile(".*\r?\n");
private static Pattern pattern = Pattern.compile("\\s+ -> (\\S+) +.*");
// Use the linePattern to break the given String into lines, applying
// the pattern to each line to see if we have a match
private static String[] findDeps(String out) {
List<String> result = new ArrayList<>();
Matcher lm = linePattern.matcher(out); // Line matcher
Matcher pm = null; // Pattern matcher
int lines = 0;
while (lm.find()) {
lines++;
CharSequence cs = lm.group(); // The current line
if (pm == null)
pm = pattern.matcher(cs);
else
pm.reset(cs);
if (pm.find())
result.add(pm.group(1));
if (lm.end() == out.length())
break;
}
return result.toArray(new String[0]);
}
void checkEqual(String label, String[] expect, String[] found) {
Set<String> s1 = new HashSet<>(Arrays.asList(expect));
Set<String> s2 = new HashSet<>(Arrays.asList(found));
if (!s1.equals(s2))
error("Unexpected " + label + " found: '" + s2 + "', expected: '" + s1 + "'");
}
void error(String msg) {
System.err.println("Error: " + msg);
errors++;
}
int errors;
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
public class Test {
public void test() {
p.Foo f = new p.Foo();
}
}
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package p;
import java.util.List;
import java.util.Collections;
public class Foo {
public static List foo() {
return Collections.emptyList();
}
public Foo() {
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册