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