提交 f575324f 编写于 作者: M mchung

8029548: (jdeps) use @jdk.Exported to determine supported vs JDK internal API

8031092: jdeps does not recognize --help option.
8048063: (jdeps) Add filtering capability
Reviewed-by: alanb, dfuchs
上级 963e056c
/* /*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -24,10 +24,8 @@ ...@@ -24,10 +24,8 @@
*/ */
package com.sun.tools.jdeps; package com.sun.tools.jdeps;
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
...@@ -37,6 +35,9 @@ import java.util.SortedSet; ...@@ -37,6 +35,9 @@ import java.util.SortedSet;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import com.sun.tools.classfile.Dependency.Location;
import com.sun.tools.jdeps.PlatformClassPath.JDKArchive;
/** /**
* Dependency Analyzer. * Dependency Analyzer.
*/ */
...@@ -52,7 +53,16 @@ public class Analyzer { ...@@ -52,7 +53,16 @@ public class Analyzer {
VERBOSE VERBOSE
}; };
/**
* Filter to be applied when analyzing the dependencies from the given archives.
* Only the accepted dependencies are recorded.
*/
interface Filter {
boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive);
}
private final Type type; private final Type type;
private final Filter filter;
private final Map<Archive, ArchiveDeps> results = new HashMap<>(); private final Map<Archive, ArchiveDeps> results = new HashMap<>();
private final Map<Location, Archive> map = new HashMap<>(); private final Map<Location, Archive> map = new HashMap<>();
private final Archive NOT_FOUND private final Archive NOT_FOUND
...@@ -62,15 +72,29 @@ public class Analyzer { ...@@ -62,15 +72,29 @@ public class Analyzer {
* Constructs an Analyzer instance. * Constructs an Analyzer instance.
* *
* @param type Type of the dependency analysis * @param type Type of the dependency analysis
* @param filter
*/ */
public Analyzer(Type type) { public Analyzer(Type type, Filter filter) {
this.type = type; this.type = type;
this.filter = filter;
} }
/** /**
* Performs the dependency analysis on the given archives. * Performs the dependency analysis on the given archives.
*/ */
public void run(List<Archive> archives) { public void run(List<Archive> archives) {
// build a map from Location to Archive
buildLocationArchiveMap(archives);
// traverse and analyze all dependencies
for (Archive archive : archives) {
ArchiveDeps deps = new ArchiveDeps(archive, type);
archive.visitDependences(deps);
results.put(archive, deps);
}
}
private void buildLocationArchiveMap(List<Archive> archives) {
// build a map from Location to Archive // build a map from Location to Archive
for (Archive archive: archives) { for (Archive archive: archives) {
for (Location l: archive.getClasses()) { for (Location l: archive.getClasses()) {
...@@ -81,190 +105,223 @@ public class Analyzer { ...@@ -81,190 +105,223 @@ public class Analyzer {
} }
} }
} }
// traverse and analyze all dependencies
for (Archive archive : archives) {
ArchiveDeps deps;
if (type == Type.CLASS || type == Type.VERBOSE) {
deps = new ClassVisitor(archive);
} else {
deps = new PackageVisitor(archive);
}
archive.visitDependences(deps);
results.put(archive, deps);
}
} }
public boolean hasDependences(Archive archive) { public boolean hasDependences(Archive archive) {
if (results.containsKey(archive)) { if (results.containsKey(archive)) {
return results.get(archive).deps.size() > 0; return results.get(archive).dependencies().size() > 0;
} }
return false; return false;
} }
public interface Visitor { public interface Visitor {
/**
* Visits the source archive to its destination archive of
* a recorded dependency.
*/
void visitArchiveDependence(Archive origin, Archive target, Profile profile);
/** /**
* Visits a recorded dependency from origin to target which can be * Visits a recorded dependency from origin to target which can be
* a fully-qualified classname, a package name, a profile or * a fully-qualified classname, a package name, a module or
* archive name depending on the Analyzer's type. * archive name depending on the Analyzer's type.
*/ */
void visitDependence(String origin, Archive source, String target, Archive archive, Profile profile); public void visitDependence(String origin, Archive originArchive,
String target, Archive targetArchive);
} }
public void visitArchiveDependences(Archive source, Visitor v) { /**
ArchiveDeps r = results.get(source); * Visit the dependencies of the given source.
for (ArchiveDeps.Dep d: r.requireArchives()) { * If the requested level is SUMMARY, it will visit the required archives list.
v.visitArchiveDependence(r.archive, d.archive, d.profile); */
public void visitDependences(Archive source, Visitor v, Type level) {
if (level == Type.SUMMARY) {
final ArchiveDeps result = results.get(source);
SortedMap<String, Archive> sorted = new TreeMap<>();
for (Archive a : result.requires()) {
sorted.put(a.getName(), a);
}
for (Archive archive : sorted.values()) {
Profile profile = result.getTargetProfile(archive);
v.visitDependence(source.getName(), source,
profile != null ? profile.profileName() : archive.getName(), archive);
}
} else {
ArchiveDeps result = results.get(source);
if (level != type) {
// requesting different level of analysis
result = new ArchiveDeps(source, level);
source.visitDependences(result);
}
SortedSet<Dep> sorted = new TreeSet<>(result.dependencies());
for (Dep d : sorted) {
v.visitDependence(d.origin(), d.originArchive(), d.target(), d.targetArchive());
}
} }
} }
public void visitDependences(Archive source, Visitor v) { public void visitDependences(Archive source, Visitor v) {
ArchiveDeps r = results.get(source); visitDependences(source, v, type);
for (Map.Entry<String, SortedSet<ArchiveDeps.Dep>> e: r.deps.entrySet()) {
String origin = e.getKey();
for (ArchiveDeps.Dep d: e.getValue()) {
// filter intra-dependency unless in verbose mode
if (type == Type.VERBOSE || d.archive != source) {
v.visitDependence(origin, source, d.target, d.archive, d.profile);
}
}
}
} }
/** /**
* ArchiveDeps contains the dependencies for an Archive that * ArchiveDeps contains the dependencies for an Archive that can have one or
* can have one or more classes. * more classes.
*/ */
private abstract class ArchiveDeps implements Archive.Visitor { class ArchiveDeps implements Archive.Visitor {
final Archive archive; protected final Archive archive;
final SortedMap<String, SortedSet<Dep>> deps; protected final Set<Archive> requires;
ArchiveDeps(Archive archive) { protected final Set<Dep> deps;
protected final Type level;
private Profile profile;
ArchiveDeps(Archive archive, Type level) {
this.archive = archive; this.archive = archive;
this.deps = new TreeMap<>(); this.deps = new HashSet<>();
this.requires = new HashSet<>();
this.level = level;
} }
void add(String origin, String target, Archive targetArchive, String pkgName) { Set<Dep> dependencies() {
SortedSet<Dep> set = deps.get(origin); return deps;
if (set == null) {
deps.put(origin, set = new TreeSet<>());
}
Profile p = targetArchive instanceof JDKArchive
? Profile.getProfile(pkgName) : null;
set.add(new Dep(target, targetArchive, p));
} }
/** Set<Archive> requires() {
* Returns the list of Archive dependences. The returned return requires;
* list contains one {@code Dep} instance per one archive }
* and with the minimum profile this archive depends on.
*/ Profile getTargetProfile(Archive target) {
List<Dep> requireArchives() { return JDKArchive.isProfileArchive(target) ? profile : null;
Map<Archive,Profile> map = new HashMap<>(); }
for (Set<Dep> set: deps.values()) {
for (Dep d: set) { Archive findArchive(Location t) {
if (this.archive != d.archive) { Archive target = archive.getClasses().contains(t) ? archive : map.get(t);
Profile p = map.get(d.archive); if (target == null) {
if (p == null || (d.profile != null && p.profile < d.profile.profile)) { map.put(t, target = NOT_FOUND);
map.put(d.archive, d.profile);
}
}
}
}
List<Dep> list = new ArrayList<>();
for (Map.Entry<Archive,Profile> e: map.entrySet()) {
list.add(new Dep("", e.getKey(), e.getValue()));
} }
return list; return target;
} }
/** // return classname or package name depedning on the level
* Dep represents a dependence where the target can be private String getLocationName(Location o) {
* a classname or packagename and the archive and profile if (level == Type.CLASS || level == Type.VERBOSE) {
* the target belongs to. return o.getClassName();
*/ } else {
class Dep implements Comparable<Dep> { String pkg = o.getPackageName();
final String target; return pkg.isEmpty() ? "<unnamed>" : pkg;
final Archive archive;
final Profile profile;
Dep(String target, Archive archive, Profile p) {
this.target = target;
this.archive = archive;
this.profile = p;
} }
}
@Override @Override
public boolean equals(Object o) { public void visit(Location o, Location t) {
if (o instanceof Dep) { Archive targetArchive = findArchive(t);
Dep d = (Dep)o; if (filter.accepts(o, archive, t, targetArchive)) {
return this.archive == d.archive && this.target.equals(d.target); addDep(o, t);
if (!requires.contains(targetArchive)) {
requires.add(targetArchive);
} }
return false;
} }
if (targetArchive instanceof JDKArchive) {
Profile p = Profile.getProfile(t.getPackageName());
if (profile == null || (p != null && p.compareTo(profile) > 0)) {
profile = p;
}
}
}
@Override private Dep curDep;
public int hashCode() { protected Dep addDep(Location o, Location t) {
int hash = 3; String origin = getLocationName(o);
hash = 17 * hash + Objects.hashCode(this.archive); String target = getLocationName(t);
hash = 17 * hash + Objects.hashCode(this.target); Archive targetArchive = findArchive(t);
return hash; if (curDep != null &&
curDep.origin().equals(origin) &&
curDep.originArchive() == archive &&
curDep.target().equals(target) &&
curDep.targetArchive() == targetArchive) {
return curDep;
} }
@Override Dep e = new Dep(origin, archive, target, targetArchive);
public int compareTo(Dep o) { if (deps.contains(e)) {
if (this.target.equals(o.target)) { for (Dep e1 : deps) {
if (this.archive == o.archive) { if (e.equals(e1)) {
return 0; curDep = e1;
} else {
return this.archive.getFileName().compareTo(o.archive.getFileName());
} }
} }
return this.target.compareTo(o.target); } else {
deps.add(e);
curDep = e;
} }
return curDep;
} }
public abstract void visit(Location o, Location t);
} }
private class ClassVisitor extends ArchiveDeps { /*
ClassVisitor(Archive archive) { * Class-level or package-level dependency
super(archive); */
class Dep implements Comparable<Dep> {
final String origin;
final Archive originArchive;
final String target;
final Archive targetArchive;
Dep(String origin, Archive originArchive, String target, Archive targetArchive) {
this.origin = origin;
this.originArchive = originArchive;
this.target = target;
this.targetArchive = targetArchive;
} }
@Override
public void visit(Location o, Location t) {
Archive targetArchive =
this.archive.getClasses().contains(t) ? this.archive : map.get(t);
if (targetArchive == null) {
map.put(t, targetArchive = NOT_FOUND);
}
String origin = o.getClassName(); String origin() {
String target = t.getClassName(); return origin;
add(origin, target, targetArchive, t.getPackageName()); }
Archive originArchive() {
return originArchive;
} }
}
private class PackageVisitor extends ArchiveDeps { String target() {
PackageVisitor(Archive archive) { return target;
super(archive);
} }
Archive targetArchive() {
return targetArchive;
}
@Override @Override
public void visit(Location o, Location t) { @SuppressWarnings("unchecked")
Archive targetArchive = public boolean equals(Object o) {
this.archive.getClasses().contains(t) ? this.archive : map.get(t); if (o instanceof Dep) {
if (targetArchive == null) { Dep d = (Dep) o;
map.put(t, targetArchive = NOT_FOUND); return this.origin.equals(d.origin) &&
this.originArchive == d.originArchive &&
this.target.equals(d.target) &&
this.targetArchive == d.targetArchive;
} }
return false;
}
String origin = packageOf(o); @Override
String target = packageOf(t); public int hashCode() {
add(origin, target, targetArchive, t.getPackageName()); int hash = 7;
hash = 67*hash + Objects.hashCode(this.origin)
+ Objects.hashCode(this.originArchive)
+ Objects.hashCode(this.target)
+ Objects.hashCode(this.targetArchive);
return hash;
} }
public String packageOf(Location o) {
String pkg = o.getPackageName(); @Override
return pkg.isEmpty() ? "<unnamed>" : pkg; public int compareTo(Dep o) {
if (this.origin.equals(o.origin)) {
if (this.target.equals(o.target)) {
if (this.originArchive == o.originArchive &&
this.targetArchive == o.targetArchive) {
return 0;
} else if (this.originArchive == o.originArchive) {
return this.targetArchive.getPathName().compareTo(o.targetArchive.getPathName());
} else {
return this.originArchive.getPathName().compareTo(o.originArchive.getPathName());
}
} else {
return this.target.compareTo(o.target);
}
}
return this.origin.compareTo(o.origin);
} }
} }
} }
/* /*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -25,28 +25,34 @@ ...@@ -25,28 +25,34 @@
package com.sun.tools.jdeps; package com.sun.tools.jdeps;
import com.sun.tools.classfile.Dependency.Location; import com.sun.tools.classfile.Dependency.Location;
import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* Represents the source of the class files. * Represents the source of the class files.
*/ */
public class Archive { public class Archive {
public static Archive getInstance(Path p) throws IOException {
return new Archive(p, ClassFileReader.newInstance(p));
}
private final Path path; private final Path path;
private final String filename; private final String filename;
private final ClassFileReader reader; private final ClassFileReader reader;
private final Map<Location, Set<Location>> deps = new HashMap<>(); protected Map<Location, Set<Location>> deps = new ConcurrentHashMap<>();
public Archive(String name) { protected Archive(String name) {
this.path = null; this.path = null;
this.filename = name; this.filename = name;
this.reader = null; this.reader = null;
} }
public Archive(Path p, ClassFileReader reader) { protected Archive(Path p, ClassFileReader reader) {
this.path = p; this.path = p;
this.filename = path.getFileName().toString(); this.filename = path.getFileName().toString();
this.reader = reader; this.reader = reader;
...@@ -56,7 +62,7 @@ public class Archive { ...@@ -56,7 +62,7 @@ public class Archive {
return reader; return reader;
} }
public String getFileName() { public String getName() {
return filename; return filename;
} }
...@@ -89,6 +95,10 @@ public class Archive { ...@@ -89,6 +95,10 @@ public class Archive {
} }
} }
public boolean isEmpty() {
return getClasses().isEmpty();
}
public String getPathName() { public String getPathName() {
return path != null ? path.toString() : filename; return path != null ? path.toString() : filename;
} }
......
/* /*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -68,7 +68,8 @@ public class ClassFileReader { ...@@ -68,7 +68,8 @@ public class ClassFileReader {
protected final Path path; protected final Path path;
protected final String baseFileName; protected final String baseFileName;
private ClassFileReader(Path path) { protected final List<String> skippedEntries = new ArrayList<>();
protected ClassFileReader(Path path) {
this.path = path; this.path = path;
this.baseFileName = path.getFileName() != null this.baseFileName = path.getFileName() != null
? path.getFileName().toString() ? path.getFileName().toString()
...@@ -79,6 +80,10 @@ public class ClassFileReader { ...@@ -79,6 +80,10 @@ public class ClassFileReader {
return baseFileName; return baseFileName;
} }
public List<String> skippedEntries() {
return skippedEntries;
}
/** /**
* Returns the ClassFile matching the given binary name * Returns the ClassFile matching the given binary name
* or a fully-qualified class name. * or a fully-qualified class name.
...@@ -232,11 +237,12 @@ public class ClassFileReader { ...@@ -232,11 +237,12 @@ public class ClassFileReader {
} }
} }
private static class JarFileReader extends ClassFileReader { static class JarFileReader extends ClassFileReader {
final JarFile jarfile; private final JarFile jarfile;
JarFileReader(Path path) throws IOException { JarFileReader(Path path) throws IOException {
this(path, new JarFile(path.toFile())); this(path, new JarFile(path.toFile(), false));
} }
JarFileReader(Path path, JarFile jf) throws IOException { JarFileReader(Path path, JarFile jf) throws IOException {
super(path); super(path);
this.jarfile = jf; this.jarfile = jf;
...@@ -252,18 +258,18 @@ public class ClassFileReader { ...@@ -252,18 +258,18 @@ public class ClassFileReader {
+ entryName.substring(i + 1, entryName.length())); + entryName.substring(i + 1, entryName.length()));
} }
if (e != null) { if (e != null) {
return readClassFile(e); return readClassFile(jarfile, e);
} }
} else { } else {
JarEntry e = jarfile.getJarEntry(name + ".class"); JarEntry e = jarfile.getJarEntry(name + ".class");
if (e != null) { if (e != null) {
return readClassFile(e); return readClassFile(jarfile, e);
} }
} }
return null; return null;
} }
private ClassFile readClassFile(JarEntry e) throws IOException { protected ClassFile readClassFile(JarFile jarfile, JarEntry e) throws IOException {
InputStream is = null; InputStream is = null;
try { try {
is = jarfile.getInputStream(e); is = jarfile.getInputStream(e);
...@@ -277,60 +283,76 @@ public class ClassFileReader { ...@@ -277,60 +283,76 @@ public class ClassFileReader {
} }
public Iterable<ClassFile> getClassFiles() throws IOException { public Iterable<ClassFile> getClassFiles() throws IOException {
final Iterator<ClassFile> iter = new JarFileIterator(); final Iterator<ClassFile> iter = new JarFileIterator(this, jarfile);
return new Iterable<ClassFile>() { return new Iterable<ClassFile>() {
public Iterator<ClassFile> iterator() { public Iterator<ClassFile> iterator() {
return iter; return iter;
} }
}; };
} }
}
class JarFileIterator implements Iterator<ClassFile> { class JarFileIterator implements Iterator<ClassFile> {
private Enumeration<JarEntry> entries; protected final JarFileReader reader;
private JarEntry nextEntry; protected Enumeration<JarEntry> entries;
JarFileIterator() { protected JarFile jf;
this.entries = jarfile.entries(); protected JarEntry nextEntry;
while (entries.hasMoreElements()) { protected ClassFile cf;
JarEntry e = entries.nextElement(); JarFileIterator(JarFileReader reader) {
String name = e.getName(); this(reader, null);
if (name.endsWith(".class")) { }
this.nextEntry = e; JarFileIterator(JarFileReader reader, JarFile jarfile) {
break; this.reader = reader;
} setJarFile(jarfile);
} }
}
public boolean hasNext() { void setJarFile(JarFile jarfile) {
return nextEntry != null; if (jarfile == null) return;
}
public ClassFile next() { this.jf = jarfile;
if (!hasNext()) { this.entries = jf.entries();
throw new NoSuchElementException(); this.nextEntry = nextEntry();
} }
ClassFile cf; public boolean hasNext() {
if (nextEntry != null && cf != null) {
return true;
}
while (nextEntry != null) {
try { try {
cf = readClassFile(nextEntry); cf = reader.readClassFile(jf, nextEntry);
} catch (IOException ex) { return true;
throw new ClassFileError(ex); } catch (ClassFileError | IOException ex) {
skippedEntries.add(nextEntry.getName());
} }
JarEntry entry = nextEntry; nextEntry = nextEntry();
nextEntry = null;
while (entries.hasMoreElements()) {
JarEntry e = entries.nextElement();
String name = e.getName();
if (name.endsWith(".class")) {
nextEntry = e;
break;
}
}
return cf;
} }
return false;
}
public void remove() { public ClassFile next() {
throw new UnsupportedOperationException("Not supported yet."); if (!hasNext()) {
throw new NoSuchElementException();
} }
ClassFile classFile = cf;
cf = null;
nextEntry = nextEntry();
return classFile;
}
protected JarEntry nextEntry() {
while (entries.hasMoreElements()) {
JarEntry e = entries.nextElement();
String name = e.getName();
if (name.endsWith(".class")) {
return e;
}
}
return null;
}
public void remove() {
throw new UnsupportedOperationException("Not supported yet.");
} }
} }
} }
/* /*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -63,4 +63,3 @@ public class Main { ...@@ -63,4 +63,3 @@ public class Main {
return t.run(args); return t.run(args);
} }
} }
/* /*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -24,6 +24,12 @@ ...@@ -24,6 +24,12 @@
*/ */
package com.sun.tools.jdeps; package com.sun.tools.jdeps;
import com.sun.tools.classfile.Annotation;
import com.sun.tools.classfile.ClassFile;
import com.sun.tools.classfile.ConstantPool;
import com.sun.tools.classfile.ConstantPoolException;
import com.sun.tools.classfile.RuntimeAnnotations_attribute;
import com.sun.tools.classfile.Dependencies.ClassFileError;
import java.io.IOException; import java.io.IOException;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Files; import java.nio.file.Files;
...@@ -33,11 +39,15 @@ import java.nio.file.SimpleFileVisitor; ...@@ -33,11 +39,15 @@ import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.*; import java.util.*;
import static com.sun.tools.classfile.Attribute.*;
/** /**
* ClassPath for Java SE and JDK * ClassPath for Java SE and JDK
*/ */
class PlatformClassPath { class PlatformClassPath {
private final static List<Archive> javaHomeArchives = init(); private static final List<String> NON_PLATFORM_JARFILES =
Arrays.asList("alt-rt.jar", "jfxrt.jar", "ant-javafx.jar", "javafx-mx.jar");
private static final List<Archive> javaHomeArchives = init();
static List<Archive> getArchives() { static List<Archive> getArchives() {
return javaHomeArchives; return javaHomeArchives;
...@@ -50,12 +60,19 @@ class PlatformClassPath { ...@@ -50,12 +60,19 @@ class PlatformClassPath {
if (home.endsWith("jre")) { if (home.endsWith("jre")) {
// jar files in <javahome>/jre/lib // jar files in <javahome>/jre/lib
result.addAll(addJarFiles(home.resolve("lib"))); result.addAll(addJarFiles(home.resolve("lib")));
if (home.getParent() != null) {
// add tools.jar and other JDK jar files
Path lib = home.getParent().resolve("lib");
if (Files.exists(lib)) {
result.addAll(addJarFiles(lib));
}
}
} else if (Files.exists(home.resolve("lib"))) { } else if (Files.exists(home.resolve("lib"))) {
// either a JRE or a jdk build image // either a JRE or a jdk build image
Path classes = home.resolve("classes"); Path classes = home.resolve("classes");
if (Files.isDirectory(classes)) { if (Files.isDirectory(classes)) {
// jdk build outputdir // jdk build outputdir
result.add(new JDKArchive(classes, ClassFileReader.newInstance(classes))); result.add(new JDKArchive(classes));
} }
// add other JAR files // add other JAR files
result.addAll(addJarFiles(home.resolve("lib"))); result.addAll(addJarFiles(home.resolve("lib")));
...@@ -91,9 +108,9 @@ class PlatformClassPath { ...@@ -91,9 +108,9 @@ class PlatformClassPath {
if (fn.endsWith(".jar")) { if (fn.endsWith(".jar")) {
// JDK may cobundle with JavaFX that doesn't belong to any profile // JDK may cobundle with JavaFX that doesn't belong to any profile
// Treat jfxrt.jar as regular Archive // Treat jfxrt.jar as regular Archive
result.add(fn.equals("jfxrt.jar") result.add(NON_PLATFORM_JARFILES.contains(fn)
? new Archive(p, ClassFileReader.newInstance(p)) ? Archive.getInstance(p)
: new JDKArchive(p, ClassFileReader.newInstance(p))); : new JDKArchive(p));
} }
return FileVisitResult.CONTINUE; return FileVisitResult.CONTINUE;
} }
...@@ -106,8 +123,91 @@ class PlatformClassPath { ...@@ -106,8 +123,91 @@ class PlatformClassPath {
* or implementation classes (i.e. JDK internal API) * or implementation classes (i.e. JDK internal API)
*/ */
static class JDKArchive extends Archive { static class JDKArchive extends Archive {
JDKArchive(Path p, ClassFileReader reader) { private static List<String> PROFILE_JARS = Arrays.asList("rt.jar", "jce.jar");
super(p, reader); public static boolean isProfileArchive(Archive archive) {
if (archive instanceof JDKArchive) {
return PROFILE_JARS.contains(archive.getName());
}
return false;
}
private final Map<String,Boolean> exportedPackages = new HashMap<>();
private final Map<String,Boolean> exportedTypes = new HashMap<>();
JDKArchive(Path p) throws IOException {
super(p, ClassFileReader.newInstance(p));
}
/**
* Tests if a given fully-qualified name is an exported type.
*/
public boolean isExported(String cn) {
int i = cn.lastIndexOf('.');
String pn = i > 0 ? cn.substring(0, i) : "";
boolean isJdkExported = isExportedPackage(pn);
if (exportedTypes.containsKey(cn)) {
return exportedTypes.get(cn);
}
return isJdkExported;
}
/**
* Tests if a given package name is exported.
*/
public boolean isExportedPackage(String pn) {
if (Profile.getProfile(pn) != null) {
return true;
}
return exportedPackages.containsKey(pn) ? exportedPackages.get(pn) : false;
}
private static final String JDK_EXPORTED_ANNOTATION = "Ljdk/Exported;";
private Boolean isJdkExported(ClassFile cf) throws ConstantPoolException {
RuntimeAnnotations_attribute attr = (RuntimeAnnotations_attribute)
cf.attributes.get(RuntimeVisibleAnnotations);
if (attr != null) {
for (int i = 0; i < attr.annotations.length; i++) {
Annotation ann = attr.annotations[i];
String annType = cf.constant_pool.getUTF8Value(ann.type_index);
if (JDK_EXPORTED_ANNOTATION.equals(annType)) {
boolean isJdkExported = true;
for (int j = 0; j < ann.num_element_value_pairs; j++) {
Annotation.element_value_pair pair = ann.element_value_pairs[j];
Annotation.Primitive_element_value ev = (Annotation.Primitive_element_value) pair.value;
ConstantPool.CONSTANT_Integer_info info = (ConstantPool.CONSTANT_Integer_info)
cf.constant_pool.get(ev.const_value_index);
isJdkExported = info.value != 0;
}
return Boolean.valueOf(isJdkExported);
}
}
}
return null;
}
void processJdkExported(ClassFile cf) throws IOException {
try {
String cn = cf.getName();
String pn = cn.substring(0, cn.lastIndexOf('/')).replace('/', '.');
Boolean b = isJdkExported(cf);
if (b != null) {
exportedTypes.put(cn.replace('/', '.'), b);
}
if (!exportedPackages.containsKey(pn)) {
// check if package-info.class has @jdk.Exported
Boolean isJdkExported = null;
ClassFile pcf = reader().getClassFile(cn.substring(0, cn.lastIndexOf('/')+1) + "package-info");
if (pcf != null) {
isJdkExported = isJdkExported(pcf);
}
if (isJdkExported != null) {
exportedPackages.put(pn, isJdkExported);
}
}
} catch (ConstantPoolException e) {
throw new ClassFileError(e);
}
} }
} }
} }
/* /*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -43,7 +43,6 @@ import java.util.jar.JarFile; ...@@ -43,7 +43,6 @@ import java.util.jar.JarFile;
* Build the profile information from ct.sym if exists. * Build the profile information from ct.sym if exists.
*/ */
enum Profile { enum Profile {
COMPACT1("compact1", 1), COMPACT1("compact1", 1),
COMPACT2("compact2", 2), COMPACT2("compact2", 2),
COMPACT3("compact3", 3), COMPACT3("compact3", 3),
...@@ -61,8 +60,7 @@ enum Profile { ...@@ -61,8 +60,7 @@ enum Profile {
this.proprietaryPkgs = new HashSet<>(); this.proprietaryPkgs = new HashSet<>();
} }
@Override public String profileName() {
public String toString() {
return name; return name;
} }
...@@ -77,7 +75,7 @@ enum Profile { ...@@ -77,7 +75,7 @@ enum Profile {
public static Profile getProfile(String pn) { public static Profile getProfile(String pn) {
Profile profile = PackageToProfile.map.get(pn); Profile profile = PackageToProfile.map.get(pn);
return (profile != null && profile.packages.contains(pn)) return (profile != null && profile.packages.contains(pn))
? profile : null; ? profile : null;
} }
static class PackageToProfile { static class PackageToProfile {
......
main.usage.summary=\ main.usage.summary=\
Usage: {0} <options> <classes...>\n\ Usage: {0} <options> <classes...>\n\
use -h, -? or --help for a list of possible options use -h, -? or -help for a list of possible options
main.usage=\ main.usage=\
Usage: {0} <options> <classes...>\n\ Usage: {0} <options> <classes...>\n\
...@@ -18,20 +18,29 @@ main.opt.version=\ ...@@ -18,20 +18,29 @@ main.opt.version=\
main.opt.v=\ main.opt.v=\
\ -v -verbose Print all class level dependencies\n\ \ -v -verbose Print all class level dependencies\n\
\ Equivalent to -verbose:class -filter:none.\n\
\ -verbose:package Print package-level dependencies excluding\n\ \ -verbose:package Print package-level dependencies excluding\n\
\ dependencies within the same archive\n\ \ dependencies within the same package by default\n\
\ -verbose:class Print class-level dependencies excluding\n\ \ -verbose:class Print class-level dependencies excluding\n\
\ dependencies within the same archive \ dependencies within the same package by default
main.opt.f=\
\ -f <regex> -filter <regex> Filter dependences matching the given pattern\n\
\ If given multiple times, the last one will be used.\n\
\ -filter:package Filter dependences within the same package (default)\n\
\ -filter:archive Filter dependences within the same archive\n\
\ -filter:none No -filter:package and -filter:archive filtering\n\
\ Filtering specified via the -filter option still applies.
main.opt.s=\ main.opt.s=\
\ -s -summary Print dependency summary only \ -s -summary Print dependency summary only
main.opt.p=\ main.opt.p=\
\ -p <pkgname> -package <pkgname> Finds dependences in the given package\n\ \ -p <pkgname> -package <pkgname> Finds dependences matching the given package name\n\
\ (may be given multiple times) \ (may be given multiple times)
main.opt.e=\ main.opt.e=\
\ -e <regex> -regex <regex> Finds dependences in packages matching pattern\n\ \ -e <regex> -regex <regex> Finds dependences matching the given pattern\n\
\ (-p and -e are exclusive) \ (-p and -e are exclusive)
main.opt.include=\ main.opt.include=\
...@@ -47,7 +56,10 @@ main.opt.cp=\ ...@@ -47,7 +56,10 @@ main.opt.cp=\
\ -cp <path> -classpath <path> Specify where to find class files \ -cp <path> -classpath <path> Specify where to find class files
main.opt.R=\ main.opt.R=\
\ -R -recursive Recursively traverse all dependencies \ -R -recursive Recursively traverse all dependencies.\n\
\ The -R option implies -filter:none. If -p, -e, -f\n\
\ option is specified, only the matching dependences\n\
\ are analyzed.
main.opt.apionly=\ main.opt.apionly=\
\ -apionly Restrict analysis to APIs i.e. dependences\n\ \ -apionly Restrict analysis to APIs i.e. dependences\n\
...@@ -74,12 +86,11 @@ main.opt.depth=\ ...@@ -74,12 +86,11 @@ main.opt.depth=\
err.unknown.option=unknown option: {0} err.unknown.option=unknown option: {0}
err.missing.arg=no value given for {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.invalid.arg.for.option=invalid argument for option: {0}
err.option.after.class=option must be specified before classes: {0} err.option.after.class=option must be specified before classes: {0}
err.option.unsupported={0} not supported: {1} err.option.unsupported={0} not supported: {1}
err.profiles.msg=No profile information err.profiles.msg=No profile information
err.dot.output.path=invalid path: {0} err.invalid.path=invalid path: {0}
warn.invalid.arg=Invalid classname or pathname not exist: {0} warn.invalid.arg=Invalid classname or pathname not exist: {0}
warn.split.package=package {0} defined in {1} {2} warn.split.package=package {0} defined in {1} {2}
......
/* /*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 8015912 8029216 * @bug 8015912 8029216 8048063
* @summary Test -apionly and -jdkinternals options * @summary Test -apionly and -jdkinternals options
* @build m.Bar m.Foo m.Gee b.B c.C c.I d.D e.E f.F g.G * @build m.Bar m.Foo m.Gee b.B c.C c.I d.D e.E f.F g.G
* @run main APIDeps * @run main APIDeps
...@@ -80,28 +80,40 @@ public class APIDeps { ...@@ -80,28 +80,40 @@ public class APIDeps {
"b.B", "c.C", "d.D", "f.F", "g.G"}, "b.B", "c.C", "d.D", "f.F", "g.G"},
new String[] {"compact1", "compact3", testDirBasename}, new String[] {"compact1", "compact3", testDirBasename},
new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"}); new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"});
test(new File(mDir, "Foo.class"),
new String[] {"c.I", "e.E", "f.F"},
new String[] {testDirBasename},
new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-P"});
test(new File(mDir, "Foo.class"), test(new File(mDir, "Foo.class"),
new String[] {"c.I", "e.E", "f.F", "m.Bar"}, new String[] {"c.I", "e.E", "f.F", "m.Bar"},
new String[] {testDirBasename}, new String[] {testDirBasename},
new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"}); new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-filter:none", "-P"});
test(new File(mDir, "Gee.class"), test(new File(mDir, "Gee.class"),
new String[] {"g.G", "sun.misc.Lock"}, new String[] {"g.G", "sun.misc.Lock", "com.sun.tools.classfile.ClassFile",
new String[] {testDirBasename, "JDK internal API"}, "com.sun.management.ThreadMXBean", "com.sun.source.tree.BinaryTree"},
new String[] {"-classpath", testDir.getPath(), "-verbose"}); new String[] {testDirBasename, "JDK internal API", "compact3", ""},
new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"});
// -jdkinternals // -jdkinternals
test(new File(mDir, "Gee.class"), test(new File(mDir, "Gee.class"),
new String[] {"sun.misc.Lock"}, new String[] {"sun.misc.Lock", "com.sun.tools.classfile.ClassFile"},
new String[] {"JDK internal API"}, new String[] {"JDK internal API"},
new String[] {"-jdkinternals"}); new String[] {"-jdkinternals"});
// -jdkinternals parses all classes on -classpath and the input arguments // -jdkinternals parses all classes on -classpath and the input arguments
test(new File(mDir, "Gee.class"), test(new File(mDir, "Gee.class"),
new String[] {"sun.misc.Lock", "sun.misc.Unsafe"}, new String[] {"com.sun.tools.jdeps.Main", "com.sun.tools.classfile.ClassFile",
"sun.misc.Lock", "sun.misc.Unsafe"},
new String[] {"JDK internal API"}, new String[] {"JDK internal API"},
new String[] {"-classpath", testDir.getPath(), "-jdkinternals"}); new String[] {"-classpath", testDir.getPath(), "-jdkinternals"});
// parse only APIs // parse only APIs
// parse only APIs test(mDir,
new String[] {"java.lang.Object", "java.lang.String",
"java.util.Set",
"c.C", "d.D", "c.I", "e.E"},
new String[] {"compact1", testDirBasename},
new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-P", "-apionly"});
test(mDir, test(mDir,
new String[] {"java.lang.Object", "java.lang.String", new String[] {"java.lang.Object", "java.lang.String",
"java.util.Set", "java.util.Set",
......
/* /*
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
/* /*
* @test * @test
* @bug 8003562 8005428 8015912 8027481 * @bug 8003562 8005428 8015912 8027481 8048063
* @summary Basic tests for jdeps tool * @summary Basic tests for jdeps tool
* @build Test p.Foo p.Bar javax.activity.NotCompactProfile * @build Test p.Foo p.Bar javax.activity.NotCompactProfile
* @run main Basic * @run main Basic
...@@ -86,6 +86,16 @@ public class Basic { ...@@ -86,6 +86,16 @@ public class Basic {
new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"}, new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
new String[] {"compact1", "compact1", "not found", "not found"}, new String[] {"compact1", "compact1", "not found", "not found"},
new String[] {"-verbose:class"}); new String[] {"-verbose:class"});
// test -filter:none option
test(new File(testDir, "p"),
new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto", "p"},
new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1", "p"},
new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:none"});
// test -filter:archive option
test(new File(testDir, "p"),
new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"},
new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"},
new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:archive"});
// test -p option // test -p option
test(new File(testDir, "Test.class"), test(new File(testDir, "Test.class"),
new String[] {"p.Foo", "p.Bar"}, new String[] {"p.Foo", "p.Bar"},
...@@ -100,11 +110,12 @@ public class Basic { ...@@ -100,11 +110,12 @@ public class Basic {
new String[] {"java.lang"}, new String[] {"java.lang"},
new String[] {"compact1"}, new String[] {"compact1"},
new String[] {"-verbose:package", "-e", "java\\.lang\\..*"}); new String[] {"-verbose:package", "-e", "java\\.lang\\..*"});
// test -classpath and -include options // test -classpath and -include options
test(null, test(null,
new String[] {"java.lang", "java.util", new String[] {"java.lang", "java.util", "java.lang.management",
"java.lang.management", "javax.crypto"}, "javax.activity", "javax.crypto"},
new String[] {"compact1", "compact1", "compact3", "compact1"}, new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"},
new String[] {"-classpath", testDir.getPath(), "-include", "p.+|Test.class"}); new String[] {"-classpath", testDir.getPath(), "-include", "p.+|Test.class"});
test(new File(testDir, "Test.class"), test(new File(testDir, "Test.class"),
new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"}, new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
......
/*
* Copyright (c) 2014, 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 -dotoutput option
* @build Test p.Foo p.Bar javax.activity.NotCompactProfile
* @run main DotFileTest
*/
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.*;
public class DotFileTest {
private static boolean symbolFileExist = initProfiles();
private static boolean initProfiles() {
// check if ct.sym exists; if not use the profiles.properties file
Path home = Paths.get(System.getProperty("java.home"));
if (home.endsWith("jre")) {
home = home.getParent();
}
Path ctsym = home.resolve("lib").resolve("ct.sym");
boolean symbolExists = ctsym.toFile().exists();
if (!symbolExists) {
Path testSrcProfiles =
Paths.get(System.getProperty("test.src", "."), "profiles.properties");
if (!testSrcProfiles.toFile().exists())
throw new Error(testSrcProfiles + " does not exist");
System.out.format("%s doesn't exist.%nUse %s to initialize profiles info%n",
ctsym, testSrcProfiles);
System.setProperty("jdeps.profiles", testSrcProfiles.toString());
}
return symbolExists;
}
public static void main(String... args) throws Exception {
int errors = 0;
errors += new DotFileTest().run();
if (errors > 0)
throw new Exception(errors + " errors found");
}
final Path dir;
final Path dotoutput;
DotFileTest() {
this.dir = Paths.get(System.getProperty("test.classes", "."));
this.dotoutput = dir.resolve("dots");
}
int run() throws IOException {
File testDir = dir.toFile();
// test a .class file
test(new File(testDir, "Test.class"),
new String[] {"java.lang", "p"},
new String[] {"compact1", "not found"});
// test a directory
// also test non-SE javax.activity class dependency
test(new File(testDir, "p"),
new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"},
new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"},
new String[] {"-classpath", testDir.getPath()});
// test class-level dependency output
test(new File(testDir, "Test.class"),
new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
new String[] {"compact1", "compact1", "not found", "not found"},
new String[] {"-verbose:class"});
// test -filter:none option
test(new File(testDir, "p"),
new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto", "p"},
new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1", "p"},
new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:none"});
// test -filter:archive option
test(new File(testDir, "p"),
new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"},
new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"},
new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:archive"});
// test -p option
test(new File(testDir, "Test.class"),
new String[] {"p.Foo", "p.Bar"},
new String[] {"not found", "not found"},
new String[] {"-verbose:class", "-p", "p"});
// test -e option
test(new File(testDir, "Test.class"),
new String[] {"p.Foo", "p.Bar"},
new String[] {"not found", "not found"},
new String[] {"-verbose:class", "-e", "p\\..*"});
test(new File(testDir, "Test.class"),
new String[] {"java.lang"},
new String[] {"compact1"},
new String[] {"-verbose:package", "-e", "java\\.lang\\..*"});
// test -classpath options
test(new File(testDir, "Test.class"),
new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
new String[] {"compact1", "compact1", testDir.getName(), testDir.getName()},
new String[] {"-v", "-classpath", testDir.getPath()});
testSummary(new File(testDir, "Test.class"),
new String[] {"rt.jar", testDir.getName()},
new String[] {"compact1", ""},
new String[] {"-classpath", testDir.getPath()});
testSummary(new File(testDir, "Test.class"),
new String[] {"java.lang", "p"},
new String[] {"compact1", testDir.getName()},
new String[] {"-v", "-classpath", testDir.getPath()});
return errors;
}
void test(File file, String[] expect, String[] profiles) throws IOException {
test(file, expect, profiles, new String[0]);
}
void test(File file, String[] expect, String[] profiles, String[] options)
throws IOException
{
Path dotfile = dotoutput.resolve(file.toPath().getFileName().toString() + ".dot");
List<String> args = new ArrayList<>(Arrays.asList(options));
args.add("-dotoutput");
args.add(dotoutput.toString());
if (file != null) {
args.add(file.getPath());
}
Map<String,String> result = jdeps(args, dotfile);
checkResult("dependencies", expect, result.keySet());
// with -P option
List<String> argsWithDashP = new ArrayList<>();
argsWithDashP.add("-dotoutput");
argsWithDashP.add(dotoutput.toString());
argsWithDashP.add("-P");
argsWithDashP.addAll(args);
result = jdeps(argsWithDashP, dotfile);
checkResult("profiles", expect, profiles, result);
}
void testSummary(File file, String[] expect, String[] profiles, String[] options)
throws IOException
{
Path dotfile = dotoutput.resolve("summary.dot");
List<String> args = new ArrayList<>(Arrays.asList(options));
args.add("-dotoutput");
args.add(dotoutput.toString());
if (file != null) {
args.add(file.getPath());
}
Map<String,String> result = jdeps(args, dotfile);
checkResult("dependencies", expect, result.keySet());
// with -P option
List<String> argsWithDashP = new ArrayList<>();
argsWithDashP.add("-dotoutput");
argsWithDashP.add(dotoutput.toString());
argsWithDashP.add("-P");
argsWithDashP.addAll(args);
result = jdeps(argsWithDashP, dotfile);
checkResult("profiles", expect, profiles, result);
}
Map<String,String> jdeps(List<String> args, Path dotfile) throws IOException {
if (Files.exists(dotoutput)) {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dotoutput)) {
for (Path p : stream) {
Files.delete(p);
}
}
Files.delete(dotoutput);
}
// invoke jdeps
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
System.err.println("jdeps " + args);
int rc = com.sun.tools.jdeps.Main.run(args.toArray(new String[0]), pw);
pw.close();
String out = sw.toString();
if (!out.isEmpty())
System.err.println(out);
if (rc != 0)
throw new Error("jdeps failed: rc=" + rc);
// check output files
if (Files.notExists(dotfile)) {
throw new RuntimeException(dotfile + " doesn't exist");
}
return parse(dotfile);
}
private static Pattern pattern = Pattern.compile("(.*) -> +([^ ]*) (.*)");
private Map<String,String> parse(Path outfile) throws IOException {
Map<String,String> result = new LinkedHashMap<>();
for (String line : Files.readAllLines(outfile)) {
line = line.replace('"', ' ').replace(';', ' ');
Matcher pm = pattern.matcher(line);
if (pm.find()) {
String origin = pm.group(1).trim();
String target = pm.group(2).trim();
String module = pm.group(3).replace('(', ' ').replace(')', ' ').trim();
result.put(target, module);
}
}
return result;
}
void checkResult(String label, String[] expect, Collection<String> found) {
List<String> list = Arrays.asList(expect);
if (!isEqual(list, found))
error("Unexpected " + label + " found: '" + found + "', expected: '" + list + "'");
}
void checkResult(String label, String[] expect, String[] profiles, Map<String,String> result) {
if (expect.length != profiles.length)
error("Invalid expected names and profiles");
// check the dependencies
checkResult(label, expect, result.keySet());
// check profile information
checkResult(label, profiles, result.values());
for (int i=0; i < expect.length; i++) {
String profile = result.get(expect[i]);
if (!profile.equals(profiles[i]))
error("Unexpected profile: '" + profile + "', expected: '" + profiles[i] + "'");
}
}
boolean isEqual(List<String> expected, Collection<String> found) {
if (expected.size() != found.size())
return false;
List<String> list = new ArrayList<>(found);
list.removeAll(expected);
return list.isEmpty();
}
void error(String msg) {
System.err.println("Error: " + msg);
errors++;
}
int errors;
}
/* /*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -26,5 +26,7 @@ package m; ...@@ -26,5 +26,7 @@ package m;
class Gee extends g.G { class Gee extends g.G {
public sun.misc.Lock lock; public sun.misc.Lock lock;
public com.sun.tools.classfile.ClassFile cf; // @jdk.Exported(false)
public com.sun.source.tree.BinaryTree tree; // @jdk.Exported
public com.sun.management.ThreadMXBean mxbean; // @jdk.Exported on package-info
} }
/* /*
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -30,4 +30,8 @@ public class Bar extends javax.activity.NotCompactProfile { ...@@ -30,4 +30,8 @@ public class Bar extends javax.activity.NotCompactProfile {
public javax.crypto.Cipher getCiper() { public javax.crypto.Cipher getCiper() {
return null; return null;
} }
public Foo foo() {
return new Foo();
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册