src/share/classes/com/sun/tools/classfile/Dependencies.java

Fri, 28 Dec 2012 22:25:21 -0800

author
mchung
date
Fri, 28 Dec 2012 22:25:21 -0800
changeset 1472
0c244701188e
parent 1358
fc123bdeddb8
child 1715
57648bad3287
permissions
-rw-r--r--

8003562: Provide a CLI tool to analyze class dependencies
Reviewed-by: jjg, alanb, ulfzibis, erikj

jjg@451 1 /*
jjg@1357 2 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
jjg@451 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
jjg@451 4 *
jjg@451 5 * This code is free software; you can redistribute it and/or modify it
jjg@451 6 * under the terms of the GNU General Public License version 2 only, as
ohair@554 7 * published by the Free Software Foundation. Oracle designates this
jjg@451 8 * particular file as subject to the "Classpath" exception as provided
ohair@554 9 * by Oracle in the LICENSE file that accompanied this code.
jjg@451 10 *
jjg@451 11 * This code is distributed in the hope that it will be useful, but WITHOUT
jjg@451 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
jjg@451 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
jjg@451 14 * version 2 for more details (a copy is included in the LICENSE file that
jjg@451 15 * accompanied this code).
jjg@451 16 *
jjg@451 17 * You should have received a copy of the GNU General Public License version
jjg@451 18 * 2 along with this work; if not, write to the Free Software Foundation,
jjg@451 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
jjg@451 20 *
ohair@554 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554 22 * or visit www.oracle.com if you need additional information or have any
ohair@554 23 * questions.
jjg@451 24 */
jjg@451 25 package com.sun.tools.classfile;
jjg@451 26
jjg@451 27 import java.util.Deque;
jjg@451 28 import java.util.HashMap;
jjg@451 29 import java.util.HashSet;
jjg@451 30 import java.util.LinkedList;
jjg@451 31 import java.util.List;
jjg@451 32 import java.util.Map;
jjg@451 33 import java.util.Set;
jjg@451 34 import java.util.regex.Pattern;
jjg@451 35
jjg@1357 36 import com.sun.tools.classfile.Dependency.Filter;
jjg@451 37 import com.sun.tools.classfile.Dependency.Finder;
jjg@451 38 import com.sun.tools.classfile.Dependency.Location;
jjg@451 39 import com.sun.tools.classfile.Type.ArrayType;
jjg@451 40 import com.sun.tools.classfile.Type.ClassSigType;
jjg@451 41 import com.sun.tools.classfile.Type.ClassType;
jjg@451 42 import com.sun.tools.classfile.Type.MethodType;
jjg@451 43 import com.sun.tools.classfile.Type.SimpleType;
jjg@451 44 import com.sun.tools.classfile.Type.TypeParamType;
jjg@451 45 import com.sun.tools.classfile.Type.WildcardType;
jjg@451 46 import static com.sun.tools.classfile.ConstantPool.*;
jjg@451 47
jjg@451 48 /**
jjg@451 49 * A framework for determining {@link Dependency dependencies} between class files.
jjg@451 50 *
jjg@451 51 * A {@link Dependency.Finder finder} is used to identify the dependencies of
jjg@451 52 * individual classes. Some finders may return subtypes of {@code Dependency} to
jjg@451 53 * further characterize the type of dependency, such as a dependency on a
jjg@451 54 * method within a class.
jjg@451 55 *
jjg@451 56 * A {@link Dependency.Filter filter} may be used to restrict the set of
jjg@451 57 * dependencies found by a finder.
jjg@451 58 *
jjg@451 59 * Dependencies that are found may be passed to a {@link Dependencies.Recorder
jjg@451 60 * recorder} so that the dependencies can be stored in a custom data structure.
jjg@451 61 */
jjg@451 62 public class Dependencies {
jjg@451 63 /**
jjg@451 64 * Thrown when a class file cannot be found.
jjg@451 65 */
jjg@451 66 public static class ClassFileNotFoundException extends Exception {
jjg@451 67 private static final long serialVersionUID = 3632265927794475048L;
jjg@451 68
jjg@451 69 public ClassFileNotFoundException(String className) {
jjg@451 70 super(className);
jjg@451 71 this.className = className;
jjg@451 72 }
jjg@451 73
jjg@451 74 public ClassFileNotFoundException(String className, Throwable cause) {
jjg@451 75 this(className);
jjg@451 76 initCause(cause);
jjg@451 77 }
jjg@451 78
jjg@451 79 public final String className;
jjg@451 80 }
jjg@451 81
jjg@451 82 /**
jjg@451 83 * Thrown when an exception is found processing a class file.
jjg@451 84 */
jjg@451 85 public static class ClassFileError extends Error {
jjg@451 86 private static final long serialVersionUID = 4111110813961313203L;
jjg@451 87
jjg@451 88 public ClassFileError(Throwable cause) {
jjg@451 89 initCause(cause);
jjg@451 90 }
jjg@451 91 }
jjg@451 92
jjg@451 93 /**
jjg@451 94 * Service provider interface to locate and read class files.
jjg@451 95 */
jjg@451 96 public interface ClassFileReader {
jjg@451 97 /**
jjg@451 98 * Get the ClassFile object for a specified class.
jjg@451 99 * @param className the name of the class to be returned.
jjg@451 100 * @return the ClassFile for the given class
jjg@1358 101 * @throws Dependencies.ClassFileNotFoundException if the classfile cannot be
jjg@451 102 * found
jjg@451 103 */
jjg@451 104 public ClassFile getClassFile(String className)
jjg@451 105 throws ClassFileNotFoundException;
jjg@451 106 }
jjg@451 107
jjg@451 108 /**
jjg@451 109 * Service provide interface to handle results.
jjg@451 110 */
jjg@451 111 public interface Recorder {
jjg@451 112 /**
jjg@451 113 * Record a dependency that has been found.
jjg@451 114 * @param d
jjg@451 115 */
jjg@451 116 public void addDependency(Dependency d);
jjg@451 117 }
jjg@451 118
jjg@451 119 /**
jjg@451 120 * Get the default finder used to locate the dependencies for a class.
jjg@451 121 * @return the default finder
jjg@451 122 */
jjg@451 123 public static Finder getDefaultFinder() {
jjg@451 124 return new APIDependencyFinder(AccessFlags.ACC_PRIVATE);
jjg@451 125 }
jjg@451 126
jjg@451 127 /**
jjg@451 128 * Get a finder used to locate the API dependencies for a class.
jjg@451 129 * These include the superclass, superinterfaces, and classes referenced in
jjg@451 130 * the declarations of fields and methods. The fields and methods that
jjg@451 131 * are checked can be limited according to a specified access.
jjg@451 132 * The access parameter must be one of {@link AccessFlags#ACC_PUBLIC ACC_PUBLIC},
jjg@451 133 * {@link AccessFlags#ACC_PRIVATE ACC_PRIVATE},
jjg@451 134 * {@link AccessFlags#ACC_PROTECTED ACC_PROTECTED}, or 0 for
jjg@451 135 * package private access. Members with greater than or equal accessibility
jjg@451 136 * to that specified will be searched for dependencies.
jjg@451 137 * @param access the access of members to be checked
jjg@451 138 * @return an API finder
jjg@451 139 */
jjg@451 140 public static Finder getAPIFinder(int access) {
jjg@451 141 return new APIDependencyFinder(access);
jjg@451 142 }
jjg@451 143
jjg@451 144 /**
mchung@1472 145 * Get a finder to do class dependency analysis.
mchung@1472 146 *
mchung@1472 147 * @return a Class dependency finder
mchung@1472 148 */
mchung@1472 149 public static Finder getClassDependencyFinder() {
mchung@1472 150 return new ClassDependencyFinder();
mchung@1472 151 }
mchung@1472 152
mchung@1472 153 /**
jjg@451 154 * Get the finder used to locate the dependencies for a class.
jjg@451 155 * @return the finder
jjg@451 156 */
jjg@451 157 public Finder getFinder() {
jjg@451 158 if (finder == null)
jjg@451 159 finder = getDefaultFinder();
jjg@451 160 return finder;
jjg@451 161 }
jjg@451 162
jjg@451 163 /**
jjg@451 164 * Set the finder used to locate the dependencies for a class.
jjg@451 165 * @param f the finder
jjg@451 166 */
jjg@451 167 public void setFinder(Finder f) {
jjg@451 168 f.getClass(); // null check
jjg@451 169 finder = f;
jjg@451 170 }
jjg@451 171
jjg@451 172 /**
jjg@451 173 * Get the default filter used to determine included when searching
jjg@451 174 * the transitive closure of all the dependencies.
jjg@451 175 * Unless overridden, the default filter accepts all dependencies.
jjg@451 176 * @return the default filter.
jjg@451 177 */
jjg@451 178 public static Filter getDefaultFilter() {
jjg@451 179 return DefaultFilter.instance();
jjg@451 180 }
jjg@451 181
jjg@451 182 /**
jjg@451 183 * Get a filter which uses a regular expression on the target's class name
jjg@451 184 * to determine if a dependency is of interest.
jjg@451 185 * @param pattern the pattern used to match the target's class name
jjg@451 186 * @return a filter for matching the target class name with a regular expression
jjg@451 187 */
jjg@451 188 public static Filter getRegexFilter(Pattern pattern) {
jjg@451 189 return new TargetRegexFilter(pattern);
jjg@451 190 }
jjg@451 191
jjg@451 192 /**
jjg@451 193 * Get a filter which checks the package of a target's class name
jjg@451 194 * to determine if a dependency is of interest. The filter checks if the
jjg@451 195 * package of the target's class matches any of a set of given package
jjg@451 196 * names. The match may optionally match subpackages of the given names as well.
jjg@451 197 * @param packageNames the package names used to match the target's class name
jjg@451 198 * @param matchSubpackages whether or not to match subpackages as well
jjg@451 199 * @return a filter for checking the target package name against a list of package names
jjg@451 200 */
jjg@451 201 public static Filter getPackageFilter(Set<String> packageNames, boolean matchSubpackages) {
jjg@451 202 return new TargetPackageFilter(packageNames, matchSubpackages);
jjg@451 203 }
jjg@451 204
jjg@451 205 /**
jjg@451 206 * Get the filter used to determine the dependencies included when searching
jjg@451 207 * the transitive closure of all the dependencies.
jjg@451 208 * Unless overridden, the default filter accepts all dependencies.
jjg@451 209 * @return the filter
jjg@451 210 */
jjg@451 211 public Filter getFilter() {
jjg@451 212 if (filter == null)
jjg@451 213 filter = getDefaultFilter();
jjg@451 214 return filter;
jjg@451 215 }
jjg@451 216
jjg@451 217 /**
jjg@451 218 * Set the filter used to determine the dependencies included when searching
jjg@451 219 * the transitive closure of all the dependencies.
jjg@451 220 * @param f the filter
jjg@451 221 */
jjg@451 222 public void setFilter(Filter f) {
jjg@451 223 f.getClass(); // null check
jjg@451 224 filter = f;
jjg@451 225 }
jjg@451 226
jjg@451 227 /**
jjg@451 228 * Find the dependencies of a class, using the current
jjg@451 229 * {@link Dependencies#getFinder finder} and
jjg@451 230 * {@link Dependencies#getFilter filter}.
jjg@451 231 * The search may optionally include the transitive closure of all the
jjg@451 232 * filtered dependencies, by also searching in the classes named in those
jjg@451 233 * dependencies.
jjg@451 234 * @param classFinder a finder to locate class files
jjg@451 235 * @param rootClassNames the names of the root classes from which to begin
jjg@451 236 * searching
jjg@451 237 * @param transitiveClosure whether or not to also search those classes
jjg@451 238 * named in any filtered dependencies that are found.
jjg@451 239 * @return the set of dependencies that were found
jjg@451 240 * @throws ClassFileNotFoundException if a required class file cannot be found
jjg@451 241 * @throws ClassFileError if an error occurs while processing a class file,
jjg@451 242 * such as an error in the internal class file structure.
jjg@451 243 */
jjg@451 244 public Set<Dependency> findAllDependencies(
jjg@451 245 ClassFileReader classFinder, Set<String> rootClassNames,
jjg@451 246 boolean transitiveClosure)
jjg@451 247 throws ClassFileNotFoundException {
jjg@451 248 final Set<Dependency> results = new HashSet<Dependency>();
jjg@451 249 Recorder r = new Recorder() {
jjg@451 250 public void addDependency(Dependency d) {
jjg@451 251 results.add(d);
jjg@451 252 }
jjg@451 253 };
jjg@451 254 findAllDependencies(classFinder, rootClassNames, transitiveClosure, r);
jjg@451 255 return results;
jjg@451 256 }
jjg@451 257
jjg@451 258 /**
jjg@451 259 * Find the dependencies of a class, using the current
jjg@451 260 * {@link Dependencies#getFinder finder} and
jjg@451 261 * {@link Dependencies#getFilter filter}.
jjg@451 262 * The search may optionally include the transitive closure of all the
jjg@451 263 * filtered dependencies, by also searching in the classes named in those
jjg@451 264 * dependencies.
jjg@451 265 * @param classFinder a finder to locate class files
jjg@451 266 * @param rootClassNames the names of the root classes from which to begin
jjg@451 267 * searching
jjg@451 268 * @param transitiveClosure whether or not to also search those classes
jjg@451 269 * named in any filtered dependencies that are found.
jjg@451 270 * @param recorder a recorder for handling the results
jjg@451 271 * @throws ClassFileNotFoundException if a required class file cannot be found
jjg@451 272 * @throws ClassFileError if an error occurs while processing a class file,
jjg@451 273 * such as an error in the internal class file structure.
jjg@451 274 */
jjg@451 275 public void findAllDependencies(
jjg@451 276 ClassFileReader classFinder, Set<String> rootClassNames,
jjg@451 277 boolean transitiveClosure, Recorder recorder)
jjg@451 278 throws ClassFileNotFoundException {
jjg@451 279 Set<String> doneClasses = new HashSet<String>();
jjg@451 280
jjg@451 281 getFinder(); // ensure initialized
jjg@451 282 getFilter(); // ensure initialized
jjg@451 283
jjg@451 284 // Work queue of names of classfiles to be searched.
jjg@451 285 // Entries will be unique, and for classes that do not yet have
jjg@451 286 // dependencies in the results map.
jjg@451 287 Deque<String> deque = new LinkedList<String>(rootClassNames);
jjg@451 288
jjg@451 289 String className;
jjg@451 290 while ((className = deque.poll()) != null) {
jjg@451 291 assert (!doneClasses.contains(className));
jjg@451 292 doneClasses.add(className);
jjg@451 293
jjg@451 294 ClassFile cf = classFinder.getClassFile(className);
jjg@451 295
jjg@451 296 // The following code just applies the filter to the dependencies
jjg@451 297 // followed for the transitive closure.
jjg@451 298 for (Dependency d: finder.findDependencies(cf)) {
jjg@451 299 recorder.addDependency(d);
jjg@451 300 if (transitiveClosure && filter.accepts(d)) {
jjg@451 301 String cn = d.getTarget().getClassName();
jjg@451 302 if (!doneClasses.contains(cn))
jjg@451 303 deque.add(cn);
jjg@451 304 }
jjg@451 305 }
jjg@451 306 }
jjg@451 307 }
jjg@451 308
jjg@451 309 private Filter filter;
jjg@451 310 private Finder finder;
jjg@451 311
jjg@451 312 /**
jjg@451 313 * A location identifying a class.
jjg@451 314 */
jjg@451 315 static class SimpleLocation implements Location {
mchung@1472 316 public SimpleLocation(String name) {
mchung@1472 317 this.name = name;
mchung@1472 318 this.className = name.replace('/', '.').replace('$', '.');
jjg@451 319 }
jjg@451 320
mchung@1472 321 public String getName() {
mchung@1472 322 return name;
mchung@1472 323 }
mchung@1472 324
jjg@451 325 public String getClassName() {
jjg@451 326 return className;
jjg@451 327 }
jjg@451 328
mchung@1472 329 public String getPackageName() {
mchung@1472 330 int i = name.lastIndexOf('/');
mchung@1472 331 return (i > 0) ? name.substring(0, i).replace('/', '.') : "";
mchung@1472 332 }
mchung@1472 333
jjg@451 334 @Override
jjg@451 335 public boolean equals(Object other) {
jjg@451 336 if (this == other)
jjg@451 337 return true;
jjg@451 338 if (!(other instanceof SimpleLocation))
jjg@451 339 return false;
mchung@1472 340 return (name.equals(((SimpleLocation) other).name));
jjg@451 341 }
jjg@451 342
jjg@451 343 @Override
jjg@451 344 public int hashCode() {
mchung@1472 345 return name.hashCode();
jjg@451 346 }
jjg@451 347
jjg@451 348 @Override
jjg@451 349 public String toString() {
mchung@1472 350 return name;
jjg@451 351 }
jjg@451 352
mchung@1472 353 private String name;
jjg@451 354 private String className;
jjg@451 355 }
jjg@451 356
jjg@451 357 /**
jjg@451 358 * A dependency of one class on another.
jjg@451 359 */
jjg@451 360 static class SimpleDependency implements Dependency {
jjg@451 361 public SimpleDependency(Location origin, Location target) {
jjg@451 362 this.origin = origin;
jjg@451 363 this.target = target;
jjg@451 364 }
jjg@451 365
jjg@451 366 public Location getOrigin() {
jjg@451 367 return origin;
jjg@451 368 }
jjg@451 369
jjg@451 370 public Location getTarget() {
jjg@451 371 return target;
jjg@451 372 }
jjg@451 373
jjg@451 374 @Override
jjg@451 375 public boolean equals(Object other) {
jjg@451 376 if (this == other)
jjg@451 377 return true;
jjg@451 378 if (!(other instanceof SimpleDependency))
jjg@451 379 return false;
jjg@451 380 SimpleDependency o = (SimpleDependency) other;
jjg@451 381 return (origin.equals(o.origin) && target.equals(o.target));
jjg@451 382 }
jjg@451 383
jjg@451 384 @Override
jjg@451 385 public int hashCode() {
jjg@451 386 return origin.hashCode() * 31 + target.hashCode();
jjg@451 387 }
jjg@451 388
jjg@451 389 @Override
jjg@451 390 public String toString() {
jjg@451 391 return origin + ":" + target;
jjg@451 392 }
jjg@451 393
jjg@451 394 private Location origin;
jjg@451 395 private Location target;
jjg@451 396 }
jjg@451 397
jjg@451 398
jjg@451 399 /**
jjg@451 400 * This class accepts all dependencies.
jjg@451 401 */
jjg@451 402 static class DefaultFilter implements Filter {
jjg@451 403 private static DefaultFilter instance;
jjg@451 404
jjg@451 405 static DefaultFilter instance() {
jjg@451 406 if (instance == null)
jjg@451 407 instance = new DefaultFilter();
jjg@451 408 return instance;
jjg@451 409 }
jjg@451 410
jjg@451 411 public boolean accepts(Dependency dependency) {
jjg@451 412 return true;
jjg@451 413 }
jjg@451 414 }
jjg@451 415
jjg@451 416 /**
jjg@451 417 * This class accepts those dependencies whose target's class name matches a
jjg@451 418 * regular expression.
jjg@451 419 */
jjg@451 420 static class TargetRegexFilter implements Filter {
jjg@451 421 TargetRegexFilter(Pattern pattern) {
jjg@451 422 this.pattern = pattern;
jjg@451 423 }
jjg@451 424
jjg@451 425 public boolean accepts(Dependency dependency) {
jjg@451 426 return pattern.matcher(dependency.getTarget().getClassName()).matches();
jjg@451 427 }
jjg@451 428
jjg@452 429 private final Pattern pattern;
jjg@451 430 }
jjg@451 431
jjg@451 432 /**
jjg@451 433 * This class accepts those dependencies whose class name is in a given
jjg@451 434 * package.
jjg@451 435 */
jjg@451 436 static class TargetPackageFilter implements Filter {
jjg@451 437 TargetPackageFilter(Set<String> packageNames, boolean matchSubpackages) {
jjg@451 438 for (String pn: packageNames) {
jjg@451 439 if (pn.length() == 0) // implies null check as well
jjg@451 440 throw new IllegalArgumentException();
jjg@451 441 }
jjg@451 442 this.packageNames = packageNames;
jjg@451 443 this.matchSubpackages = matchSubpackages;
jjg@451 444 }
jjg@451 445
jjg@451 446 public boolean accepts(Dependency dependency) {
mchung@1472 447 String pn = dependency.getTarget().getPackageName();
jjg@451 448 if (packageNames.contains(pn))
jjg@451 449 return true;
jjg@451 450
jjg@451 451 if (matchSubpackages) {
jjg@451 452 for (String n: packageNames) {
jjg@451 453 if (pn.startsWith(n + "."))
jjg@451 454 return true;
jjg@451 455 }
jjg@451 456 }
jjg@451 457
jjg@451 458 return false;
jjg@451 459 }
jjg@451 460
jjg@452 461 private final Set<String> packageNames;
jjg@452 462 private final boolean matchSubpackages;
jjg@451 463 }
jjg@451 464
jjg@451 465 /**
jjg@451 466 * This class identifies class names directly or indirectly in the constant pool.
jjg@451 467 */
jjg@451 468 static class ClassDependencyFinder extends BasicDependencyFinder {
jjg@451 469 public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
jjg@451 470 Visitor v = new Visitor(classfile);
jjg@451 471 for (CPInfo cpInfo: classfile.constant_pool.entries()) {
jjg@451 472 v.scan(cpInfo);
jjg@451 473 }
mchung@1472 474 try {
mchung@1472 475 v.addClass(classfile.super_class);
mchung@1472 476 v.addClasses(classfile.interfaces);
mchung@1472 477 v.scan(classfile.attributes);
mchung@1472 478
mchung@1472 479 for (Field f : classfile.fields) {
mchung@1472 480 v.scan(f.descriptor, f.attributes);
mchung@1472 481 }
mchung@1472 482 for (Method m : classfile.methods) {
mchung@1472 483 v.scan(m.descriptor, m.attributes);
mchung@1472 484 Exceptions_attribute e =
mchung@1472 485 (Exceptions_attribute)m.attributes.get(Attribute.Exceptions);
mchung@1472 486 if (e != null) {
mchung@1472 487 v.addClasses(e.exception_index_table);
mchung@1472 488 }
mchung@1472 489 }
mchung@1472 490 } catch (ConstantPoolException e) {
mchung@1472 491 throw new ClassFileError(e);
mchung@1472 492 }
mchung@1472 493
jjg@451 494 return v.deps;
jjg@451 495 }
jjg@451 496 }
jjg@451 497
jjg@451 498 /**
jjg@451 499 * This class identifies class names in the signatures of classes, fields,
jjg@451 500 * and methods in a class.
jjg@451 501 */
jjg@451 502 static class APIDependencyFinder extends BasicDependencyFinder {
jjg@451 503 APIDependencyFinder(int access) {
jjg@451 504 switch (access) {
jjg@451 505 case AccessFlags.ACC_PUBLIC:
jjg@451 506 case AccessFlags.ACC_PROTECTED:
jjg@451 507 case AccessFlags.ACC_PRIVATE:
jjg@451 508 case 0:
jjg@451 509 showAccess = access;
jjg@451 510 break;
jjg@451 511 default:
jjg@451 512 throw new IllegalArgumentException("invalid access 0x"
jjg@451 513 + Integer.toHexString(access));
jjg@451 514 }
jjg@451 515 }
jjg@451 516
jjg@451 517 public Iterable<? extends Dependency> findDependencies(ClassFile classfile) {
jjg@451 518 try {
jjg@451 519 Visitor v = new Visitor(classfile);
jjg@451 520 v.addClass(classfile.super_class);
jjg@451 521 v.addClasses(classfile.interfaces);
jjg@451 522 // inner classes?
jjg@451 523 for (Field f : classfile.fields) {
jjg@451 524 if (checkAccess(f.access_flags))
jjg@451 525 v.scan(f.descriptor, f.attributes);
jjg@451 526 }
jjg@451 527 for (Method m : classfile.methods) {
jjg@451 528 if (checkAccess(m.access_flags)) {
jjg@451 529 v.scan(m.descriptor, m.attributes);
jjg@451 530 Exceptions_attribute e =
jjg@451 531 (Exceptions_attribute) m.attributes.get(Attribute.Exceptions);
jjg@451 532 if (e != null)
jjg@451 533 v.addClasses(e.exception_index_table);
jjg@451 534 }
jjg@451 535 }
jjg@451 536 return v.deps;
jjg@451 537 } catch (ConstantPoolException e) {
jjg@451 538 throw new ClassFileError(e);
jjg@451 539 }
jjg@451 540 }
jjg@451 541
jjg@451 542 boolean checkAccess(AccessFlags flags) {
jjg@451 543 // code copied from javap.Options.checkAccess
jjg@451 544 boolean isPublic = flags.is(AccessFlags.ACC_PUBLIC);
jjg@451 545 boolean isProtected = flags.is(AccessFlags.ACC_PROTECTED);
jjg@451 546 boolean isPrivate = flags.is(AccessFlags.ACC_PRIVATE);
jjg@451 547 boolean isPackage = !(isPublic || isProtected || isPrivate);
jjg@451 548
jjg@451 549 if ((showAccess == AccessFlags.ACC_PUBLIC) && (isProtected || isPrivate || isPackage))
jjg@451 550 return false;
jjg@451 551 else if ((showAccess == AccessFlags.ACC_PROTECTED) && (isPrivate || isPackage))
jjg@451 552 return false;
jjg@451 553 else if ((showAccess == 0) && (isPrivate))
jjg@451 554 return false;
jjg@451 555 else
jjg@451 556 return true;
jjg@451 557 }
jjg@451 558
jjg@451 559 private int showAccess;
jjg@451 560 }
jjg@451 561
jjg@451 562 static abstract class BasicDependencyFinder implements Finder {
jjg@451 563 private Map<String,Location> locations = new HashMap<String,Location>();
jjg@451 564
jjg@451 565 Location getLocation(String className) {
jjg@451 566 Location l = locations.get(className);
jjg@451 567 if (l == null)
jjg@451 568 locations.put(className, l = new SimpleLocation(className));
jjg@451 569 return l;
jjg@451 570 }
jjg@451 571
jjg@451 572 class Visitor implements ConstantPool.Visitor<Void,Void>, Type.Visitor<Void, Void> {
jjg@451 573 private ConstantPool constant_pool;
jjg@451 574 private Location origin;
jjg@452 575 Set<Dependency> deps;
jjg@451 576
jjg@451 577 Visitor(ClassFile classFile) {
jjg@451 578 try {
jjg@451 579 constant_pool = classFile.constant_pool;
jjg@451 580 origin = getLocation(classFile.getName());
jjg@451 581 deps = new HashSet<Dependency>();
jjg@451 582 } catch (ConstantPoolException e) {
jjg@451 583 throw new ClassFileError(e);
jjg@451 584 }
jjg@451 585 }
jjg@451 586
jjg@451 587 void scan(Descriptor d, Attributes attrs) {
jjg@451 588 try {
jjg@451 589 scan(new Signature(d.index).getType(constant_pool));
mchung@1472 590 scan(attrs);
jjg@451 591 } catch (ConstantPoolException e) {
jjg@451 592 throw new ClassFileError(e);
jjg@451 593 }
jjg@451 594 }
jjg@451 595
jjg@451 596 void scan(CPInfo cpInfo) {
jjg@451 597 cpInfo.accept(this, null);
jjg@451 598 }
jjg@451 599
jjg@451 600 void scan(Type t) {
jjg@451 601 t.accept(this, null);
jjg@451 602 }
jjg@451 603
mchung@1472 604 void scan(Attributes attrs) {
mchung@1472 605 try {
mchung@1472 606 Signature_attribute sa = (Signature_attribute)attrs.get(Attribute.Signature);
mchung@1472 607 if (sa != null)
mchung@1472 608 scan(sa.getParsedSignature().getType(constant_pool));
mchung@1472 609
mchung@1472 610 scan((RuntimeVisibleAnnotations_attribute)
mchung@1472 611 attrs.get(Attribute.RuntimeVisibleAnnotations));
mchung@1472 612 scan((RuntimeVisibleParameterAnnotations_attribute)
mchung@1472 613 attrs.get(Attribute.RuntimeVisibleParameterAnnotations));
mchung@1472 614 } catch (ConstantPoolException e) {
mchung@1472 615 throw new ClassFileError(e);
mchung@1472 616 }
mchung@1472 617 }
mchung@1472 618
mchung@1472 619 private void scan(RuntimeAnnotations_attribute attr) throws ConstantPoolException {
mchung@1472 620 if (attr == null) {
mchung@1472 621 return;
mchung@1472 622 }
mchung@1472 623 for (int i = 0; i < attr.annotations.length; i++) {
mchung@1472 624 int index = attr.annotations[i].type_index;
mchung@1472 625 scan(new Signature(index).getType(constant_pool));
mchung@1472 626 }
mchung@1472 627 }
mchung@1472 628
mchung@1472 629 private void scan(RuntimeParameterAnnotations_attribute attr) throws ConstantPoolException {
mchung@1472 630 if (attr == null) {
mchung@1472 631 return;
mchung@1472 632 }
mchung@1472 633 for (int param = 0; param < attr.parameter_annotations.length; param++) {
mchung@1472 634 for (int i = 0; i < attr.parameter_annotations[param].length; i++) {
mchung@1472 635 int index = attr.parameter_annotations[param][i].type_index;
mchung@1472 636 scan(new Signature(index).getType(constant_pool));
mchung@1472 637 }
mchung@1472 638 }
mchung@1472 639 }
mchung@1472 640
jjg@451 641 void addClass(int index) throws ConstantPoolException {
jjg@451 642 if (index != 0) {
jjg@451 643 String name = constant_pool.getClassInfo(index).getBaseName();
jjg@451 644 if (name != null)
jjg@451 645 addDependency(name);
jjg@451 646 }
jjg@451 647 }
jjg@451 648
jjg@451 649 void addClasses(int[] indices) throws ConstantPoolException {
jjg@451 650 for (int i: indices)
jjg@451 651 addClass(i);
jjg@451 652 }
jjg@451 653
jjg@451 654 private void addDependency(String name) {
jjg@451 655 deps.add(new SimpleDependency(origin, getLocation(name)));
jjg@451 656 }
jjg@451 657
jjg@451 658 // ConstantPool.Visitor methods
jjg@451 659
jjg@451 660 public Void visitClass(CONSTANT_Class_info info, Void p) {
jjg@451 661 try {
jjg@451 662 if (info.getName().startsWith("["))
jjg@451 663 new Signature(info.name_index).getType(constant_pool).accept(this, null);
jjg@451 664 else
jjg@451 665 addDependency(info.getBaseName());
jjg@451 666 return null;
jjg@451 667 } catch (ConstantPoolException e) {
jjg@451 668 throw new ClassFileError(e);
jjg@451 669 }
jjg@451 670 }
jjg@451 671
jjg@451 672 public Void visitDouble(CONSTANT_Double_info info, Void p) {
jjg@451 673 return null;
jjg@451 674 }
jjg@451 675
jjg@451 676 public Void visitFieldref(CONSTANT_Fieldref_info info, Void p) {
jjg@451 677 return visitRef(info, p);
jjg@451 678 }
jjg@451 679
jjg@451 680 public Void visitFloat(CONSTANT_Float_info info, Void p) {
jjg@451 681 return null;
jjg@451 682 }
jjg@451 683
jjg@451 684 public Void visitInteger(CONSTANT_Integer_info info, Void p) {
jjg@451 685 return null;
jjg@451 686 }
jjg@451 687
jjg@451 688 public Void visitInterfaceMethodref(CONSTANT_InterfaceMethodref_info info, Void p) {
jjg@451 689 return visitRef(info, p);
jjg@451 690 }
jjg@451 691
ksrini@826 692 public Void visitInvokeDynamic(CONSTANT_InvokeDynamic_info info, Void p) {
ksrini@826 693 return null;
ksrini@826 694 }
ksrini@826 695
jjg@451 696 public Void visitLong(CONSTANT_Long_info info, Void p) {
jjg@451 697 return null;
jjg@451 698 }
jjg@451 699
ksrini@826 700 public Void visitMethodHandle(CONSTANT_MethodHandle_info info, Void p) {
ksrini@826 701 return null;
ksrini@826 702 }
ksrini@826 703
ksrini@826 704 public Void visitMethodType(CONSTANT_MethodType_info info, Void p) {
ksrini@826 705 return null;
ksrini@826 706 }
ksrini@826 707
ksrini@826 708 public Void visitMethodref(CONSTANT_Methodref_info info, Void p) {
ksrini@826 709 return visitRef(info, p);
ksrini@826 710 }
ksrini@826 711
jjg@451 712 public Void visitNameAndType(CONSTANT_NameAndType_info info, Void p) {
jjg@451 713 try {
jjg@451 714 new Signature(info.type_index).getType(constant_pool).accept(this, null);
jjg@451 715 return null;
jjg@451 716 } catch (ConstantPoolException e) {
jjg@451 717 throw new ClassFileError(e);
jjg@451 718 }
jjg@451 719 }
jjg@451 720
jjg@451 721 public Void visitString(CONSTANT_String_info info, Void p) {
jjg@451 722 return null;
jjg@451 723 }
jjg@451 724
jjg@451 725 public Void visitUtf8(CONSTANT_Utf8_info info, Void p) {
jjg@451 726 return null;
jjg@451 727 }
jjg@451 728
jjg@451 729 private Void visitRef(CPRefInfo info, Void p) {
jjg@451 730 try {
jjg@451 731 visitClass(info.getClassInfo(), p);
jjg@451 732 return null;
jjg@451 733 } catch (ConstantPoolException e) {
jjg@451 734 throw new ClassFileError(e);
jjg@451 735 }
jjg@451 736 }
jjg@451 737
jjg@451 738 // Type.Visitor methods
jjg@451 739
jjg@451 740 private void findDependencies(Type t) {
jjg@451 741 if (t != null)
jjg@451 742 t.accept(this, null);
jjg@451 743 }
jjg@451 744
jjg@451 745 private void findDependencies(List<? extends Type> ts) {
jjg@451 746 if (ts != null) {
jjg@451 747 for (Type t: ts)
jjg@451 748 t.accept(this, null);
jjg@451 749 }
jjg@451 750 }
jjg@451 751
jjg@451 752 public Void visitSimpleType(SimpleType type, Void p) {
jjg@451 753 return null;
jjg@451 754 }
jjg@451 755
jjg@451 756 public Void visitArrayType(ArrayType type, Void p) {
jjg@451 757 findDependencies(type.elemType);
jjg@451 758 return null;
jjg@451 759 }
jjg@451 760
jjg@451 761 public Void visitMethodType(MethodType type, Void p) {
jjg@451 762 findDependencies(type.paramTypes);
jjg@451 763 findDependencies(type.returnType);
jjg@451 764 findDependencies(type.throwsTypes);
mchung@1472 765 findDependencies(type.typeParamTypes);
jjg@451 766 return null;
jjg@451 767 }
jjg@451 768
jjg@451 769 public Void visitClassSigType(ClassSigType type, Void p) {
jjg@451 770 findDependencies(type.superclassType);
jjg@451 771 findDependencies(type.superinterfaceTypes);
jjg@451 772 return null;
jjg@451 773 }
jjg@451 774
jjg@451 775 public Void visitClassType(ClassType type, Void p) {
jjg@451 776 findDependencies(type.outerType);
mchung@1472 777 addDependency(type.getBinaryName());
jjg@451 778 findDependencies(type.typeArgs);
jjg@451 779 return null;
jjg@451 780 }
jjg@451 781
jjg@451 782 public Void visitTypeParamType(TypeParamType type, Void p) {
jjg@451 783 findDependencies(type.classBound);
jjg@451 784 findDependencies(type.interfaceBounds);
jjg@451 785 return null;
jjg@451 786 }
jjg@451 787
jjg@451 788 public Void visitWildcardType(WildcardType type, Void p) {
jjg@451 789 findDependencies(type.boundType);
jjg@451 790 return null;
jjg@451 791 }
jjg@451 792 }
jjg@451 793 }
jjg@451 794 }

mercurial