Thu, 17 Jul 2014 15:23:08 -0700
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
1.1 --- a/src/share/classes/com/sun/tools/jdeps/Analyzer.java Mon Jul 07 18:03:08 2014 -0700 1.2 +++ b/src/share/classes/com/sun/tools/jdeps/Analyzer.java Thu Jul 17 15:23:08 2014 -0700 1.3 @@ -1,5 +1,5 @@ 1.4 /* 1.5 - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 1.6 + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 1.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.8 * 1.9 * This code is free software; you can redistribute it and/or modify it 1.10 @@ -24,10 +24,8 @@ 1.11 */ 1.12 package com.sun.tools.jdeps; 1.13 1.14 -import com.sun.tools.classfile.Dependency.Location; 1.15 -import com.sun.tools.jdeps.PlatformClassPath.JDKArchive; 1.16 -import java.util.ArrayList; 1.17 import java.util.HashMap; 1.18 +import java.util.HashSet; 1.19 import java.util.List; 1.20 import java.util.Map; 1.21 import java.util.Objects; 1.22 @@ -37,6 +35,9 @@ 1.23 import java.util.TreeMap; 1.24 import java.util.TreeSet; 1.25 1.26 +import com.sun.tools.classfile.Dependency.Location; 1.27 +import com.sun.tools.jdeps.PlatformClassPath.JDKArchive; 1.28 + 1.29 /** 1.30 * Dependency Analyzer. 1.31 */ 1.32 @@ -52,7 +53,16 @@ 1.33 VERBOSE 1.34 }; 1.35 1.36 + /** 1.37 + * Filter to be applied when analyzing the dependencies from the given archives. 1.38 + * Only the accepted dependencies are recorded. 1.39 + */ 1.40 + interface Filter { 1.41 + boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive); 1.42 + } 1.43 + 1.44 private final Type type; 1.45 + private final Filter filter; 1.46 private final Map<Archive, ArchiveDeps> results = new HashMap<>(); 1.47 private final Map<Location, Archive> map = new HashMap<>(); 1.48 private final Archive NOT_FOUND 1.49 @@ -62,9 +72,11 @@ 1.50 * Constructs an Analyzer instance. 1.51 * 1.52 * @param type Type of the dependency analysis 1.53 + * @param filter 1.54 */ 1.55 - public Analyzer(Type type) { 1.56 + public Analyzer(Type type, Filter filter) { 1.57 this.type = type; 1.58 + this.filter = filter; 1.59 } 1.60 1.61 /** 1.62 @@ -72,6 +84,18 @@ 1.63 */ 1.64 public void run(List<Archive> archives) { 1.65 // build a map from Location to Archive 1.66 + buildLocationArchiveMap(archives); 1.67 + 1.68 + // traverse and analyze all dependencies 1.69 + for (Archive archive : archives) { 1.70 + ArchiveDeps deps = new ArchiveDeps(archive, type); 1.71 + archive.visitDependences(deps); 1.72 + results.put(archive, deps); 1.73 + } 1.74 + } 1.75 + 1.76 + private void buildLocationArchiveMap(List<Archive> archives) { 1.77 + // build a map from Location to Archive 1.78 for (Archive archive: archives) { 1.79 for (Location l: archive.getClasses()) { 1.80 if (!map.containsKey(l)) { 1.81 @@ -81,190 +105,223 @@ 1.82 } 1.83 } 1.84 } 1.85 - // traverse and analyze all dependencies 1.86 - for (Archive archive : archives) { 1.87 - ArchiveDeps deps; 1.88 - if (type == Type.CLASS || type == Type.VERBOSE) { 1.89 - deps = new ClassVisitor(archive); 1.90 - } else { 1.91 - deps = new PackageVisitor(archive); 1.92 - } 1.93 - archive.visitDependences(deps); 1.94 - results.put(archive, deps); 1.95 - } 1.96 } 1.97 1.98 public boolean hasDependences(Archive archive) { 1.99 if (results.containsKey(archive)) { 1.100 - return results.get(archive).deps.size() > 0; 1.101 + return results.get(archive).dependencies().size() > 0; 1.102 } 1.103 return false; 1.104 } 1.105 1.106 public interface Visitor { 1.107 /** 1.108 - * Visits the source archive to its destination archive of 1.109 - * a recorded dependency. 1.110 - */ 1.111 - void visitArchiveDependence(Archive origin, Archive target, Profile profile); 1.112 - /** 1.113 * Visits a recorded dependency from origin to target which can be 1.114 - * a fully-qualified classname, a package name, a profile or 1.115 + * a fully-qualified classname, a package name, a module or 1.116 * archive name depending on the Analyzer's type. 1.117 */ 1.118 - void visitDependence(String origin, Archive source, String target, Archive archive, Profile profile); 1.119 + public void visitDependence(String origin, Archive originArchive, 1.120 + String target, Archive targetArchive); 1.121 } 1.122 1.123 - public void visitArchiveDependences(Archive source, Visitor v) { 1.124 - ArchiveDeps r = results.get(source); 1.125 - for (ArchiveDeps.Dep d: r.requireArchives()) { 1.126 - v.visitArchiveDependence(r.archive, d.archive, d.profile); 1.127 + /** 1.128 + * Visit the dependencies of the given source. 1.129 + * If the requested level is SUMMARY, it will visit the required archives list. 1.130 + */ 1.131 + public void visitDependences(Archive source, Visitor v, Type level) { 1.132 + if (level == Type.SUMMARY) { 1.133 + final ArchiveDeps result = results.get(source); 1.134 + SortedMap<String, Archive> sorted = new TreeMap<>(); 1.135 + for (Archive a : result.requires()) { 1.136 + sorted.put(a.getName(), a); 1.137 + } 1.138 + for (Archive archive : sorted.values()) { 1.139 + Profile profile = result.getTargetProfile(archive); 1.140 + v.visitDependence(source.getName(), source, 1.141 + profile != null ? profile.profileName() : archive.getName(), archive); 1.142 + } 1.143 + } else { 1.144 + ArchiveDeps result = results.get(source); 1.145 + if (level != type) { 1.146 + // requesting different level of analysis 1.147 + result = new ArchiveDeps(source, level); 1.148 + source.visitDependences(result); 1.149 + } 1.150 + SortedSet<Dep> sorted = new TreeSet<>(result.dependencies()); 1.151 + for (Dep d : sorted) { 1.152 + v.visitDependence(d.origin(), d.originArchive(), d.target(), d.targetArchive()); 1.153 + } 1.154 } 1.155 } 1.156 1.157 public void visitDependences(Archive source, Visitor v) { 1.158 - ArchiveDeps r = results.get(source); 1.159 - for (Map.Entry<String, SortedSet<ArchiveDeps.Dep>> e: r.deps.entrySet()) { 1.160 - String origin = e.getKey(); 1.161 - for (ArchiveDeps.Dep d: e.getValue()) { 1.162 - // filter intra-dependency unless in verbose mode 1.163 - if (type == Type.VERBOSE || d.archive != source) { 1.164 - v.visitDependence(origin, source, d.target, d.archive, d.profile); 1.165 + visitDependences(source, v, type); 1.166 + } 1.167 + 1.168 + /** 1.169 + * ArchiveDeps contains the dependencies for an Archive that can have one or 1.170 + * more classes. 1.171 + */ 1.172 + class ArchiveDeps implements Archive.Visitor { 1.173 + protected final Archive archive; 1.174 + protected final Set<Archive> requires; 1.175 + protected final Set<Dep> deps; 1.176 + protected final Type level; 1.177 + private Profile profile; 1.178 + ArchiveDeps(Archive archive, Type level) { 1.179 + this.archive = archive; 1.180 + this.deps = new HashSet<>(); 1.181 + this.requires = new HashSet<>(); 1.182 + this.level = level; 1.183 + } 1.184 + 1.185 + Set<Dep> dependencies() { 1.186 + return deps; 1.187 + } 1.188 + 1.189 + Set<Archive> requires() { 1.190 + return requires; 1.191 + } 1.192 + 1.193 + Profile getTargetProfile(Archive target) { 1.194 + return JDKArchive.isProfileArchive(target) ? profile : null; 1.195 + } 1.196 + 1.197 + Archive findArchive(Location t) { 1.198 + Archive target = archive.getClasses().contains(t) ? archive : map.get(t); 1.199 + if (target == null) { 1.200 + map.put(t, target = NOT_FOUND); 1.201 + } 1.202 + return target; 1.203 + } 1.204 + 1.205 + // return classname or package name depedning on the level 1.206 + private String getLocationName(Location o) { 1.207 + if (level == Type.CLASS || level == Type.VERBOSE) { 1.208 + return o.getClassName(); 1.209 + } else { 1.210 + String pkg = o.getPackageName(); 1.211 + return pkg.isEmpty() ? "<unnamed>" : pkg; 1.212 + } 1.213 + } 1.214 + 1.215 + @Override 1.216 + public void visit(Location o, Location t) { 1.217 + Archive targetArchive = findArchive(t); 1.218 + if (filter.accepts(o, archive, t, targetArchive)) { 1.219 + addDep(o, t); 1.220 + if (!requires.contains(targetArchive)) { 1.221 + requires.add(targetArchive); 1.222 + } 1.223 + } 1.224 + if (targetArchive instanceof JDKArchive) { 1.225 + Profile p = Profile.getProfile(t.getPackageName()); 1.226 + if (profile == null || (p != null && p.compareTo(profile) > 0)) { 1.227 + profile = p; 1.228 } 1.229 } 1.230 } 1.231 - } 1.232 1.233 - /** 1.234 - * ArchiveDeps contains the dependencies for an Archive that 1.235 - * can have one or more classes. 1.236 - */ 1.237 - private abstract class ArchiveDeps implements Archive.Visitor { 1.238 - final Archive archive; 1.239 - final SortedMap<String, SortedSet<Dep>> deps; 1.240 - ArchiveDeps(Archive archive) { 1.241 - this.archive = archive; 1.242 - this.deps = new TreeMap<>(); 1.243 - } 1.244 + private Dep curDep; 1.245 + protected Dep addDep(Location o, Location t) { 1.246 + String origin = getLocationName(o); 1.247 + String target = getLocationName(t); 1.248 + Archive targetArchive = findArchive(t); 1.249 + if (curDep != null && 1.250 + curDep.origin().equals(origin) && 1.251 + curDep.originArchive() == archive && 1.252 + curDep.target().equals(target) && 1.253 + curDep.targetArchive() == targetArchive) { 1.254 + return curDep; 1.255 + } 1.256 1.257 - void add(String origin, String target, Archive targetArchive, String pkgName) { 1.258 - SortedSet<Dep> set = deps.get(origin); 1.259 - if (set == null) { 1.260 - deps.put(origin, set = new TreeSet<>()); 1.261 - } 1.262 - Profile p = targetArchive instanceof JDKArchive 1.263 - ? Profile.getProfile(pkgName) : null; 1.264 - set.add(new Dep(target, targetArchive, p)); 1.265 - } 1.266 - 1.267 - /** 1.268 - * Returns the list of Archive dependences. The returned 1.269 - * list contains one {@code Dep} instance per one archive 1.270 - * and with the minimum profile this archive depends on. 1.271 - */ 1.272 - List<Dep> requireArchives() { 1.273 - Map<Archive,Profile> map = new HashMap<>(); 1.274 - for (Set<Dep> set: deps.values()) { 1.275 - for (Dep d: set) { 1.276 - if (this.archive != d.archive) { 1.277 - Profile p = map.get(d.archive); 1.278 - if (p == null || (d.profile != null && p.profile < d.profile.profile)) { 1.279 - map.put(d.archive, d.profile); 1.280 - } 1.281 + Dep e = new Dep(origin, archive, target, targetArchive); 1.282 + if (deps.contains(e)) { 1.283 + for (Dep e1 : deps) { 1.284 + if (e.equals(e1)) { 1.285 + curDep = e1; 1.286 } 1.287 } 1.288 + } else { 1.289 + deps.add(e); 1.290 + curDep = e; 1.291 } 1.292 - List<Dep> list = new ArrayList<>(); 1.293 - for (Map.Entry<Archive,Profile> e: map.entrySet()) { 1.294 - list.add(new Dep("", e.getKey(), e.getValue())); 1.295 - } 1.296 - return list; 1.297 - } 1.298 - 1.299 - /** 1.300 - * Dep represents a dependence where the target can be 1.301 - * a classname or packagename and the archive and profile 1.302 - * the target belongs to. 1.303 - */ 1.304 - class Dep implements Comparable<Dep> { 1.305 - final String target; 1.306 - final Archive archive; 1.307 - final Profile profile; 1.308 - Dep(String target, Archive archive, Profile p) { 1.309 - this.target = target; 1.310 - this.archive = archive; 1.311 - this.profile = p; 1.312 - } 1.313 - 1.314 - @Override 1.315 - public boolean equals(Object o) { 1.316 - if (o instanceof Dep) { 1.317 - Dep d = (Dep)o; 1.318 - return this.archive == d.archive && this.target.equals(d.target); 1.319 - } 1.320 - return false; 1.321 - } 1.322 - 1.323 - @Override 1.324 - public int hashCode() { 1.325 - int hash = 3; 1.326 - hash = 17 * hash + Objects.hashCode(this.archive); 1.327 - hash = 17 * hash + Objects.hashCode(this.target); 1.328 - return hash; 1.329 - } 1.330 - 1.331 - @Override 1.332 - public int compareTo(Dep o) { 1.333 - if (this.target.equals(o.target)) { 1.334 - if (this.archive == o.archive) { 1.335 - return 0; 1.336 - } else { 1.337 - return this.archive.getFileName().compareTo(o.archive.getFileName()); 1.338 - } 1.339 - } 1.340 - return this.target.compareTo(o.target); 1.341 - } 1.342 - } 1.343 - public abstract void visit(Location o, Location t); 1.344 - } 1.345 - 1.346 - private class ClassVisitor extends ArchiveDeps { 1.347 - ClassVisitor(Archive archive) { 1.348 - super(archive); 1.349 - } 1.350 - @Override 1.351 - public void visit(Location o, Location t) { 1.352 - Archive targetArchive = 1.353 - this.archive.getClasses().contains(t) ? this.archive : map.get(t); 1.354 - if (targetArchive == null) { 1.355 - map.put(t, targetArchive = NOT_FOUND); 1.356 - } 1.357 - 1.358 - String origin = o.getClassName(); 1.359 - String target = t.getClassName(); 1.360 - add(origin, target, targetArchive, t.getPackageName()); 1.361 + return curDep; 1.362 } 1.363 } 1.364 1.365 - private class PackageVisitor extends ArchiveDeps { 1.366 - PackageVisitor(Archive archive) { 1.367 - super(archive); 1.368 + /* 1.369 + * Class-level or package-level dependency 1.370 + */ 1.371 + class Dep implements Comparable<Dep> { 1.372 + final String origin; 1.373 + final Archive originArchive; 1.374 + final String target; 1.375 + final Archive targetArchive; 1.376 + 1.377 + Dep(String origin, Archive originArchive, String target, Archive targetArchive) { 1.378 + this.origin = origin; 1.379 + this.originArchive = originArchive; 1.380 + this.target = target; 1.381 + this.targetArchive = targetArchive; 1.382 } 1.383 + 1.384 + String origin() { 1.385 + return origin; 1.386 + } 1.387 + 1.388 + Archive originArchive() { 1.389 + return originArchive; 1.390 + } 1.391 + 1.392 + String target() { 1.393 + return target; 1.394 + } 1.395 + 1.396 + Archive targetArchive() { 1.397 + return targetArchive; 1.398 + } 1.399 + 1.400 @Override 1.401 - public void visit(Location o, Location t) { 1.402 - Archive targetArchive = 1.403 - this.archive.getClasses().contains(t) ? this.archive : map.get(t); 1.404 - if (targetArchive == null) { 1.405 - map.put(t, targetArchive = NOT_FOUND); 1.406 + @SuppressWarnings("unchecked") 1.407 + public boolean equals(Object o) { 1.408 + if (o instanceof Dep) { 1.409 + Dep d = (Dep) o; 1.410 + return this.origin.equals(d.origin) && 1.411 + this.originArchive == d.originArchive && 1.412 + this.target.equals(d.target) && 1.413 + this.targetArchive == d.targetArchive; 1.414 } 1.415 + return false; 1.416 + } 1.417 1.418 - String origin = packageOf(o); 1.419 - String target = packageOf(t); 1.420 - add(origin, target, targetArchive, t.getPackageName()); 1.421 + @Override 1.422 + public int hashCode() { 1.423 + int hash = 7; 1.424 + hash = 67*hash + Objects.hashCode(this.origin) 1.425 + + Objects.hashCode(this.originArchive) 1.426 + + Objects.hashCode(this.target) 1.427 + + Objects.hashCode(this.targetArchive); 1.428 + return hash; 1.429 } 1.430 - public String packageOf(Location o) { 1.431 - String pkg = o.getPackageName(); 1.432 - return pkg.isEmpty() ? "<unnamed>" : pkg; 1.433 + 1.434 + @Override 1.435 + public int compareTo(Dep o) { 1.436 + if (this.origin.equals(o.origin)) { 1.437 + if (this.target.equals(o.target)) { 1.438 + if (this.originArchive == o.originArchive && 1.439 + this.targetArchive == o.targetArchive) { 1.440 + return 0; 1.441 + } else if (this.originArchive == o.originArchive) { 1.442 + return this.targetArchive.getPathName().compareTo(o.targetArchive.getPathName()); 1.443 + } else { 1.444 + return this.originArchive.getPathName().compareTo(o.originArchive.getPathName()); 1.445 + } 1.446 + } else { 1.447 + return this.target.compareTo(o.target); 1.448 + } 1.449 + } 1.450 + return this.origin.compareTo(o.origin); 1.451 } 1.452 } 1.453 }
2.1 --- a/src/share/classes/com/sun/tools/jdeps/Archive.java Mon Jul 07 18:03:08 2014 -0700 2.2 +++ b/src/share/classes/com/sun/tools/jdeps/Archive.java Thu Jul 17 15:23:08 2014 -0700 2.3 @@ -1,5 +1,5 @@ 2.4 /* 2.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 2.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 2.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.8 * 2.9 * This code is free software; you can redistribute it and/or modify it 2.10 @@ -25,28 +25,34 @@ 2.11 package com.sun.tools.jdeps; 2.12 2.13 import com.sun.tools.classfile.Dependency.Location; 2.14 + 2.15 +import java.io.IOException; 2.16 import java.nio.file.Path; 2.17 -import java.util.HashMap; 2.18 import java.util.HashSet; 2.19 import java.util.Map; 2.20 import java.util.Set; 2.21 +import java.util.concurrent.ConcurrentHashMap; 2.22 2.23 /** 2.24 * Represents the source of the class files. 2.25 */ 2.26 public class Archive { 2.27 + public static Archive getInstance(Path p) throws IOException { 2.28 + return new Archive(p, ClassFileReader.newInstance(p)); 2.29 + } 2.30 + 2.31 private final Path path; 2.32 private final String filename; 2.33 private final ClassFileReader reader; 2.34 - private final Map<Location, Set<Location>> deps = new HashMap<>(); 2.35 + protected Map<Location, Set<Location>> deps = new ConcurrentHashMap<>(); 2.36 2.37 - public Archive(String name) { 2.38 + protected Archive(String name) { 2.39 this.path = null; 2.40 this.filename = name; 2.41 this.reader = null; 2.42 } 2.43 2.44 - public Archive(Path p, ClassFileReader reader) { 2.45 + protected Archive(Path p, ClassFileReader reader) { 2.46 this.path = p; 2.47 this.filename = path.getFileName().toString(); 2.48 this.reader = reader; 2.49 @@ -56,7 +62,7 @@ 2.50 return reader; 2.51 } 2.52 2.53 - public String getFileName() { 2.54 + public String getName() { 2.55 return filename; 2.56 } 2.57 2.58 @@ -89,6 +95,10 @@ 2.59 } 2.60 } 2.61 2.62 + public boolean isEmpty() { 2.63 + return getClasses().isEmpty(); 2.64 + } 2.65 + 2.66 public String getPathName() { 2.67 return path != null ? path.toString() : filename; 2.68 }
3.1 --- a/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java Mon Jul 07 18:03:08 2014 -0700 3.2 +++ b/src/share/classes/com/sun/tools/jdeps/ClassFileReader.java Thu Jul 17 15:23:08 2014 -0700 3.3 @@ -1,5 +1,5 @@ 3.4 /* 3.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 3.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 3.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.8 * 3.9 * This code is free software; you can redistribute it and/or modify it 3.10 @@ -68,7 +68,8 @@ 3.11 3.12 protected final Path path; 3.13 protected final String baseFileName; 3.14 - private ClassFileReader(Path path) { 3.15 + protected final List<String> skippedEntries = new ArrayList<>(); 3.16 + protected ClassFileReader(Path path) { 3.17 this.path = path; 3.18 this.baseFileName = path.getFileName() != null 3.19 ? path.getFileName().toString() 3.20 @@ -79,6 +80,10 @@ 3.21 return baseFileName; 3.22 } 3.23 3.24 + public List<String> skippedEntries() { 3.25 + return skippedEntries; 3.26 + } 3.27 + 3.28 /** 3.29 * Returns the ClassFile matching the given binary name 3.30 * or a fully-qualified class name. 3.31 @@ -232,11 +237,12 @@ 3.32 } 3.33 } 3.34 3.35 - private static class JarFileReader extends ClassFileReader { 3.36 - final JarFile jarfile; 3.37 + static class JarFileReader extends ClassFileReader { 3.38 + private final JarFile jarfile; 3.39 JarFileReader(Path path) throws IOException { 3.40 - this(path, new JarFile(path.toFile())); 3.41 + this(path, new JarFile(path.toFile(), false)); 3.42 } 3.43 + 3.44 JarFileReader(Path path, JarFile jf) throws IOException { 3.45 super(path); 3.46 this.jarfile = jf; 3.47 @@ -252,18 +258,18 @@ 3.48 + entryName.substring(i + 1, entryName.length())); 3.49 } 3.50 if (e != null) { 3.51 - return readClassFile(e); 3.52 + return readClassFile(jarfile, e); 3.53 } 3.54 } else { 3.55 JarEntry e = jarfile.getJarEntry(name + ".class"); 3.56 if (e != null) { 3.57 - return readClassFile(e); 3.58 + return readClassFile(jarfile, e); 3.59 } 3.60 } 3.61 return null; 3.62 } 3.63 3.64 - private ClassFile readClassFile(JarEntry e) throws IOException { 3.65 + protected ClassFile readClassFile(JarFile jarfile, JarEntry e) throws IOException { 3.66 InputStream is = null; 3.67 try { 3.68 is = jarfile.getInputStream(e); 3.69 @@ -277,60 +283,76 @@ 3.70 } 3.71 3.72 public Iterable<ClassFile> getClassFiles() throws IOException { 3.73 - final Iterator<ClassFile> iter = new JarFileIterator(); 3.74 + final Iterator<ClassFile> iter = new JarFileIterator(this, jarfile); 3.75 return new Iterable<ClassFile>() { 3.76 public Iterator<ClassFile> iterator() { 3.77 return iter; 3.78 } 3.79 }; 3.80 } 3.81 + } 3.82 3.83 - class JarFileIterator implements Iterator<ClassFile> { 3.84 - private Enumeration<JarEntry> entries; 3.85 - private JarEntry nextEntry; 3.86 - JarFileIterator() { 3.87 - this.entries = jarfile.entries(); 3.88 - while (entries.hasMoreElements()) { 3.89 - JarEntry e = entries.nextElement(); 3.90 - String name = e.getName(); 3.91 - if (name.endsWith(".class")) { 3.92 - this.nextEntry = e; 3.93 - break; 3.94 - } 3.95 + class JarFileIterator implements Iterator<ClassFile> { 3.96 + protected final JarFileReader reader; 3.97 + protected Enumeration<JarEntry> entries; 3.98 + protected JarFile jf; 3.99 + protected JarEntry nextEntry; 3.100 + protected ClassFile cf; 3.101 + JarFileIterator(JarFileReader reader) { 3.102 + this(reader, null); 3.103 + } 3.104 + JarFileIterator(JarFileReader reader, JarFile jarfile) { 3.105 + this.reader = reader; 3.106 + setJarFile(jarfile); 3.107 + } 3.108 + 3.109 + void setJarFile(JarFile jarfile) { 3.110 + if (jarfile == null) return; 3.111 + 3.112 + this.jf = jarfile; 3.113 + this.entries = jf.entries(); 3.114 + this.nextEntry = nextEntry(); 3.115 + } 3.116 + 3.117 + public boolean hasNext() { 3.118 + if (nextEntry != null && cf != null) { 3.119 + return true; 3.120 + } 3.121 + while (nextEntry != null) { 3.122 + try { 3.123 + cf = reader.readClassFile(jf, nextEntry); 3.124 + return true; 3.125 + } catch (ClassFileError | IOException ex) { 3.126 + skippedEntries.add(nextEntry.getName()); 3.127 + } 3.128 + nextEntry = nextEntry(); 3.129 + } 3.130 + return false; 3.131 + } 3.132 + 3.133 + public ClassFile next() { 3.134 + if (!hasNext()) { 3.135 + throw new NoSuchElementException(); 3.136 + } 3.137 + ClassFile classFile = cf; 3.138 + cf = null; 3.139 + nextEntry = nextEntry(); 3.140 + return classFile; 3.141 + } 3.142 + 3.143 + protected JarEntry nextEntry() { 3.144 + while (entries.hasMoreElements()) { 3.145 + JarEntry e = entries.nextElement(); 3.146 + String name = e.getName(); 3.147 + if (name.endsWith(".class")) { 3.148 + return e; 3.149 } 3.150 } 3.151 + return null; 3.152 + } 3.153 3.154 - public boolean hasNext() { 3.155 - return nextEntry != null; 3.156 - } 3.157 - 3.158 - public ClassFile next() { 3.159 - if (!hasNext()) { 3.160 - throw new NoSuchElementException(); 3.161 - } 3.162 - 3.163 - ClassFile cf; 3.164 - try { 3.165 - cf = readClassFile(nextEntry); 3.166 - } catch (IOException ex) { 3.167 - throw new ClassFileError(ex); 3.168 - } 3.169 - JarEntry entry = nextEntry; 3.170 - nextEntry = null; 3.171 - while (entries.hasMoreElements()) { 3.172 - JarEntry e = entries.nextElement(); 3.173 - String name = e.getName(); 3.174 - if (name.endsWith(".class")) { 3.175 - nextEntry = e; 3.176 - break; 3.177 - } 3.178 - } 3.179 - return cf; 3.180 - } 3.181 - 3.182 - public void remove() { 3.183 - throw new UnsupportedOperationException("Not supported yet."); 3.184 - } 3.185 + public void remove() { 3.186 + throw new UnsupportedOperationException("Not supported yet."); 3.187 } 3.188 } 3.189 }
4.1 --- a/src/share/classes/com/sun/tools/jdeps/JdepsTask.java Mon Jul 07 18:03:08 2014 -0700 4.2 +++ b/src/share/classes/com/sun/tools/jdeps/JdepsTask.java Thu Jul 17 15:23:08 2014 -0700 4.3 @@ -1,5 +1,5 @@ 4.4 /* 4.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 4.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 4.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.8 * 4.9 * This code is free software; you can redistribute it and/or modify it 4.10 @@ -30,7 +30,9 @@ 4.11 import com.sun.tools.classfile.Dependencies; 4.12 import com.sun.tools.classfile.Dependencies.ClassFileError; 4.13 import com.sun.tools.classfile.Dependency; 4.14 +import com.sun.tools.classfile.Dependency.Location; 4.15 import com.sun.tools.jdeps.PlatformClassPath.JDKArchive; 4.16 +import static com.sun.tools.jdeps.Analyzer.Type.*; 4.17 import java.io.*; 4.18 import java.nio.file.DirectoryStream; 4.19 import java.nio.file.Files; 4.20 @@ -110,7 +112,7 @@ 4.21 void process(JdepsTask task, String opt, String arg) throws BadArgs { 4.22 Path p = Paths.get(arg); 4.23 if (Files.exists(p) && (!Files.isDirectory(p) || !Files.isWritable(p))) { 4.24 - throw new BadArgs("err.dot.output.path", arg); 4.25 + throw new BadArgs("err.invalid.path", arg); 4.26 } 4.27 task.options.dotOutputDir = arg; 4.28 } 4.29 @@ -118,25 +120,26 @@ 4.30 new Option(false, "-s", "-summary") { 4.31 void process(JdepsTask task, String opt, String arg) { 4.32 task.options.showSummary = true; 4.33 - task.options.verbose = Analyzer.Type.SUMMARY; 4.34 + task.options.verbose = SUMMARY; 4.35 } 4.36 }, 4.37 new Option(false, "-v", "-verbose", 4.38 "-verbose:package", 4.39 - "-verbose:class") 4.40 - { 4.41 + "-verbose:class") { 4.42 void process(JdepsTask task, String opt, String arg) throws BadArgs { 4.43 switch (opt) { 4.44 case "-v": 4.45 case "-verbose": 4.46 - task.options.verbose = Analyzer.Type.VERBOSE; 4.47 + task.options.verbose = VERBOSE; 4.48 + task.options.filterSameArchive = false; 4.49 + task.options.filterSamePackage = false; 4.50 break; 4.51 case "-verbose:package": 4.52 - task.options.verbose = Analyzer.Type.PACKAGE; 4.53 - break; 4.54 + task.options.verbose = PACKAGE; 4.55 + break; 4.56 case "-verbose:class": 4.57 - task.options.verbose = Analyzer.Type.CLASS; 4.58 - break; 4.59 + task.options.verbose = CLASS; 4.60 + break; 4.61 default: 4.62 throw new BadArgs("err.invalid.arg.for.option", opt); 4.63 } 4.64 @@ -157,6 +160,32 @@ 4.65 task.options.regex = arg; 4.66 } 4.67 }, 4.68 + 4.69 + new Option(true, "-f", "-filter") { 4.70 + void process(JdepsTask task, String opt, String arg) { 4.71 + task.options.filterRegex = arg; 4.72 + } 4.73 + }, 4.74 + new Option(false, "-filter:package", 4.75 + "-filter:archive", 4.76 + "-filter:none") { 4.77 + void process(JdepsTask task, String opt, String arg) { 4.78 + switch (opt) { 4.79 + case "-filter:package": 4.80 + task.options.filterSamePackage = true; 4.81 + task.options.filterSameArchive = false; 4.82 + break; 4.83 + case "-filter:archive": 4.84 + task.options.filterSameArchive = true; 4.85 + task.options.filterSamePackage = false; 4.86 + break; 4.87 + case "-filter:none": 4.88 + task.options.filterSameArchive = false; 4.89 + task.options.filterSamePackage = false; 4.90 + break; 4.91 + } 4.92 + } 4.93 + }, 4.94 new Option(true, "-include") { 4.95 void process(JdepsTask task, String opt, String arg) throws BadArgs { 4.96 task.options.includePattern = Pattern.compile(arg); 4.97 @@ -178,12 +207,15 @@ 4.98 new Option(false, "-R", "-recursive") { 4.99 void process(JdepsTask task, String opt, String arg) { 4.100 task.options.depth = 0; 4.101 + // turn off filtering 4.102 + task.options.filterSameArchive = false; 4.103 + task.options.filterSamePackage = false; 4.104 } 4.105 }, 4.106 new Option(false, "-jdkinternals") { 4.107 void process(JdepsTask task, String opt, String arg) { 4.108 task.options.findJDKInternals = true; 4.109 - task.options.verbose = Analyzer.Type.CLASS; 4.110 + task.options.verbose = CLASS; 4.111 if (task.options.includePattern == null) { 4.112 task.options.includePattern = Pattern.compile(".*"); 4.113 } 4.114 @@ -262,7 +294,7 @@ 4.115 showHelp(); 4.116 return EXIT_CMDERR; 4.117 } 4.118 - if (options.showSummary && options.verbose != Analyzer.Type.SUMMARY) { 4.119 + if (options.showSummary && options.verbose != SUMMARY) { 4.120 showHelp(); 4.121 return EXIT_CMDERR; 4.122 } 4.123 @@ -283,9 +315,28 @@ 4.124 4.125 private final List<Archive> sourceLocations = new ArrayList<>(); 4.126 private boolean run() throws IOException { 4.127 + // parse classfiles and find all dependencies 4.128 findDependencies(); 4.129 - Analyzer analyzer = new Analyzer(options.verbose); 4.130 + 4.131 + Analyzer analyzer = new Analyzer(options.verbose, new Analyzer.Filter() { 4.132 + @Override 4.133 + public boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive) { 4.134 + if (options.findJDKInternals) { 4.135 + // accepts target that is JDK class but not exported 4.136 + return isJDKArchive(targetArchive) && 4.137 + !((JDKArchive) targetArchive).isExported(target.getClassName()); 4.138 + } else if (options.filterSameArchive) { 4.139 + // accepts origin and target that from different archive 4.140 + return originArchive != targetArchive; 4.141 + } 4.142 + return true; 4.143 + } 4.144 + }); 4.145 + 4.146 + // analyze the dependencies 4.147 analyzer.run(sourceLocations); 4.148 + 4.149 + // output result 4.150 if (options.dotOutputDir != null) { 4.151 Path dir = Paths.get(options.dotOutputDir); 4.152 Files.createDirectories(dir); 4.153 @@ -296,27 +347,34 @@ 4.154 return true; 4.155 } 4.156 4.157 - private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException { 4.158 + private void generateSummaryDotFile(Path dir, Analyzer analyzer) throws IOException { 4.159 + // If verbose mode (-v or -verbose option), 4.160 + // the summary.dot file shows package-level dependencies. 4.161 + Analyzer.Type summaryType = 4.162 + (options.verbose == PACKAGE || options.verbose == SUMMARY) ? SUMMARY : PACKAGE; 4.163 Path summary = dir.resolve("summary.dot"); 4.164 - boolean verbose = options.verbose == Analyzer.Type.VERBOSE; 4.165 - DotGraph<?> graph = verbose ? new DotSummaryForPackage() 4.166 - : new DotSummaryForArchive(); 4.167 - for (Archive archive : sourceLocations) { 4.168 - analyzer.visitArchiveDependences(archive, graph); 4.169 - if (verbose || options.showLabel) { 4.170 - // traverse detailed dependences to generate package-level 4.171 - // summary or build labels for edges 4.172 - analyzer.visitDependences(archive, graph); 4.173 + try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary)); 4.174 + SummaryDotFile dotfile = new SummaryDotFile(sw, summaryType)) { 4.175 + for (Archive archive : sourceLocations) { 4.176 + if (!archive.isEmpty()) { 4.177 + if (options.verbose == PACKAGE || options.verbose == SUMMARY) { 4.178 + if (options.showLabel) { 4.179 + // build labels listing package-level dependencies 4.180 + analyzer.visitDependences(archive, dotfile.labelBuilder(), PACKAGE); 4.181 + } 4.182 + } 4.183 + analyzer.visitDependences(archive, dotfile, summaryType); 4.184 + } 4.185 } 4.186 } 4.187 - try (PrintWriter sw = new PrintWriter(Files.newOutputStream(summary))) { 4.188 - graph.writeTo(sw); 4.189 - } 4.190 + } 4.191 + 4.192 + private void generateDotFiles(Path dir, Analyzer analyzer) throws IOException { 4.193 // output individual .dot file for each archive 4.194 - if (options.verbose != Analyzer.Type.SUMMARY) { 4.195 + if (options.verbose != SUMMARY) { 4.196 for (Archive archive : sourceLocations) { 4.197 if (analyzer.hasDependences(archive)) { 4.198 - Path dotfile = dir.resolve(archive.getFileName() + ".dot"); 4.199 + Path dotfile = dir.resolve(archive.getName() + ".dot"); 4.200 try (PrintWriter pw = new PrintWriter(Files.newOutputStream(dotfile)); 4.201 DotFileFormatter formatter = new DotFileFormatter(pw, archive)) { 4.202 analyzer.visitDependences(archive, formatter); 4.203 @@ -324,17 +382,23 @@ 4.204 } 4.205 } 4.206 } 4.207 + // generate summary dot file 4.208 + generateSummaryDotFile(dir, analyzer); 4.209 } 4.210 4.211 private void printRawOutput(PrintWriter writer, Analyzer analyzer) { 4.212 + RawOutputFormatter depFormatter = new RawOutputFormatter(writer); 4.213 + RawSummaryFormatter summaryFormatter = new RawSummaryFormatter(writer); 4.214 for (Archive archive : sourceLocations) { 4.215 - RawOutputFormatter formatter = new RawOutputFormatter(writer); 4.216 - analyzer.visitArchiveDependences(archive, formatter); 4.217 - if (options.verbose != Analyzer.Type.SUMMARY) { 4.218 - analyzer.visitDependences(archive, formatter); 4.219 + if (!archive.isEmpty()) { 4.220 + analyzer.visitDependences(archive, summaryFormatter, SUMMARY); 4.221 + if (analyzer.hasDependences(archive) && options.verbose != SUMMARY) { 4.222 + analyzer.visitDependences(archive, depFormatter); 4.223 + } 4.224 } 4.225 } 4.226 } 4.227 + 4.228 private boolean isValidClassName(String name) { 4.229 if (!Character.isJavaIdentifierStart(name.charAt(0))) { 4.230 return false; 4.231 @@ -348,21 +412,54 @@ 4.232 return true; 4.233 } 4.234 4.235 - private Dependency.Filter getDependencyFilter() { 4.236 - if (options.regex != null) { 4.237 - return Dependencies.getRegexFilter(Pattern.compile(options.regex)); 4.238 - } else if (options.packageNames.size() > 0) { 4.239 - return Dependencies.getPackageFilter(options.packageNames, false); 4.240 - } else { 4.241 - return new Dependency.Filter() { 4.242 - @Override 4.243 - public boolean accepts(Dependency dependency) { 4.244 - return !dependency.getOrigin().equals(dependency.getTarget()); 4.245 - } 4.246 - }; 4.247 + /* 4.248 + * Dep Filter configured based on the input jdeps option 4.249 + * 1. -p and -regex to match target dependencies 4.250 + * 2. -filter:package to filter out same-package dependencies 4.251 + * 4.252 + * This filter is applied when jdeps parses the class files 4.253 + * and filtered dependencies are not stored in the Analyzer. 4.254 + * 4.255 + * -filter:archive is applied later in the Analyzer as the 4.256 + * containing archive of a target class may not be known until 4.257 + * the entire archive 4.258 + */ 4.259 + class DependencyFilter implements Dependency.Filter { 4.260 + final Dependency.Filter filter; 4.261 + final Pattern filterPattern; 4.262 + DependencyFilter() { 4.263 + if (options.regex != null) { 4.264 + this.filter = Dependencies.getRegexFilter(Pattern.compile(options.regex)); 4.265 + } else if (options.packageNames.size() > 0) { 4.266 + this.filter = Dependencies.getPackageFilter(options.packageNames, false); 4.267 + } else { 4.268 + this.filter = null; 4.269 + } 4.270 + 4.271 + this.filterPattern = 4.272 + options.filterRegex != null ? Pattern.compile(options.filterRegex) : null; 4.273 + } 4.274 + @Override 4.275 + public boolean accepts(Dependency d) { 4.276 + if (d.getOrigin().equals(d.getTarget())) { 4.277 + return false; 4.278 + } 4.279 + String pn = d.getTarget().getPackageName(); 4.280 + if (options.filterSamePackage && d.getOrigin().getPackageName().equals(pn)) { 4.281 + return false; 4.282 + } 4.283 + 4.284 + if (filterPattern != null && filterPattern.matcher(pn).matches()) { 4.285 + return false; 4.286 + } 4.287 + return filter != null ? filter.accepts(d) : true; 4.288 } 4.289 } 4.290 4.291 + /** 4.292 + * Tests if the given class matches the pattern given in the -include option 4.293 + * or if it's a public class if -apionly option is specified 4.294 + */ 4.295 private boolean matches(String classname, AccessFlags flags) { 4.296 if (options.apiOnly && !flags.is(AccessFlags.ACC_PUBLIC)) { 4.297 return false; 4.298 @@ -377,14 +474,14 @@ 4.299 Dependency.Finder finder = 4.300 options.apiOnly ? Dependencies.getAPIFinder(AccessFlags.ACC_PROTECTED) 4.301 : Dependencies.getClassDependencyFinder(); 4.302 - Dependency.Filter filter = getDependencyFilter(); 4.303 + Dependency.Filter filter = new DependencyFilter(); 4.304 4.305 List<Archive> archives = new ArrayList<>(); 4.306 Deque<String> roots = new LinkedList<>(); 4.307 for (String s : classes) { 4.308 Path p = Paths.get(s); 4.309 if (Files.exists(p)) { 4.310 - archives.add(new Archive(p, ClassFileReader.newInstance(p))); 4.311 + archives.add(Archive.getInstance(p)); 4.312 } else { 4.313 if (isValidClassName(s)) { 4.314 roots.add(s); 4.315 @@ -421,19 +518,26 @@ 4.316 throw new ClassFileError(e); 4.317 } 4.318 4.319 - if (matches(classFileName, cf.access_flags)) { 4.320 - if (!doneClasses.contains(classFileName)) { 4.321 - doneClasses.add(classFileName); 4.322 + // tests if this class matches the -include or -apiOnly option if specified 4.323 + if (!matches(classFileName, cf.access_flags)) { 4.324 + continue; 4.325 + } 4.326 + 4.327 + if (!doneClasses.contains(classFileName)) { 4.328 + doneClasses.add(classFileName); 4.329 + } 4.330 + 4.331 + for (Dependency d : finder.findDependencies(cf)) { 4.332 + if (filter.accepts(d)) { 4.333 + String cn = d.getTarget().getName(); 4.334 + if (!doneClasses.contains(cn) && !deque.contains(cn)) { 4.335 + deque.add(cn); 4.336 + } 4.337 + a.addClass(d.getOrigin(), d.getTarget()); 4.338 } 4.339 - for (Dependency d : finder.findDependencies(cf)) { 4.340 - if (filter.accepts(d)) { 4.341 - String cn = d.getTarget().getName(); 4.342 - if (!doneClasses.contains(cn) && !deque.contains(cn)) { 4.343 - deque.add(cn); 4.344 - } 4.345 - a.addClass(d.getOrigin(), d.getTarget()); 4.346 - } 4.347 - } 4.348 + } 4.349 + for (String name : a.reader().skippedEntries()) { 4.350 + warning("warn.skipped.entry", name, a.getPathName()); 4.351 } 4.352 } 4.353 } 4.354 @@ -462,6 +566,10 @@ 4.355 // if name is a fully-qualified class name specified 4.356 // from command-line, this class might already be parsed 4.357 doneClasses.add(classFileName); 4.358 + // process @jdk.Exported for JDK classes 4.359 + if (isJDKArchive(a)) { 4.360 + ((JDKArchive)a).processJdkExported(cf); 4.361 + } 4.362 for (Dependency d : finder.findDependencies(cf)) { 4.363 if (depth == 0) { 4.364 // ignore the dependency 4.365 @@ -544,7 +652,7 @@ 4.366 for (Option o : recognizedOptions) { 4.367 String name = o.aliases[0].substring(1); // there must always be at least one name 4.368 name = name.charAt(0) == '-' ? name.substring(1) : name; 4.369 - if (o.isHidden() || name.equals("h")) { 4.370 + if (o.isHidden() || name.equals("h") || name.startsWith("filter:")) { 4.371 continue; 4.372 } 4.373 log.println(getMessage("main.opt." + name)); 4.374 @@ -582,14 +690,18 @@ 4.375 boolean fullVersion; 4.376 boolean showProfile; 4.377 boolean showSummary; 4.378 - boolean wildcard; 4.379 boolean apiOnly; 4.380 boolean showLabel; 4.381 boolean findJDKInternals; 4.382 + // default is to show package-level dependencies 4.383 + // and filter references from same package 4.384 + Analyzer.Type verbose = PACKAGE; 4.385 + boolean filterSamePackage = true; 4.386 + boolean filterSameArchive = false; 4.387 + String filterRegex; 4.388 String dotOutputDir; 4.389 String classpath = ""; 4.390 int depth = 1; 4.391 - Analyzer.Type verbose = Analyzer.Type.PACKAGE; 4.392 Set<String> packageNames = new HashSet<>(); 4.393 String regex; // apply to the dependences 4.394 Pattern includePattern; // apply to classes 4.395 @@ -613,19 +725,6 @@ 4.396 } 4.397 } 4.398 4.399 - private List<Archive> getArchives(List<String> filenames) throws IOException { 4.400 - List<Archive> result = new ArrayList<Archive>(); 4.401 - for (String s : filenames) { 4.402 - Path p = Paths.get(s); 4.403 - if (Files.exists(p)) { 4.404 - result.add(new Archive(p, ClassFileReader.newInstance(p))); 4.405 - } else { 4.406 - warning("warn.file.not.exist", s); 4.407 - } 4.408 - } 4.409 - return result; 4.410 - } 4.411 - 4.412 private List<Archive> getClassPathArchives(String paths) throws IOException { 4.413 List<Archive> result = new ArrayList<>(); 4.414 if (paths.isEmpty()) { 4.415 @@ -648,7 +747,7 @@ 4.416 } 4.417 for (Path f : files) { 4.418 if (Files.exists(f)) { 4.419 - result.add(new Archive(f, ClassFileReader.newInstance(f))); 4.420 + result.add(Archive.getInstance(f)); 4.421 } 4.422 } 4.423 } 4.424 @@ -656,81 +755,50 @@ 4.425 return result; 4.426 } 4.427 4.428 - /** 4.429 - * If the given archive is JDK archive and non-null Profile, 4.430 - * this method returns the profile name only if -profile option is specified; 4.431 - * a null profile indicates it accesses a private JDK API and this method 4.432 - * will return "JDK internal API". 4.433 - * 4.434 - * For non-JDK archives, this method returns the file name of the archive. 4.435 - */ 4.436 - private String getProfileArchiveInfo(Archive source, Profile profile) { 4.437 - if (options.showProfile && profile != null) 4.438 - return profile.toString(); 4.439 - 4.440 - if (source instanceof JDKArchive) { 4.441 - return profile == null ? "JDK internal API (" + source.getFileName() + ")" : ""; 4.442 - } 4.443 - return source.getFileName(); 4.444 - } 4.445 - 4.446 - /** 4.447 - * Returns the profile name or "JDK internal API" for JDK archive; 4.448 - * otherwise empty string. 4.449 - */ 4.450 - private String profileName(Archive archive, Profile profile) { 4.451 - if (archive instanceof JDKArchive) { 4.452 - return Objects.toString(profile, "JDK internal API"); 4.453 - } else { 4.454 - return ""; 4.455 - } 4.456 - } 4.457 - 4.458 class RawOutputFormatter implements Analyzer.Visitor { 4.459 private final PrintWriter writer; 4.460 + private String pkg = ""; 4.461 RawOutputFormatter(PrintWriter writer) { 4.462 this.writer = writer; 4.463 } 4.464 - 4.465 - private String pkg = ""; 4.466 @Override 4.467 - public void visitDependence(String origin, Archive source, 4.468 - String target, Archive archive, Profile profile) { 4.469 - if (options.findJDKInternals && 4.470 - !(archive instanceof JDKArchive && profile == null)) { 4.471 - // filter dependences other than JDK internal APIs 4.472 - return; 4.473 - } 4.474 - if (options.verbose == Analyzer.Type.VERBOSE) { 4.475 - writer.format(" %-50s -> %-50s %s%n", 4.476 - origin, target, getProfileArchiveInfo(archive, profile)); 4.477 + public void visitDependence(String origin, Archive originArchive, 4.478 + String target, Archive targetArchive) { 4.479 + String tag = toTag(target, targetArchive); 4.480 + if (options.verbose == VERBOSE) { 4.481 + writer.format(" %-50s -> %-50s %s%n", origin, target, tag); 4.482 } else { 4.483 if (!origin.equals(pkg)) { 4.484 pkg = origin; 4.485 - writer.format(" %s (%s)%n", origin, source.getFileName()); 4.486 + writer.format(" %s (%s)%n", origin, originArchive.getName()); 4.487 } 4.488 - writer.format(" -> %-50s %s%n", 4.489 - target, getProfileArchiveInfo(archive, profile)); 4.490 - } 4.491 - } 4.492 - 4.493 - @Override 4.494 - public void visitArchiveDependence(Archive origin, Archive target, Profile profile) { 4.495 - writer.format("%s -> %s", origin.getPathName(), target.getPathName()); 4.496 - if (options.showProfile && profile != null) { 4.497 - writer.format(" (%s)%n", profile); 4.498 - } else { 4.499 - writer.format("%n"); 4.500 + writer.format(" -> %-50s %s%n", target, tag); 4.501 } 4.502 } 4.503 } 4.504 4.505 - class DotFileFormatter extends DotGraph<String> implements AutoCloseable { 4.506 + class RawSummaryFormatter implements Analyzer.Visitor { 4.507 + private final PrintWriter writer; 4.508 + RawSummaryFormatter(PrintWriter writer) { 4.509 + this.writer = writer; 4.510 + } 4.511 + @Override 4.512 + public void visitDependence(String origin, Archive originArchive, 4.513 + String target, Archive targetArchive) { 4.514 + writer.format("%s -> %s", originArchive.getName(), targetArchive.getPathName()); 4.515 + if (options.showProfile && JDKArchive.isProfileArchive(targetArchive)) { 4.516 + writer.format(" (%s)", target); 4.517 + } 4.518 + writer.format("%n"); 4.519 + } 4.520 + } 4.521 + 4.522 + class DotFileFormatter implements Analyzer.Visitor, AutoCloseable { 4.523 private final PrintWriter writer; 4.524 private final String name; 4.525 DotFileFormatter(PrintWriter writer, Archive archive) { 4.526 this.writer = writer; 4.527 - this.name = archive.getFileName(); 4.528 + this.name = archive.getName(); 4.529 writer.format("digraph \"%s\" {%n", name); 4.530 writer.format(" // Path: %s%n", archive.getPathName()); 4.531 } 4.532 @@ -741,173 +809,130 @@ 4.533 } 4.534 4.535 @Override 4.536 - public void visitDependence(String origin, Archive source, 4.537 - String target, Archive archive, Profile profile) { 4.538 - if (options.findJDKInternals && 4.539 - !(archive instanceof JDKArchive && profile == null)) { 4.540 - // filter dependences other than JDK internal APIs 4.541 - return; 4.542 - } 4.543 - // if -P option is specified, package name -> profile will 4.544 - // be shown and filter out multiple same edges. 4.545 - String name = getProfileArchiveInfo(archive, profile); 4.546 - writeEdge(writer, new Edge(origin, target, getProfileArchiveInfo(archive, profile))); 4.547 - } 4.548 - @Override 4.549 - public void visitArchiveDependence(Archive origin, Archive target, Profile profile) { 4.550 - throw new UnsupportedOperationException(); 4.551 + public void visitDependence(String origin, Archive originArchive, 4.552 + String target, Archive targetArchive) { 4.553 + String tag = toTag(target, targetArchive); 4.554 + writer.format(" %-50s -> \"%s\";%n", 4.555 + String.format("\"%s\"", origin), 4.556 + tag.isEmpty() ? target 4.557 + : String.format("%s (%s)", target, tag)); 4.558 } 4.559 } 4.560 4.561 - class DotSummaryForArchive extends DotGraph<Archive> { 4.562 + class SummaryDotFile implements Analyzer.Visitor, AutoCloseable { 4.563 + private final PrintWriter writer; 4.564 + private final Analyzer.Type type; 4.565 + private final Map<Archive, Map<Archive,StringBuilder>> edges = new HashMap<>(); 4.566 + SummaryDotFile(PrintWriter writer, Analyzer.Type type) { 4.567 + this.writer = writer; 4.568 + this.type = type; 4.569 + writer.format("digraph \"summary\" {%n"); 4.570 + } 4.571 + 4.572 @Override 4.573 - public void visitDependence(String origin, Archive source, 4.574 - String target, Archive archive, Profile profile) { 4.575 - Edge e = findEdge(source, archive); 4.576 - assert e != null; 4.577 - // add the dependency to the label if enabled and not compact1 4.578 - if (profile == Profile.COMPACT1) { 4.579 - return; 4.580 + public void close() { 4.581 + writer.println("}"); 4.582 + } 4.583 + 4.584 + @Override 4.585 + public void visitDependence(String origin, Archive originArchive, 4.586 + String target, Archive targetArchive) { 4.587 + String targetName = type == PACKAGE ? target : targetArchive.getName(); 4.588 + if (type == PACKAGE) { 4.589 + String tag = toTag(target, targetArchive, type); 4.590 + if (!tag.isEmpty()) 4.591 + targetName += " (" + tag + ")"; 4.592 + } else if (options.showProfile && JDKArchive.isProfileArchive(targetArchive)) { 4.593 + targetName += " (" + target + ")"; 4.594 } 4.595 - e.addLabel(origin, target, profileName(archive, profile)); 4.596 + String label = getLabel(originArchive, targetArchive); 4.597 + writer.format(" %-50s -> \"%s\"%s;%n", 4.598 + String.format("\"%s\"", origin), targetName, label); 4.599 } 4.600 - @Override 4.601 - public void visitArchiveDependence(Archive origin, Archive target, Profile profile) { 4.602 - // add an edge with the archive's name with no tag 4.603 - // so that there is only one node for each JDK archive 4.604 - // while there may be edges to different profiles 4.605 - Edge e = addEdge(origin, target, ""); 4.606 - if (target instanceof JDKArchive) { 4.607 - // add a label to print the profile 4.608 - if (profile == null) { 4.609 - e.addLabel("JDK internal API"); 4.610 - } else if (options.showProfile && !options.showLabel) { 4.611 - e.addLabel(profile.toString()); 4.612 + 4.613 + String getLabel(Archive origin, Archive target) { 4.614 + if (edges.isEmpty()) 4.615 + return ""; 4.616 + 4.617 + StringBuilder label = edges.get(origin).get(target); 4.618 + return label == null ? "" : String.format(" [label=\"%s\",fontsize=9]", label.toString()); 4.619 + } 4.620 + 4.621 + Analyzer.Visitor labelBuilder() { 4.622 + // show the package-level dependencies as labels in the dot graph 4.623 + return new Analyzer.Visitor() { 4.624 + @Override 4.625 + public void visitDependence(String origin, Archive originArchive, 4.626 + String target, Archive targetArchive) 4.627 + { 4.628 + Map<Archive,StringBuilder> labels = edges.get(originArchive); 4.629 + if (!edges.containsKey(originArchive)) { 4.630 + edges.put(originArchive, labels = new HashMap<>()); 4.631 + } 4.632 + StringBuilder sb = labels.get(targetArchive); 4.633 + if (sb == null) { 4.634 + labels.put(targetArchive, sb = new StringBuilder()); 4.635 + } 4.636 + String tag = toTag(target, targetArchive, PACKAGE); 4.637 + addLabel(sb, origin, target, tag); 4.638 } 4.639 - } 4.640 + 4.641 + void addLabel(StringBuilder label, String origin, String target, String tag) { 4.642 + label.append(origin).append(" -> ").append(target); 4.643 + if (!tag.isEmpty()) { 4.644 + label.append(" (" + tag + ")"); 4.645 + } 4.646 + label.append("\\n"); 4.647 + } 4.648 + }; 4.649 } 4.650 } 4.651 4.652 - // DotSummaryForPackage generates the summary.dot file for verbose mode 4.653 - // (-v or -verbose option) that includes all class dependencies. 4.654 - // The summary.dot file shows package-level dependencies. 4.655 - class DotSummaryForPackage extends DotGraph<String> { 4.656 - private String packageOf(String cn) { 4.657 - int i = cn.lastIndexOf('.'); 4.658 - return i > 0 ? cn.substring(0, i) : "<unnamed>"; 4.659 + /** 4.660 + * Test if the given archive is part of the JDK 4.661 + */ 4.662 + private boolean isJDKArchive(Archive archive) { 4.663 + return JDKArchive.class.isInstance(archive); 4.664 + } 4.665 + 4.666 + /** 4.667 + * If the given archive is JDK archive, this method returns the profile name 4.668 + * only if -profile option is specified; it accesses a private JDK API and 4.669 + * the returned value will have "JDK internal API" prefix 4.670 + * 4.671 + * For non-JDK archives, this method returns the file name of the archive. 4.672 + */ 4.673 + private String toTag(String name, Archive source, Analyzer.Type type) { 4.674 + if (!isJDKArchive(source)) { 4.675 + return source.getName(); 4.676 } 4.677 - @Override 4.678 - public void visitDependence(String origin, Archive source, 4.679 - String target, Archive archive, Profile profile) { 4.680 - // add a package dependency edge 4.681 - String from = packageOf(origin); 4.682 - String to = packageOf(target); 4.683 - Edge e = addEdge(from, to, getProfileArchiveInfo(archive, profile)); 4.684 4.685 - // add the dependency to the label if enabled and not compact1 4.686 - if (!options.showLabel || profile == Profile.COMPACT1) { 4.687 - return; 4.688 - } 4.689 - 4.690 - // trim the package name of origin to shorten the label 4.691 - int i = origin.lastIndexOf('.'); 4.692 - String n1 = i < 0 ? origin : origin.substring(i+1); 4.693 - e.addLabel(n1, target, profileName(archive, profile)); 4.694 + JDKArchive jdk = (JDKArchive)source; 4.695 + boolean isExported = false; 4.696 + if (type == CLASS || type == VERBOSE) { 4.697 + isExported = jdk.isExported(name); 4.698 + } else { 4.699 + isExported = jdk.isExportedPackage(name); 4.700 } 4.701 - @Override 4.702 - public void visitArchiveDependence(Archive origin, Archive target, Profile profile) { 4.703 - // nop 4.704 + Profile p = getProfile(name, type); 4.705 + if (isExported) { 4.706 + // exported API 4.707 + return options.showProfile && p != null ? p.profileName() : ""; 4.708 + } else { 4.709 + return "JDK internal API (" + source.getName() + ")"; 4.710 } 4.711 } 4.712 - abstract class DotGraph<T> implements Analyzer.Visitor { 4.713 - private final Set<Edge> edges = new LinkedHashSet<>(); 4.714 - private Edge curEdge; 4.715 - public void writeTo(PrintWriter writer) { 4.716 - writer.format("digraph \"summary\" {%n"); 4.717 - for (Edge e: edges) { 4.718 - writeEdge(writer, e); 4.719 - } 4.720 - writer.println("}"); 4.721 + 4.722 + private String toTag(String name, Archive source) { 4.723 + return toTag(name, source, options.verbose); 4.724 + } 4.725 + 4.726 + private Profile getProfile(String name, Analyzer.Type type) { 4.727 + String pn = name; 4.728 + if (type == CLASS || type == VERBOSE) { 4.729 + int i = name.lastIndexOf('.'); 4.730 + pn = i > 0 ? name.substring(0, i) : ""; 4.731 } 4.732 - 4.733 - void writeEdge(PrintWriter writer, Edge e) { 4.734 - writer.format(" %-50s -> \"%s\"%s;%n", 4.735 - String.format("\"%s\"", e.from.toString()), 4.736 - e.tag.isEmpty() ? e.to 4.737 - : String.format("%s (%s)", e.to, e.tag), 4.738 - getLabel(e)); 4.739 - } 4.740 - 4.741 - Edge addEdge(T origin, T target, String tag) { 4.742 - Edge e = new Edge(origin, target, tag); 4.743 - if (e.equals(curEdge)) { 4.744 - return curEdge; 4.745 - } 4.746 - 4.747 - if (edges.contains(e)) { 4.748 - for (Edge e1 : edges) { 4.749 - if (e.equals(e1)) { 4.750 - curEdge = e1; 4.751 - } 4.752 - } 4.753 - } else { 4.754 - edges.add(e); 4.755 - curEdge = e; 4.756 - } 4.757 - return curEdge; 4.758 - } 4.759 - 4.760 - Edge findEdge(T origin, T target) { 4.761 - for (Edge e : edges) { 4.762 - if (e.from.equals(origin) && e.to.equals(target)) { 4.763 - return e; 4.764 - } 4.765 - } 4.766 - return null; 4.767 - } 4.768 - 4.769 - String getLabel(Edge e) { 4.770 - String label = e.label.toString(); 4.771 - return label.isEmpty() ? "" : String.format("[label=\"%s\",fontsize=9]", label); 4.772 - } 4.773 - 4.774 - class Edge { 4.775 - final T from; 4.776 - final T to; 4.777 - final String tag; // optional tag 4.778 - final StringBuilder label = new StringBuilder(); 4.779 - Edge(T from, T to, String tag) { 4.780 - this.from = from; 4.781 - this.to = to; 4.782 - this.tag = tag; 4.783 - } 4.784 - void addLabel(String s) { 4.785 - label.append(s).append("\\n"); 4.786 - } 4.787 - void addLabel(String origin, String target, String profile) { 4.788 - label.append(origin).append(" -> ").append(target); 4.789 - if (!profile.isEmpty()) { 4.790 - label.append(" (" + profile + ")"); 4.791 - } 4.792 - label.append("\\n"); 4.793 - } 4.794 - @Override @SuppressWarnings("unchecked") 4.795 - public boolean equals(Object o) { 4.796 - if (o instanceof DotGraph<?>.Edge) { 4.797 - DotGraph<?>.Edge e = (DotGraph<?>.Edge)o; 4.798 - return this.from.equals(e.from) && 4.799 - this.to.equals(e.to) && 4.800 - this.tag.equals(e.tag); 4.801 - } 4.802 - return false; 4.803 - } 4.804 - @Override 4.805 - public int hashCode() { 4.806 - int hash = 7; 4.807 - hash = 67 * hash + Objects.hashCode(this.from) + 4.808 - Objects.hashCode(this.to) + Objects.hashCode(this.tag); 4.809 - return hash; 4.810 - } 4.811 - } 4.812 + return Profile.getProfile(pn); 4.813 } 4.814 }
5.1 --- a/src/share/classes/com/sun/tools/jdeps/Main.java Mon Jul 07 18:03:08 2014 -0700 5.2 +++ b/src/share/classes/com/sun/tools/jdeps/Main.java Thu Jul 17 15:23:08 2014 -0700 5.3 @@ -1,5 +1,5 @@ 5.4 /* 5.5 - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. 5.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 5.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.8 * 5.9 * This code is free software; you can redistribute it and/or modify it 5.10 @@ -63,4 +63,3 @@ 5.11 return t.run(args); 5.12 } 5.13 } 5.14 -
6.1 --- a/src/share/classes/com/sun/tools/jdeps/PlatformClassPath.java Mon Jul 07 18:03:08 2014 -0700 6.2 +++ b/src/share/classes/com/sun/tools/jdeps/PlatformClassPath.java Thu Jul 17 15:23:08 2014 -0700 6.3 @@ -1,5 +1,5 @@ 6.4 /* 6.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 6.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 6.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6.8 * 6.9 * This code is free software; you can redistribute it and/or modify it 6.10 @@ -24,6 +24,12 @@ 6.11 */ 6.12 package com.sun.tools.jdeps; 6.13 6.14 +import com.sun.tools.classfile.Annotation; 6.15 +import com.sun.tools.classfile.ClassFile; 6.16 +import com.sun.tools.classfile.ConstantPool; 6.17 +import com.sun.tools.classfile.ConstantPoolException; 6.18 +import com.sun.tools.classfile.RuntimeAnnotations_attribute; 6.19 +import com.sun.tools.classfile.Dependencies.ClassFileError; 6.20 import java.io.IOException; 6.21 import java.nio.file.FileVisitResult; 6.22 import java.nio.file.Files; 6.23 @@ -33,11 +39,15 @@ 6.24 import java.nio.file.attribute.BasicFileAttributes; 6.25 import java.util.*; 6.26 6.27 +import static com.sun.tools.classfile.Attribute.*; 6.28 + 6.29 /** 6.30 * ClassPath for Java SE and JDK 6.31 */ 6.32 class PlatformClassPath { 6.33 - private final static List<Archive> javaHomeArchives = init(); 6.34 + private static final List<String> NON_PLATFORM_JARFILES = 6.35 + Arrays.asList("alt-rt.jar", "jfxrt.jar", "ant-javafx.jar", "javafx-mx.jar"); 6.36 + private static final List<Archive> javaHomeArchives = init(); 6.37 6.38 static List<Archive> getArchives() { 6.39 return javaHomeArchives; 6.40 @@ -50,12 +60,19 @@ 6.41 if (home.endsWith("jre")) { 6.42 // jar files in <javahome>/jre/lib 6.43 result.addAll(addJarFiles(home.resolve("lib"))); 6.44 + if (home.getParent() != null) { 6.45 + // add tools.jar and other JDK jar files 6.46 + Path lib = home.getParent().resolve("lib"); 6.47 + if (Files.exists(lib)) { 6.48 + result.addAll(addJarFiles(lib)); 6.49 + } 6.50 + } 6.51 } else if (Files.exists(home.resolve("lib"))) { 6.52 // either a JRE or a jdk build image 6.53 Path classes = home.resolve("classes"); 6.54 if (Files.isDirectory(classes)) { 6.55 // jdk build outputdir 6.56 - result.add(new JDKArchive(classes, ClassFileReader.newInstance(classes))); 6.57 + result.add(new JDKArchive(classes)); 6.58 } 6.59 // add other JAR files 6.60 result.addAll(addJarFiles(home.resolve("lib"))); 6.61 @@ -91,9 +108,9 @@ 6.62 if (fn.endsWith(".jar")) { 6.63 // JDK may cobundle with JavaFX that doesn't belong to any profile 6.64 // Treat jfxrt.jar as regular Archive 6.65 - result.add(fn.equals("jfxrt.jar") 6.66 - ? new Archive(p, ClassFileReader.newInstance(p)) 6.67 - : new JDKArchive(p, ClassFileReader.newInstance(p))); 6.68 + result.add(NON_PLATFORM_JARFILES.contains(fn) 6.69 + ? Archive.getInstance(p) 6.70 + : new JDKArchive(p)); 6.71 } 6.72 return FileVisitResult.CONTINUE; 6.73 } 6.74 @@ -106,8 +123,91 @@ 6.75 * or implementation classes (i.e. JDK internal API) 6.76 */ 6.77 static class JDKArchive extends Archive { 6.78 - JDKArchive(Path p, ClassFileReader reader) { 6.79 - super(p, reader); 6.80 + private static List<String> PROFILE_JARS = Arrays.asList("rt.jar", "jce.jar"); 6.81 + public static boolean isProfileArchive(Archive archive) { 6.82 + if (archive instanceof JDKArchive) { 6.83 + return PROFILE_JARS.contains(archive.getName()); 6.84 + } 6.85 + return false; 6.86 + } 6.87 + 6.88 + private final Map<String,Boolean> exportedPackages = new HashMap<>(); 6.89 + private final Map<String,Boolean> exportedTypes = new HashMap<>(); 6.90 + JDKArchive(Path p) throws IOException { 6.91 + super(p, ClassFileReader.newInstance(p)); 6.92 + } 6.93 + 6.94 + /** 6.95 + * Tests if a given fully-qualified name is an exported type. 6.96 + */ 6.97 + public boolean isExported(String cn) { 6.98 + int i = cn.lastIndexOf('.'); 6.99 + String pn = i > 0 ? cn.substring(0, i) : ""; 6.100 + 6.101 + boolean isJdkExported = isExportedPackage(pn); 6.102 + if (exportedTypes.containsKey(cn)) { 6.103 + return exportedTypes.get(cn); 6.104 + } 6.105 + return isJdkExported; 6.106 + } 6.107 + 6.108 + /** 6.109 + * Tests if a given package name is exported. 6.110 + */ 6.111 + public boolean isExportedPackage(String pn) { 6.112 + if (Profile.getProfile(pn) != null) { 6.113 + return true; 6.114 + } 6.115 + return exportedPackages.containsKey(pn) ? exportedPackages.get(pn) : false; 6.116 + } 6.117 + 6.118 + private static final String JDK_EXPORTED_ANNOTATION = "Ljdk/Exported;"; 6.119 + private Boolean isJdkExported(ClassFile cf) throws ConstantPoolException { 6.120 + RuntimeAnnotations_attribute attr = (RuntimeAnnotations_attribute) 6.121 + cf.attributes.get(RuntimeVisibleAnnotations); 6.122 + if (attr != null) { 6.123 + for (int i = 0; i < attr.annotations.length; i++) { 6.124 + Annotation ann = attr.annotations[i]; 6.125 + String annType = cf.constant_pool.getUTF8Value(ann.type_index); 6.126 + if (JDK_EXPORTED_ANNOTATION.equals(annType)) { 6.127 + boolean isJdkExported = true; 6.128 + for (int j = 0; j < ann.num_element_value_pairs; j++) { 6.129 + Annotation.element_value_pair pair = ann.element_value_pairs[j]; 6.130 + Annotation.Primitive_element_value ev = (Annotation.Primitive_element_value) pair.value; 6.131 + ConstantPool.CONSTANT_Integer_info info = (ConstantPool.CONSTANT_Integer_info) 6.132 + cf.constant_pool.get(ev.const_value_index); 6.133 + isJdkExported = info.value != 0; 6.134 + } 6.135 + return Boolean.valueOf(isJdkExported); 6.136 + } 6.137 + } 6.138 + } 6.139 + return null; 6.140 + } 6.141 + 6.142 + void processJdkExported(ClassFile cf) throws IOException { 6.143 + try { 6.144 + String cn = cf.getName(); 6.145 + String pn = cn.substring(0, cn.lastIndexOf('/')).replace('/', '.'); 6.146 + 6.147 + Boolean b = isJdkExported(cf); 6.148 + if (b != null) { 6.149 + exportedTypes.put(cn.replace('/', '.'), b); 6.150 + } 6.151 + if (!exportedPackages.containsKey(pn)) { 6.152 + // check if package-info.class has @jdk.Exported 6.153 + Boolean isJdkExported = null; 6.154 + ClassFile pcf = reader().getClassFile(cn.substring(0, cn.lastIndexOf('/')+1) + "package-info"); 6.155 + if (pcf != null) { 6.156 + isJdkExported = isJdkExported(pcf); 6.157 + } 6.158 + if (isJdkExported != null) { 6.159 + exportedPackages.put(pn, isJdkExported); 6.160 + } 6.161 + } 6.162 + } catch (ConstantPoolException e) { 6.163 + throw new ClassFileError(e); 6.164 + } 6.165 } 6.166 } 6.167 }
7.1 --- a/src/share/classes/com/sun/tools/jdeps/Profile.java Mon Jul 07 18:03:08 2014 -0700 7.2 +++ b/src/share/classes/com/sun/tools/jdeps/Profile.java Thu Jul 17 15:23:08 2014 -0700 7.3 @@ -1,5 +1,5 @@ 7.4 /* 7.5 - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 7.6 + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 7.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 7.8 * 7.9 * This code is free software; you can redistribute it and/or modify it 7.10 @@ -43,7 +43,6 @@ 7.11 * Build the profile information from ct.sym if exists. 7.12 */ 7.13 enum Profile { 7.14 - 7.15 COMPACT1("compact1", 1), 7.16 COMPACT2("compact2", 2), 7.17 COMPACT3("compact3", 3), 7.18 @@ -61,8 +60,7 @@ 7.19 this.proprietaryPkgs = new HashSet<>(); 7.20 } 7.21 7.22 - @Override 7.23 - public String toString() { 7.24 + public String profileName() { 7.25 return name; 7.26 } 7.27 7.28 @@ -77,7 +75,7 @@ 7.29 public static Profile getProfile(String pn) { 7.30 Profile profile = PackageToProfile.map.get(pn); 7.31 return (profile != null && profile.packages.contains(pn)) 7.32 - ? profile : null; 7.33 + ? profile : null; 7.34 } 7.35 7.36 static class PackageToProfile {
8.1 --- a/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties Mon Jul 07 18:03:08 2014 -0700 8.2 +++ b/src/share/classes/com/sun/tools/jdeps/resources/jdeps.properties Thu Jul 17 15:23:08 2014 -0700 8.3 @@ -1,6 +1,6 @@ 8.4 main.usage.summary=\ 8.5 Usage: {0} <options> <classes...>\n\ 8.6 -use -h, -? or --help for a list of possible options 8.7 +use -h, -? or -help for a list of possible options 8.8 8.9 main.usage=\ 8.10 Usage: {0} <options> <classes...>\n\ 8.11 @@ -18,20 +18,29 @@ 8.12 8.13 main.opt.v=\ 8.14 \ -v -verbose Print all class level dependencies\n\ 8.15 +\ Equivalent to -verbose:class -filter:none.\n\ 8.16 \ -verbose:package Print package-level dependencies excluding\n\ 8.17 -\ dependencies within the same archive\n\ 8.18 +\ dependencies within the same package by default\n\ 8.19 \ -verbose:class Print class-level dependencies excluding\n\ 8.20 -\ dependencies within the same archive 8.21 +\ dependencies within the same package by default 8.22 + 8.23 +main.opt.f=\ 8.24 +\ -f <regex> -filter <regex> Filter dependences matching the given pattern\n\ 8.25 +\ If given multiple times, the last one will be used.\n\ 8.26 +\ -filter:package Filter dependences within the same package (default)\n\ 8.27 +\ -filter:archive Filter dependences within the same archive\n\ 8.28 +\ -filter:none No -filter:package and -filter:archive filtering\n\ 8.29 +\ Filtering specified via the -filter option still applies. 8.30 8.31 main.opt.s=\ 8.32 \ -s -summary Print dependency summary only 8.33 8.34 main.opt.p=\ 8.35 -\ -p <pkgname> -package <pkgname> Finds dependences in the given package\n\ 8.36 +\ -p <pkgname> -package <pkgname> Finds dependences matching the given package name\n\ 8.37 \ (may be given multiple times) 8.38 8.39 main.opt.e=\ 8.40 -\ -e <regex> -regex <regex> Finds dependences in packages matching pattern\n\ 8.41 +\ -e <regex> -regex <regex> Finds dependences matching the given pattern\n\ 8.42 \ (-p and -e are exclusive) 8.43 8.44 main.opt.include=\ 8.45 @@ -47,7 +56,10 @@ 8.46 \ -cp <path> -classpath <path> Specify where to find class files 8.47 8.48 main.opt.R=\ 8.49 -\ -R -recursive Recursively traverse all dependencies 8.50 +\ -R -recursive Recursively traverse all dependencies.\n\ 8.51 +\ The -R option implies -filter:none. If -p, -e, -f\n\ 8.52 +\ option is specified, only the matching dependences\n\ 8.53 +\ are analyzed. 8.54 8.55 main.opt.apionly=\ 8.56 \ -apionly Restrict analysis to APIs i.e. dependences\n\ 8.57 @@ -74,12 +86,11 @@ 8.58 8.59 err.unknown.option=unknown option: {0} 8.60 err.missing.arg=no value given for {0} 8.61 -err.internal.error=internal error: {0} {1} {2} 8.62 err.invalid.arg.for.option=invalid argument for option: {0} 8.63 err.option.after.class=option must be specified before classes: {0} 8.64 err.option.unsupported={0} not supported: {1} 8.65 err.profiles.msg=No profile information 8.66 -err.dot.output.path=invalid path: {0} 8.67 +err.invalid.path=invalid path: {0} 8.68 warn.invalid.arg=Invalid classname or pathname not exist: {0} 8.69 warn.split.package=package {0} defined in {1} {2} 8.70
9.1 --- a/test/tools/jdeps/APIDeps.java Mon Jul 07 18:03:08 2014 -0700 9.2 +++ b/test/tools/jdeps/APIDeps.java Thu Jul 17 15:23:08 2014 -0700 9.3 @@ -1,5 +1,5 @@ 9.4 /* 9.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 9.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 9.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 9.8 * 9.9 * This code is free software; you can redistribute it and/or modify it 9.10 @@ -23,7 +23,7 @@ 9.11 9.12 /* 9.13 * @test 9.14 - * @bug 8015912 8029216 9.15 + * @bug 8015912 8029216 8048063 9.16 * @summary Test -apionly and -jdkinternals options 9.17 * @build m.Bar m.Foo m.Gee b.B c.C c.I d.D e.E f.F g.G 9.18 * @run main APIDeps 9.19 @@ -81,27 +81,39 @@ 9.20 new String[] {"compact1", "compact3", testDirBasename}, 9.21 new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"}); 9.22 test(new File(mDir, "Foo.class"), 9.23 + new String[] {"c.I", "e.E", "f.F"}, 9.24 + new String[] {testDirBasename}, 9.25 + new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-P"}); 9.26 + test(new File(mDir, "Foo.class"), 9.27 new String[] {"c.I", "e.E", "f.F", "m.Bar"}, 9.28 new String[] {testDirBasename}, 9.29 + new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-filter:none", "-P"}); 9.30 + test(new File(mDir, "Gee.class"), 9.31 + new String[] {"g.G", "sun.misc.Lock", "com.sun.tools.classfile.ClassFile", 9.32 + "com.sun.management.ThreadMXBean", "com.sun.source.tree.BinaryTree"}, 9.33 + new String[] {testDirBasename, "JDK internal API", "compact3", ""}, 9.34 new String[] {"-classpath", testDir.getPath(), "-verbose", "-P"}); 9.35 - test(new File(mDir, "Gee.class"), 9.36 - new String[] {"g.G", "sun.misc.Lock"}, 9.37 - new String[] {testDirBasename, "JDK internal API"}, 9.38 - new String[] {"-classpath", testDir.getPath(), "-verbose"}); 9.39 9.40 // -jdkinternals 9.41 test(new File(mDir, "Gee.class"), 9.42 - new String[] {"sun.misc.Lock"}, 9.43 + new String[] {"sun.misc.Lock", "com.sun.tools.classfile.ClassFile"}, 9.44 new String[] {"JDK internal API"}, 9.45 new String[] {"-jdkinternals"}); 9.46 // -jdkinternals parses all classes on -classpath and the input arguments 9.47 test(new File(mDir, "Gee.class"), 9.48 - new String[] {"sun.misc.Lock", "sun.misc.Unsafe"}, 9.49 + new String[] {"com.sun.tools.jdeps.Main", "com.sun.tools.classfile.ClassFile", 9.50 + "sun.misc.Lock", "sun.misc.Unsafe"}, 9.51 new String[] {"JDK internal API"}, 9.52 new String[] {"-classpath", testDir.getPath(), "-jdkinternals"}); 9.53 9.54 // parse only APIs 9.55 - // parse only APIs 9.56 + test(mDir, 9.57 + new String[] {"java.lang.Object", "java.lang.String", 9.58 + "java.util.Set", 9.59 + "c.C", "d.D", "c.I", "e.E"}, 9.60 + new String[] {"compact1", testDirBasename}, 9.61 + new String[] {"-classpath", testDir.getPath(), "-verbose:class", "-P", "-apionly"}); 9.62 + 9.63 test(mDir, 9.64 new String[] {"java.lang.Object", "java.lang.String", 9.65 "java.util.Set",
10.1 --- a/test/tools/jdeps/Basic.java Mon Jul 07 18:03:08 2014 -0700 10.2 +++ b/test/tools/jdeps/Basic.java Thu Jul 17 15:23:08 2014 -0700 10.3 @@ -1,5 +1,5 @@ 10.4 /* 10.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 10.6 + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 10.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 10.8 * 10.9 * This code is free software; you can redistribute it and/or modify it 10.10 @@ -23,7 +23,7 @@ 10.11 10.12 /* 10.13 * @test 10.14 - * @bug 8003562 8005428 8015912 8027481 10.15 + * @bug 8003562 8005428 8015912 8027481 8048063 10.16 * @summary Basic tests for jdeps tool 10.17 * @build Test p.Foo p.Bar javax.activity.NotCompactProfile 10.18 * @run main Basic 10.19 @@ -86,6 +86,16 @@ 10.20 new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"}, 10.21 new String[] {"compact1", "compact1", "not found", "not found"}, 10.22 new String[] {"-verbose:class"}); 10.23 + // test -filter:none option 10.24 + test(new File(testDir, "p"), 10.25 + new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto", "p"}, 10.26 + new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1", "p"}, 10.27 + new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:none"}); 10.28 + // test -filter:archive option 10.29 + test(new File(testDir, "p"), 10.30 + new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"}, 10.31 + new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"}, 10.32 + new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:archive"}); 10.33 // test -p option 10.34 test(new File(testDir, "Test.class"), 10.35 new String[] {"p.Foo", "p.Bar"}, 10.36 @@ -100,11 +110,12 @@ 10.37 new String[] {"java.lang"}, 10.38 new String[] {"compact1"}, 10.39 new String[] {"-verbose:package", "-e", "java\\.lang\\..*"}); 10.40 + 10.41 // test -classpath and -include options 10.42 test(null, 10.43 - new String[] {"java.lang", "java.util", 10.44 - "java.lang.management", "javax.crypto"}, 10.45 - new String[] {"compact1", "compact1", "compact3", "compact1"}, 10.46 + new String[] {"java.lang", "java.util", "java.lang.management", 10.47 + "javax.activity", "javax.crypto"}, 10.48 + new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"}, 10.49 new String[] {"-classpath", testDir.getPath(), "-include", "p.+|Test.class"}); 10.50 test(new File(testDir, "Test.class"), 10.51 new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"},
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 11.2 +++ b/test/tools/jdeps/DotFileTest.java Thu Jul 17 15:23:08 2014 -0700 11.3 @@ -0,0 +1,272 @@ 11.4 +/* 11.5 + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 11.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 11.7 + * 11.8 + * This code is free software; you can redistribute it and/or modify it 11.9 + * under the terms of the GNU General Public License version 2 only, as 11.10 + * published by the Free Software Foundation. 11.11 + * 11.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 11.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 11.15 + * version 2 for more details (a copy is included in the LICENSE file that 11.16 + * accompanied this code). 11.17 + * 11.18 + * You should have received a copy of the GNU General Public License version 11.19 + * 2 along with this work; if not, write to the Free Software Foundation, 11.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 11.21 + * 11.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 11.23 + * or visit www.oracle.com if you need additional information or have any 11.24 + * questions. 11.25 + */ 11.26 + 11.27 +/* 11.28 + * @test 11.29 + * @bug 8003562 11.30 + * @summary Basic tests for jdeps -dotoutput option 11.31 + * @build Test p.Foo p.Bar javax.activity.NotCompactProfile 11.32 + * @run main DotFileTest 11.33 + */ 11.34 + 11.35 +import java.io.File; 11.36 +import java.io.IOException; 11.37 +import java.io.PrintWriter; 11.38 +import java.io.StringWriter; 11.39 +import java.nio.file.DirectoryStream; 11.40 +import java.nio.file.Files; 11.41 +import java.nio.file.Path; 11.42 +import java.nio.file.Paths; 11.43 +import java.util.*; 11.44 +import java.util.regex.*; 11.45 + 11.46 +public class DotFileTest { 11.47 + private static boolean symbolFileExist = initProfiles(); 11.48 + private static boolean initProfiles() { 11.49 + // check if ct.sym exists; if not use the profiles.properties file 11.50 + Path home = Paths.get(System.getProperty("java.home")); 11.51 + if (home.endsWith("jre")) { 11.52 + home = home.getParent(); 11.53 + } 11.54 + Path ctsym = home.resolve("lib").resolve("ct.sym"); 11.55 + boolean symbolExists = ctsym.toFile().exists(); 11.56 + if (!symbolExists) { 11.57 + Path testSrcProfiles = 11.58 + Paths.get(System.getProperty("test.src", "."), "profiles.properties"); 11.59 + if (!testSrcProfiles.toFile().exists()) 11.60 + throw new Error(testSrcProfiles + " does not exist"); 11.61 + System.out.format("%s doesn't exist.%nUse %s to initialize profiles info%n", 11.62 + ctsym, testSrcProfiles); 11.63 + System.setProperty("jdeps.profiles", testSrcProfiles.toString()); 11.64 + } 11.65 + return symbolExists; 11.66 + } 11.67 + 11.68 + public static void main(String... args) throws Exception { 11.69 + int errors = 0; 11.70 + errors += new DotFileTest().run(); 11.71 + if (errors > 0) 11.72 + throw new Exception(errors + " errors found"); 11.73 + } 11.74 + 11.75 + final Path dir; 11.76 + final Path dotoutput; 11.77 + DotFileTest() { 11.78 + this.dir = Paths.get(System.getProperty("test.classes", ".")); 11.79 + this.dotoutput = dir.resolve("dots"); 11.80 + } 11.81 + 11.82 + int run() throws IOException { 11.83 + File testDir = dir.toFile(); 11.84 + // test a .class file 11.85 + test(new File(testDir, "Test.class"), 11.86 + new String[] {"java.lang", "p"}, 11.87 + new String[] {"compact1", "not found"}); 11.88 + // test a directory 11.89 + // also test non-SE javax.activity class dependency 11.90 + test(new File(testDir, "p"), 11.91 + new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"}, 11.92 + new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"}, 11.93 + new String[] {"-classpath", testDir.getPath()}); 11.94 + // test class-level dependency output 11.95 + test(new File(testDir, "Test.class"), 11.96 + new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"}, 11.97 + new String[] {"compact1", "compact1", "not found", "not found"}, 11.98 + new String[] {"-verbose:class"}); 11.99 + // test -filter:none option 11.100 + test(new File(testDir, "p"), 11.101 + new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto", "p"}, 11.102 + new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1", "p"}, 11.103 + new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:none"}); 11.104 + // test -filter:archive option 11.105 + test(new File(testDir, "p"), 11.106 + new String[] {"java.lang", "java.util", "java.lang.management", "javax.activity", "javax.crypto"}, 11.107 + new String[] {"compact1", "compact1", "compact3", testDir.getName(), "compact1"}, 11.108 + new String[] {"-classpath", testDir.getPath(), "-verbose:package", "-filter:archive"}); 11.109 + // test -p option 11.110 + test(new File(testDir, "Test.class"), 11.111 + new String[] {"p.Foo", "p.Bar"}, 11.112 + new String[] {"not found", "not found"}, 11.113 + new String[] {"-verbose:class", "-p", "p"}); 11.114 + // test -e option 11.115 + test(new File(testDir, "Test.class"), 11.116 + new String[] {"p.Foo", "p.Bar"}, 11.117 + new String[] {"not found", "not found"}, 11.118 + new String[] {"-verbose:class", "-e", "p\\..*"}); 11.119 + test(new File(testDir, "Test.class"), 11.120 + new String[] {"java.lang"}, 11.121 + new String[] {"compact1"}, 11.122 + new String[] {"-verbose:package", "-e", "java\\.lang\\..*"}); 11.123 + // test -classpath options 11.124 + test(new File(testDir, "Test.class"), 11.125 + new String[] {"java.lang.Object", "java.lang.String", "p.Foo", "p.Bar"}, 11.126 + new String[] {"compact1", "compact1", testDir.getName(), testDir.getName()}, 11.127 + new String[] {"-v", "-classpath", testDir.getPath()}); 11.128 + 11.129 + testSummary(new File(testDir, "Test.class"), 11.130 + new String[] {"rt.jar", testDir.getName()}, 11.131 + new String[] {"compact1", ""}, 11.132 + new String[] {"-classpath", testDir.getPath()}); 11.133 + testSummary(new File(testDir, "Test.class"), 11.134 + new String[] {"java.lang", "p"}, 11.135 + new String[] {"compact1", testDir.getName()}, 11.136 + new String[] {"-v", "-classpath", testDir.getPath()}); 11.137 + return errors; 11.138 + } 11.139 + 11.140 + void test(File file, String[] expect, String[] profiles) throws IOException { 11.141 + test(file, expect, profiles, new String[0]); 11.142 + } 11.143 + 11.144 + void test(File file, String[] expect, String[] profiles, String[] options) 11.145 + throws IOException 11.146 + { 11.147 + Path dotfile = dotoutput.resolve(file.toPath().getFileName().toString() + ".dot"); 11.148 + 11.149 + List<String> args = new ArrayList<>(Arrays.asList(options)); 11.150 + args.add("-dotoutput"); 11.151 + args.add(dotoutput.toString()); 11.152 + if (file != null) { 11.153 + args.add(file.getPath()); 11.154 + } 11.155 + 11.156 + Map<String,String> result = jdeps(args, dotfile); 11.157 + checkResult("dependencies", expect, result.keySet()); 11.158 + 11.159 + // with -P option 11.160 + List<String> argsWithDashP = new ArrayList<>(); 11.161 + argsWithDashP.add("-dotoutput"); 11.162 + argsWithDashP.add(dotoutput.toString()); 11.163 + argsWithDashP.add("-P"); 11.164 + argsWithDashP.addAll(args); 11.165 + 11.166 + result = jdeps(argsWithDashP, dotfile); 11.167 + checkResult("profiles", expect, profiles, result); 11.168 + } 11.169 + 11.170 + void testSummary(File file, String[] expect, String[] profiles, String[] options) 11.171 + throws IOException 11.172 + { 11.173 + Path dotfile = dotoutput.resolve("summary.dot"); 11.174 + 11.175 + List<String> args = new ArrayList<>(Arrays.asList(options)); 11.176 + args.add("-dotoutput"); 11.177 + args.add(dotoutput.toString()); 11.178 + if (file != null) { 11.179 + args.add(file.getPath()); 11.180 + } 11.181 + 11.182 + Map<String,String> result = jdeps(args, dotfile); 11.183 + checkResult("dependencies", expect, result.keySet()); 11.184 + 11.185 + // with -P option 11.186 + List<String> argsWithDashP = new ArrayList<>(); 11.187 + argsWithDashP.add("-dotoutput"); 11.188 + argsWithDashP.add(dotoutput.toString()); 11.189 + argsWithDashP.add("-P"); 11.190 + argsWithDashP.addAll(args); 11.191 + 11.192 + result = jdeps(argsWithDashP, dotfile); 11.193 + checkResult("profiles", expect, profiles, result); 11.194 + } 11.195 + 11.196 + Map<String,String> jdeps(List<String> args, Path dotfile) throws IOException { 11.197 + if (Files.exists(dotoutput)) { 11.198 + try (DirectoryStream<Path> stream = Files.newDirectoryStream(dotoutput)) { 11.199 + for (Path p : stream) { 11.200 + Files.delete(p); 11.201 + } 11.202 + } 11.203 + Files.delete(dotoutput); 11.204 + } 11.205 + // invoke jdeps 11.206 + StringWriter sw = new StringWriter(); 11.207 + PrintWriter pw = new PrintWriter(sw); 11.208 + System.err.println("jdeps " + args); 11.209 + int rc = com.sun.tools.jdeps.Main.run(args.toArray(new String[0]), pw); 11.210 + pw.close(); 11.211 + String out = sw.toString(); 11.212 + if (!out.isEmpty()) 11.213 + System.err.println(out); 11.214 + if (rc != 0) 11.215 + throw new Error("jdeps failed: rc=" + rc); 11.216 + 11.217 + // check output files 11.218 + if (Files.notExists(dotfile)) { 11.219 + throw new RuntimeException(dotfile + " doesn't exist"); 11.220 + } 11.221 + return parse(dotfile); 11.222 + } 11.223 + private static Pattern pattern = Pattern.compile("(.*) -> +([^ ]*) (.*)"); 11.224 + private Map<String,String> parse(Path outfile) throws IOException { 11.225 + Map<String,String> result = new LinkedHashMap<>(); 11.226 + for (String line : Files.readAllLines(outfile)) { 11.227 + line = line.replace('"', ' ').replace(';', ' '); 11.228 + Matcher pm = pattern.matcher(line); 11.229 + if (pm.find()) { 11.230 + String origin = pm.group(1).trim(); 11.231 + String target = pm.group(2).trim(); 11.232 + String module = pm.group(3).replace('(', ' ').replace(')', ' ').trim(); 11.233 + result.put(target, module); 11.234 + } 11.235 + } 11.236 + return result; 11.237 + } 11.238 + 11.239 + void checkResult(String label, String[] expect, Collection<String> found) { 11.240 + List<String> list = Arrays.asList(expect); 11.241 + if (!isEqual(list, found)) 11.242 + error("Unexpected " + label + " found: '" + found + "', expected: '" + list + "'"); 11.243 + } 11.244 + 11.245 + void checkResult(String label, String[] expect, String[] profiles, Map<String,String> result) { 11.246 + if (expect.length != profiles.length) 11.247 + error("Invalid expected names and profiles"); 11.248 + 11.249 + // check the dependencies 11.250 + checkResult(label, expect, result.keySet()); 11.251 + // check profile information 11.252 + checkResult(label, profiles, result.values()); 11.253 + for (int i=0; i < expect.length; i++) { 11.254 + String profile = result.get(expect[i]); 11.255 + if (!profile.equals(profiles[i])) 11.256 + error("Unexpected profile: '" + profile + "', expected: '" + profiles[i] + "'"); 11.257 + } 11.258 + } 11.259 + 11.260 + boolean isEqual(List<String> expected, Collection<String> found) { 11.261 + if (expected.size() != found.size()) 11.262 + return false; 11.263 + 11.264 + List<String> list = new ArrayList<>(found); 11.265 + list.removeAll(expected); 11.266 + return list.isEmpty(); 11.267 + } 11.268 + 11.269 + void error(String msg) { 11.270 + System.err.println("Error: " + msg); 11.271 + errors++; 11.272 + } 11.273 + 11.274 + int errors; 11.275 +}
12.1 --- a/test/tools/jdeps/m/Gee.java Mon Jul 07 18:03:08 2014 -0700 12.2 +++ b/test/tools/jdeps/m/Gee.java Thu Jul 17 15:23:08 2014 -0700 12.3 @@ -1,5 +1,5 @@ 12.4 /* 12.5 - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 12.6 + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 12.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 12.8 * 12.9 * This code is free software; you can redistribute it and/or modify it 12.10 @@ -26,5 +26,7 @@ 12.11 12.12 class Gee extends g.G { 12.13 public sun.misc.Lock lock; 12.14 + public com.sun.tools.classfile.ClassFile cf; // @jdk.Exported(false) 12.15 + public com.sun.source.tree.BinaryTree tree; // @jdk.Exported 12.16 + public com.sun.management.ThreadMXBean mxbean; // @jdk.Exported on package-info 12.17 } 12.18 -
13.1 --- a/test/tools/jdeps/p/Bar.java Mon Jul 07 18:03:08 2014 -0700 13.2 +++ b/test/tools/jdeps/p/Bar.java Thu Jul 17 15:23:08 2014 -0700 13.3 @@ -1,5 +1,5 @@ 13.4 /* 13.5 - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. 13.6 + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. 13.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 13.8 * 13.9 * This code is free software; you can redistribute it and/or modify it 13.10 @@ -30,4 +30,8 @@ 13.11 public javax.crypto.Cipher getCiper() { 13.12 return null; 13.13 } 13.14 + 13.15 + public Foo foo() { 13.16 + return new Foo(); 13.17 + } 13.18 }