Каков наилучший способ (программно) разобрать скомпилированный файл Java (.class) и сгенерировать список всех и всех других классов Java, на которые он ссылается?Определить классы, на которые ссылается файл .class
ответ
Это очень просто сделать это, если знаете the spec.
Heres быстрый и грязный демонстрационная программа, как это сделать:
package test;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class Main {
private static Map<Integer, String> strings = new HashMap<Integer, String>();
private static Set<Integer> classes = new HashSet<Integer>();
private static int indexCorrection = 0; // for correcting indexes to constant pools with long and double entries
public static void main(String[] args) throws Exception {
printReferencedClassesFromClass(String.class);
}
private static void printReferencedClassesFromClass(Class<?> c) throws IOException {
saveReferencedClassesFromStream(c.getResourceAsStream(c.getSimpleName() + ".class"));
printReferencedClasses();
}
private static void printReferencedClasses() {
for (Integer index : classes)
System.out.println(strings.get(index));
}
private static void saveReferencedClassesFromStream(InputStream stream) throws IOException {
DataInputStream dataStream = new DataInputStream(stream);
skipHeader(dataStream);
saveReferencedClassesFromConstantPool(dataStream);
}
private static void skipHeader(DataInputStream dataInputStream) throws IOException {
readU4(dataInputStream); // magic byte
readU2(dataInputStream); // minor version
readU2(dataInputStream); // major version
}
private static void saveReferencedClassesFromConstantPool(DataInputStream stream) throws IOException {
int poolSize = readU2(stream);
for (int n = 1; n < poolSize - indexCorrection; n++)
savePoolEntryIfIsClassReference(n, stream);
}
private static void savePoolEntryIfIsClassReference(int index, DataInputStream stream) throws IOException {
int tag = readU1(stream);
switch (tag) {
case 1: // Utf8
saveStringFromUtf8Entry(index, stream);
break;
case 7: // Class
saveClassEntry(stream);
break;
case 8: // String
case 16: // MethodType
readU2(stream);
break;
case 3: // Integer
case 4: // Float
readU4(stream);
break;
case 5: // Long
case 6: // Double
readU4(stream);
readU4(stream);
indexCorrection++;
break;
case 9: // Fieldref
case 10: // Methodref
case 11: // InterfaceMethodref
case 12: // NameAndType
case 18: // InvokeDynmaic
readU2(stream);
readU2(stream);
break;
case 15: // MethodHandle
readU1(stream);
readU2(stream);
break;
}
}
private static void saveClassEntry(DataInputStream stream) throws IOException {
classes.add(readU2(stream));
}
private static void saveStringFromUtf8Entry(int index, DataInputStream stream) throws IOException {
strings.put(index + indexCorrection, readString(stream));
}
private static String readString(DataInputStream stream) throws IOException {
return stream.readUTF();
}
private static int readU1(DataInputStream stream) throws IOException {
return stream.readUnsignedByte();
}
private static int readU2(DataInputStream stream) throws IOException {
return stream.readUnsignedShort();
}
private static int readU4(DataInputStream stream) throws IOException {
return stream.readInt();
}
}
Выход:
test/Main
java/lang/Object
java/io/BufferedInputStream
java/util/Iterator
java/io/FileInputStream
java/lang/System
java/lang/Integer
java/lang/Exception
java/lang/String
java/io/File
java/util/Map
java/util/HashMap
java/io/IOException
java/util/Set
java/io/DataInputStream
java/io/PrintStream
java/util/HashSet
java/lang/Throwable
Вы пробовали JDepend? Он анализирует файлы классов для определения ссылок.
Я не знаю, какой лучший способ. Я знаю, что есть 2 API, которые могут помочь.
Вы можете попробовать jclasslib, для проверки .class файлов
http://www.ej-technologies.com/products/jclasslib/resources.html
или BCEL (байт-код инженерной библиотеки) для анализа и обработки .class файлов.
http://jakarta.apache.org/bcel/
Я понимаю, что любой класс для использования в .class (насколько поле, параметр, тип возвращаемого значения и т.д.) определяется в constant_pool. Поэтому вы можете прочитать об этом.
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html
Это возможно сделать с Apache BCEL.
Я думаю, что вы можете использовать мой Java Dependency Resolver project Который будет найти не только ссылки Java файлы но и банки для конкретного класса в проекте
К сожалению, это не сработает каждый раз, должно быть ошибка где-то. Попробуйте его на 'ImmutableSetMultimap.class' из guava-17. Он будет рассматривать строку «Invalid key count» как имя класса. Кроме того, попробуйте добавить раздел 'default' в' switch (tag) ': он будет запущен в самом конце пула с именем' tag == 0' – eprst
Я исправил ошибку и адаптировал код для работы с java 8. Ожидается снова сломаться с новыми форматами файлов классов, если они добавят дополнительные типы тегов в постоянный пул. – Arne