Получение квалифицированного имени метода по номеру строки

Этот вопрос специфичен для Java и Maven. Обратите внимание на дополнительные ограничения, приведенные ниже, поскольку они отличаются от других вопросов.

У меня есть несколько проектов Maven (Java) для анализа. Что у меня есть:

  • исходный код
  • скомпилированный maven код Jave с двоичными файлами в target/ folder

Вопрос в том, что, учитывая один файл с исходным кодом (.java) и номер строки, как я могу получить полное имя метода, охватывающего эту строку? Если строка отсутствует в методе, просто выведите null. Приемлемые языки для реализации этого: Java, ruby ​​или python.

Не могли бы вы ответить на вопрос одним из следующих двух способов?

  1. используйте двоичный файл и извлеките квалифицированное имя метода этой строки. (Это может включать переплетение в отладочной информации, но это нормально.)

  2. напрямую используйте указанный исходный файл, попробуйте разобрать его и использовать AST.

Использование определенных библиотек (например, BCEL) или любых сторонних (если они хорошо документированы и могут использоваться) также допустимо.

Большое спасибо за огромную помощь!

2 ответа

Решение

К сожалению, ваш вопрос полон недостатков:

  • Конечно, вы можете анализировать входной источник (через анализатор Javacc или ANTLR), пока не достигнете нужной строки. Но кажется, что анализ одного и того же источника является пустой тратой усилий, поскольку у вас уже есть .class файлы.
  • Так что, кажется, лучше проанализировать .class файл. Но, к сожалению, у вас нет гарантии, что это класс, в котором создается ваша строка, потому что в одном и том же исходном файле может быть несколько классов.

Augh! Это приводит меня к некоему сложному решению:

Я объявлю класс, который будет содержать все логин:

public class SourceMethodsIndexer
{
    private final SortedMap<Integer, List<Method>> indexOfMethodsByFirstLineNumber;
}

Конструктор будет выглядеть так:

public SourceMethodsIndexer(File sourceFile)

... и должен выполнить следующие задачи:

1. Найдите каталог класса, связанный с целевым пакетом.

File targetPackageDir=getTargetPackageDir(sourceFile);
File[] classFiles=targetPackageDir.listFiles(new FileFilter(){
    public boolean accept(File dir, String name){
        return name.endsWith(".class");
}

});

2. Используйте Apache BCEL для сбора всех непубличных классов, принадлежащих вашему исходному файлу (вы можете вызвать JavaClass.getSourceFileName() фильтровать классы), плюс открытый класс, соответствующий имени вашего входного исходного файла.

Collection<JavaClass> targetClasses=getNonPublicClasses(classFiles, sourceFile.getName());
targetClasses.add(publicClass);

3. Соберите все методы в каждом классе.

Set<Method> targetMethods=new HashSet<Method>(1024);
for (JavaClass javaClass:targetClasses)
{
    targetMethods.addAll(Arrays.asList(javaClass.getMethods()));
}

4.Теперь вы можете либо искать непосредственно по номеру вашей строки, либо сначала индексировать методы по номеру строки, чтобы позже получить к ним более быстрый доступ: JavaClass.getMethods()[n].getLineNumberTable().getSourceLine(0) (позаботьтесь о том, чтобы могли быть повторные значения).

this.indexOfMethodsByFirstLineNumber=new TreeMap<Integer, List<Method>>((int)(1.7d*methods.size()));
for (Method method: methods)
{
    // Note: The -1 in this line stands to make the SortedMap work properly when searching for ranges.
    int firstLine=getLineNumberTable().getSourceLine(0)-1;
    List<Method> methodsInTheSameLine=indexOfMethodsByFirstLineNumber.get(firstLine);
    if (methodsInTheSameLine==null)
    {
        methodsInTheSameLine=new ArrayList<Method>();
        indexOfMethodsByFirstLineNumber.put(firstLine,methodsInTheSameLine);
    }
    methodsInTheSameLine.add(method);
}

5. Опубликовать метод для поиска:

public Method getMethodByLine(int lineNumber)
{
    Set<Method> methodsInTheSameLine=this.indexOfMethodsByFirstLineNumber.headMap(lineNumber).lastKey();
    if (methodsInTheSameLine.size()==0)
    {
        // There are no methods method in that line: Absurd.
    }
    else if (methodsInTheSameLine.size()>1)
    {
        // There are more than one method in that line. Hardly probable, but possible.
    }
    else
    {
        // There is one method in that line:
        return methods.get(0);
    }
}

Существует ряд плагинов Maven с открытым исходным кодом, которые анализируют исходный код и отчитываются по каждому методу. Тщательное изучение некоторых из них может быть вашим лучшим выбором.

Примеры включают Checkstyle, FindBugs, PMD, JDepend, JavaNCSS.

Также взгляните на SonarQube.

Другие вопросы по тегам