Find out which classes of a given API are used

后端 未结 8 879
鱼传尺愫
鱼传尺愫 2020-11-29 06:20

In a Java Project of mine, I would like to find out programmatically which classes from a given API are used. Is there a good way to do that? Through source code parsing or

8条回答
  •  野趣味
    野趣味 (楼主)
    2020-11-29 06:52

    You can discover the classes using ASM's Remapper class (believe it or not). This class is actually meant to replace all occurrences of a class name within bytecode. For your purposes, however, it doesn't need to replace anything.

    This probably doesn't make a whole lot of sense, so here is an example...

    First, you create a subclass of Remapper whose only purpose in life is to intercept all calls to the mapType(String) method, recording its argument for later use.

    public class ClassNameRecordingRemapper extends Remapper {
    
        private final Set classNames;
    
        public ClassNameRecordingRemapper(Set classNames) {
            this.classNames = classNames;
        }
    
        @Override
        public String mapType(String type) {
            classNames.add(type);
            return type;
        }
    
    }
    

    Now you can write a method like this:

    public Set findClassNames(byte[] bytecode) {
        Set classNames = new HashSet();
    
        ClassReader classReader = new ClassReader(bytecode);
        ClassWriter classWriter = new ClassWriter(classReader, 0);
    
        ClassNameRecordingRemapper remapper = new ClassNameRecordingRemapper(classNames);
        classReader.accept(remapper, 0);
    
        return classNames;
    }
    

    It's your responsibility to actually obtain all classes' bytecode.


    EDIT by seanizer (OP)

    I am accepting this answer, but as the above code is not quite correct, I will insert the way I used this:

    public static class Collector extends Remapper{
    
        private final Set> classNames;
        private final String prefix;
    
        public Collector(final Set> classNames, final String prefix){
            this.classNames = classNames;
            this.prefix = prefix;
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public String mapDesc(final String desc){
            if(desc.startsWith("L")){
                this.addType(desc.substring(1, desc.length() - 1));
            }
            return super.mapDesc(desc);
        }
    
        /**
         * {@inheritDoc}
         */
        @Override
        public String[] mapTypes(final String[] types){
            for(final String type : types){
                this.addType(type);
            }
            return super.mapTypes(types);
        }
    
        private void addType(final String type){
            final String className = type.replace('/', '.');
            if(className.startsWith(this.prefix)){
                try{
                    this.classNames.add(Class.forName(className));
                } catch(final ClassNotFoundException e){
                    throw new IllegalStateException(e);
                }
            }
        }
    
        @Override
        public String mapType(final String type){
            this.addType(type);
            return type;
        }
    
    }
    
    public static Set> getClassesUsedBy(
        final String name,   // class name
        final String prefix  // common prefix for all classes
                             // that will be retrieved
        ) throws IOException{
        final ClassReader reader = new ClassReader(name);
        final Set> classes =
            new TreeSet>(new Comparator>(){
    
                @Override
                public int compare(final Class o1, final Class o2){
                    return o1.getName().compareTo(o2.getName());
                }
            });
        final Remapper remapper = new Collector(classes, prefix);
        final ClassVisitor inner = new EmptyVisitor();
        final RemappingClassAdapter visitor =
            new RemappingClassAdapter(inner, remapper);
        reader.accept(visitor, 0);
        return classes;
    }
    

    Here's a main class to test it using:

    public static void main(final String[] args) throws Exception{
        final Collection> classes =
            getClassesUsedBy(Collections.class.getName(), "java.util");
        System.out.println("Used classes:");
        for(final Class cls : classes){
            System.out.println(" - " + cls.getName());
        }
    
    }
    

    And here's the Output:

    Used classes:
     - java.util.ArrayList
     - java.util.Arrays
     - java.util.Collection
     - java.util.Collections
     - java.util.Collections$1
     - java.util.Collections$AsLIFOQueue
     - java.util.Collections$CheckedCollection
     - java.util.Collections$CheckedList
     - java.util.Collections$CheckedMap
     - java.util.Collections$CheckedRandomAccessList
     - java.util.Collections$CheckedSet
     - java.util.Collections$CheckedSortedMap
     - java.util.Collections$CheckedSortedSet
     - java.util.Collections$CopiesList
     - java.util.Collections$EmptyList
     - java.util.Collections$EmptyMap
     - java.util.Collections$EmptySet
     - java.util.Collections$ReverseComparator
     - java.util.Collections$ReverseComparator2
     - java.util.Collections$SelfComparable
     - java.util.Collections$SetFromMap
     - java.util.Collections$SingletonList
     - java.util.Collections$SingletonMap
     - java.util.Collections$SingletonSet
     - java.util.Collections$SynchronizedCollection
     - java.util.Collections$SynchronizedList
     - java.util.Collections$SynchronizedMap
     - java.util.Collections$SynchronizedRandomAccessList
     - java.util.Collections$SynchronizedSet
     - java.util.Collections$SynchronizedSortedMap
     - java.util.Collections$SynchronizedSortedSet
     - java.util.Collections$UnmodifiableCollection
     - java.util.Collections$UnmodifiableList
     - java.util.Collections$UnmodifiableMap
     - java.util.Collections$UnmodifiableRandomAccessList
     - java.util.Collections$UnmodifiableSet
     - java.util.Collections$UnmodifiableSortedMap
     - java.util.Collections$UnmodifiableSortedSet
     - java.util.Comparator
     - java.util.Deque
     - java.util.Enumeration
     - java.util.Iterator
     - java.util.List
     - java.util.ListIterator
     - java.util.Map
     - java.util.Queue
     - java.util.Random
     - java.util.RandomAccess
     - java.util.Set
     - java.util.SortedMap
     - java.util.SortedSet
    

提交回复
热议问题