View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.lang.java.typeresolution;
    5
    6   import java.io.IOException;
    7   import java.util.ArrayList;
    8   import java.util.Collections;
    9   import java.util.HashSet;
    10  import java.util.List;
    11  import java.util.Map;
    12  import java.util.Set;
    13
    14  import net.sourceforge.pmd.lang.java.typeresolution.visitors.PMDASMVisitor;
    15
    16  import org.objectweb.asm.ClassReader;
    17
    18  /*
    19   * I've refactored this class to not cache the results any more. This is a
    20   * tradeoff in testing I've found the CPU tradeoff is negligeable. With the
    21   * cache, large codebases consumed a lot of memory and slowed down greatly when
    22   * approaching 3,000 classes. I'm adding this comment in case someone is looking
    23   * at this code and thinks a cache may help.
    24   *
    25   * see: git show 9e7deee88f63870a1de2cd86458278a027deb6d6
    26   *
    27   * However, there seems to be a big performance improvement by caching
    28   * the negative cases only. The cache is shared between loadClass and getImportedClasses,
    29   * as they are using the same (parent) class loader, e.g. if the class foo.Bar cannot be loaded,
    30   * then the resource foo/Bar.class will not exist, too.
    31   */
    32  public class PMDASMClassLoader extends ClassLoader {
    33
    34      private static PMDASMClassLoader cachedPMDASMClassLoader;
    35      private static ClassLoader cachedClassLoader;
    36
    37      /**
    38       * A new PMDASMClassLoader is created for each compilation unit, this method allows to reuse the same
    39       * PMDASMClassLoader across all the compilation units.
    40       */
    41      public static synchronized PMDASMClassLoader getInstance(ClassLoader parent) {
    42          if (parent == cachedClassLoader) return cachedPMDASMClassLoader;
    43          cachedClassLoader = parent;
    44          cachedPMDASMClassLoader = new PMDASMClassLoader(parent);
    45          return cachedPMDASMClassLoader;
    46      }
    47
    48      //
    49
    50      private PMDASMClassLoader(ClassLoader parent) {
    51      	super(parent);
    52      }
    53
    54      /** Caches the names of the classes that we can't load or that don't exist. */
    55      private final Set<String> dontBother = new HashSet<String>();
    56
    57      @Override
    58      public synchronized Class<?> loadClass(String name) throws ClassNotFoundException {
    59  	if (dontBother.contains(name)) {
    60  	    throw new ClassNotFoundException(name);
    61  	}
    62  	try {
    63  	    return super.loadClass(name);
    64  	} catch (ClassNotFoundException e) {
    65  	    dontBother.add(name);
    66  	    throw e;
    67  	}
    68      }
    69
    70      public synchronized Map<String, String> getImportedClasses(String name) throws ClassNotFoundException {
    71
    72          if (dontBother.contains(name)) {
    73              throw new ClassNotFoundException(name);
    74          }
    75          try {
    76              ClassReader reader = new ClassReader(getResourceAsStream(name.replace('.', '/') + ".class"));
    77              PMDASMVisitor asmVisitor = new PMDASMVisitor();
    78              reader.accept(asmVisitor, 0);
    79
    80              List<String> inner = asmVisitor.getInnerClasses();
    81              if (inner != null && !inner.isEmpty()) {
    82                  inner = new ArrayList<String>(inner); // to avoid ConcurrentModificationException
    83                  for (String str: inner) {
    84                      reader = new ClassReader(getResourceAsStream(str.replace('.', '/') + ".class"));
    85                      reader.accept(asmVisitor, 0);
    86                  }
    87              }
    88              return asmVisitor.getPackages();
    89          } catch (IOException e) {
    90              dontBother.add(name);
    91              throw new ClassNotFoundException(name, e);
    92          }
    93      }
    94  }