@Override public void visit(Location o, Location t) { Archive targetArchive = findArchive(t); if (filter.accepts(o, archive, t, targetArchive)) { addDep(o, t); if (!requires.contains(targetArchive)) { requires.add(targetArchive); } } if (targetArchive instanceof JDKArchive) { Profile p = Profile.getProfile(t.getPackageName()); if (profile == null || (p != null && p.compareTo(profile) > 0)) { profile = p; } } }
protected Dep addDep(Location o, Location t) { String origin = getLocationName(o); String target = getLocationName(t); Archive targetArchive = findArchive(t); if (curDep != null && curDep.origin().equals(origin) && curDep.originArchive() == archive && curDep.target().equals(target) && curDep.targetArchive() == targetArchive) { return curDep; } Dep e = new Dep(origin, archive, target, targetArchive); if (deps.contains(e)) { for (Dep e1 : deps) { if (e.equals(e1)) { curDep = e1; } } } else { deps.add(e); curDep = e; } return curDep; }
private void transitiveArchiveDeps(int depth) throws IOException { Stream<Location> deps = archives.stream() .flatMap(Archive::getDependencies); // start with the unresolved archives Set<Archive> unresolved = unresolvedArchives(deps); do { // parse all unresolved archives Set<Location> targets = apiOnly ? finder.parseExportedAPIs(unresolved.stream()) : finder.parse(unresolved.stream()); archives.addAll(unresolved); // Add dependencies to the next batch for analysis unresolved = unresolvedArchives(targets.stream()); } while (!unresolved.isEmpty() && depth-- > 0); }
Archive findArchive(Location t) { // local in this archive if (archive.getClasses().contains(t)) return archive; Archive target; if (locationToArchive.containsKey(t)) { target = locationToArchive.get(t); } else { // special case JDK removed API target = configuration.findClass(t) .orElseGet(() -> REMOVED_JDK_INTERNALS.contains(t) ? REMOVED_JDK_INTERNALS : NOT_FOUND); } return locationToArchive.computeIfAbsent(t, _k -> target); }
@Override public void visit(Location o, Location t) { Archive targetArchive = findArchive(t); if (filter.accepts(o, archive, t, targetArchive)) { addDep(o, t); if (archive != targetArchive && !requires.contains(targetArchive)) { requires.add(targetArchive); } } if (targetArchive.getModule().isNamed()) { Profile p = Profile.getProfile(t.getPackageName()); if (profile == null || (p != null && p.compareTo(profile) > 0)) { profile = p; } } }
private Set<Location> waitForTasksCompleted() { try { Set<Location> targets = new HashSet<>(); FutureTask<Set<Location>> task; while ((task = tasks.poll()) != null) { // wait for completion if (!task.isDone()) targets.addAll(task.get()); } return targets; } catch (InterruptedException|ExecutionException e) { throw new Error(e); } }
@Override public void visit(Location o, Location t) { Archive targetArchive = findArchive(t); if (filter.accepts(o, archive, t, targetArchive)) { addDep(o, t); if (archive != targetArchive && !requires.contains(targetArchive)) { requires.add(targetArchive); } } if (targetArchive instanceof JDKArchive) { Profile p = Profile.getProfile(t.getPackageName()); if (profile == null || (p != null && p.compareTo(profile) > 0)) { profile = p; } } }
public void addClass(Location origin) { Set<Location> set = deps.get(origin); if (set == null) { set = new HashSet<>(); deps.put(origin, set); } }
public void addClass(Location origin, Location target) { Set<Location> set = deps.get(origin); if (set == null) { set = new HashSet<>(); deps.put(origin, set); } set.add(target); }
public void visitDependences(Visitor v) { for (Map.Entry<Location,Set<Location>> e: deps.entrySet()) { for (Location target : e.getValue()) { v.visit(e.getKey(), target); } } }
private boolean run() throws IOException { // parse classfiles and find all dependencies findDependencies(); Analyzer analyzer = new Analyzer(options.verbose, new Analyzer.Filter() { @Override public boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive) { if (options.findJDKInternals) { // accepts target that is JDK class but not exported return isJDKArchive(targetArchive) && !((JDKArchive) targetArchive).isExported(target.getClassName()); } else if (options.filterSameArchive) { // accepts origin and target that from different archive return originArchive != targetArchive; } return true; } }); // analyze the dependencies analyzer.run(sourceLocations); // output result if (options.dotOutputDir != null) { Path dir = Paths.get(options.dotOutputDir); Files.createDirectories(dir); generateDotFiles(dir, analyzer); } else { printRawOutput(log, analyzer); } if (options.findJDKInternals && !options.nowarning) { showReplacements(analyzer); } return true; }
private void buildLocationArchiveMap(List<Archive> archives) { // build a map from Location to Archive for (Archive archive: archives) { for (Location l: archive.getClasses()) { if (!map.containsKey(l)) { map.put(l, archive); } else { // duplicated class warning? } } } }
Archive findArchive(Location t) { Archive target = archive.getClasses().contains(t) ? archive : map.get(t); if (target == null) { map.put(t, target = NOT_FOUND); } return target; }
private String getLocationName(Location o) { if (level == Type.CLASS || level == Type.VERBOSE) { return o.getClassName(); } else { String pkg = o.getPackageName(); return pkg.isEmpty() ? "<unnamed>" : pkg; } }
/** * Returns the archives that contains the given locations and * not parsed and analyzed. */ private Set<Archive> unresolvedArchives(Stream<Location> locations) { return locations.filter(l -> !finder.isParsed(l)) .distinct() .map(configuration::findClass) .flatMap(Optional::stream) .collect(toSet()); }
private void transitiveDeps(int depth) throws IOException { Stream<Location> deps = archives.stream() .flatMap(Archive::getDependencies); Deque<Location> unresolved = deps.collect(Collectors.toCollection(LinkedList::new)); ConcurrentLinkedDeque<Location> deque = new ConcurrentLinkedDeque<>(); do { Location target; while ((target = unresolved.poll()) != null) { if (finder.isParsed(target)) continue; Archive archive = configuration.findClass(target).orElse(null); if (archive != null) { archives.add(archive); String name = target.getName(); Set<Location> targets = apiOnly ? finder.parseExportedAPIs(archive, name) : finder.parse(archive, name); // build unresolved dependencies targets.stream() .filter(t -> !finder.isParsed(t)) .forEach(deque::add); } } unresolved = deque; deque = new ConcurrentLinkedDeque<>(); } while (!unresolved.isEmpty() && depth-- > 0); }
/** * Performs the dependency analysis on the given archives. */ boolean run(Iterable<? extends Archive> archives, Map<Location, Archive> locationMap) { this.locationToArchive.putAll(locationMap); // traverse and analyze all dependencies for (Archive archive : archives) { Dependences deps = new Dependences(archive, type); archive.visitDependences(deps); results.put(archive, deps); } return true; }
private String getLocationName(Location o) { if (level == Type.CLASS || level == Type.VERBOSE) { return VersionHelper.get(o.getClassName()); } else { String pkg = o.getPackageName(); return pkg.isEmpty() ? "<unnamed>" : pkg; } }
public boolean contains(Location location) { String cn = location.getClassName(); int i = cn.lastIndexOf('.'); String pn = i > 0 ? cn.substring(0, i) : ""; return jdk8Internals.contains(pn); }
/** * Parses the named class from the given archive and * returns all target locations the named class references. */ public Set<Location> parse(Archive archive, String name) { try { return parse(archive, CLASS_FINDER, name); } catch (IOException e) { throw new UncheckedIOException(e); } }
/** * Parses the exported API of the named class from the given archive and * returns all target locations the named class references. */ public Set<Location> parseExportedAPIs(Archive archive, String name) { try { return parse(archive, API_FINDER, name); } catch (IOException e) { throw new UncheckedIOException(e); } }
private Set<Location> parse(Archive archive, Finder finder, String name) throws IOException { ClassFile cf = archive.reader().getClassFile(name); if (cf == null) { throw new IllegalArgumentException(archive.getName() + " does not contain " + name); } if (cf.access_flags.is(AccessFlags.ACC_MODULE)) return Collections.emptySet(); Set<Location> targets = new HashSet<>(); String cn; try { cn = cf.getName().replace('/', '.'); } catch (ConstantPoolException e) { throw new Dependencies.ClassFileError(e); } if (!finder.accept(archive, cn, cf.access_flags)) return targets; // tests if this class matches the -include if (!filter.matches(cn)) return targets; // skip checking filter.matches for (Dependency d : finder.findDependencies(cf)) { if (filter.accepts(d)) { targets.add(d.getTarget()); archive.addClass(d.getOrigin(), d.getTarget()); } else { // ensure that the parsed class is added the archive archive.addClass(d.getOrigin()); } parsedClasses.putIfAbsent(d.getOrigin(), archive); } return targets; }
/** * Filter depending on the containing archive or module */ @Override public boolean accepts(Location origin, Archive originArchive, Location target, Archive targetArchive) { if (findJDKInternals) { // accepts target that is JDK class but not exported Module module = targetArchive.getModule(); return originArchive != targetArchive && isJDKInternalPackage(module, target.getPackageName()); } else if (filterSameArchive) { // accepts origin and target that from different archive return originArchive != targetArchive; } return true; }
void run(PrintWriter out, String... args) throws IOException, ClassFileNotFoundException { decodeArgs(args); final StandardJavaFileManager fm = new JavacFileManager(new Context(), false, null); if (classpath != null) fm.setLocation(StandardLocation.CLASS_PATH, classpath); ClassFileReader reader = new ClassFileReader(fm); Dependencies d = new Dependencies(); if (regex != null) d.setFilter(Dependencies.getRegexFilter(Pattern.compile(regex))); if (packageNames.size() > 0) d.setFilter(Dependencies.getPackageFilter(packageNames, false)); SortedRecorder r = new SortedRecorder(reverse); d.findAllDependencies(reader, rootClassNames, transitiveClosure, r); SortedMap<Location,SortedSet<Dependency>> deps = r.getMap(); for (Map.Entry<Location, SortedSet<Dependency>> e: deps.entrySet()) { out.println(e.getKey()); for (Dependency dep: e.getValue()) { out.println(" " + dep.getTarget()); } } }
public void addDependency(Dependency d) { Location o = (reverse ? d.getTarget() : d.getOrigin()); SortedSet<Dependency> odeps = map.get(o); if (odeps == null) { Comparator<Dependency> c = (reverse ? originComparator : targetComparator); map.put(o, odeps = new TreeSet<Dependency>(c)); } odeps.add(d); }
/** * Returns the archives that contains the given locations and * not parsed and analyzed. */ private Set<Archive> unresolvedArchives(Stream<Location> locations) { return locations.filter(l -> !finder.isParsed(l)) .distinct() .map(configuration::findClass) .flatMap(Optional::stream) .filter(filter::include) .collect(toSet()); }
private void transitiveDeps(int depth) throws IOException { Stream<Location> deps = archives.stream() .flatMap(Archive::getDependencies); Deque<Location> unresolved = deps.collect(Collectors.toCollection(LinkedList::new)); ConcurrentLinkedDeque<Location> deque = new ConcurrentLinkedDeque<>(); do { Location target; while ((target = unresolved.poll()) != null) { if (finder.isParsed(target)) continue; Archive archive = configuration.findClass(target).orElse(null); if (archive != null && filter.include(archive)) { archives.add(archive); String name = target.getName(); Set<Location> targets = apiOnly ? finder.parseExportedAPIs(archive, name) : finder.parse(archive, name); // build unresolved dependencies targets.stream() .filter(t -> !finder.isParsed(t)) .forEach(deque::add); } } unresolved = deque; deque = new ConcurrentLinkedDeque<>(); } while (!unresolved.isEmpty() && depth-- > 0); }