From 5bb77023b6c36bf041723a2cdac3a613455e5ce2 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Thu, 20 Nov 2025 11:41:21 +0000 Subject: [PATCH 1/8] add deadline --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e3d9c8a..2486cde 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-22041afd0340ce965d47ae6ef1cefeee28c7c493a6346c4f15d667ab976d596c.svg)](https://classroom.github.com/a/9A22t-SS) Разработать standalone приложение, которое имеет следующие возможности: Принимает на вход проект в виде .jar файла From a9619fc1569fa650a8065cceac4d6be7638f01b9 Mon Sep 17 00:00:00 2001 From: Viktor Shamin Date: Sun, 30 Nov 2025 15:33:13 +0300 Subject: [PATCH 2/8] First version --- build.gradle.kts | 9 ++ src/main/java/org/example/Main.java | 65 ++++++++ .../example/analyzer/JarMetricsAnalyzer.java | 41 +++++ .../example/analyzer/MetricsCollector.java | 91 +++++++++++ .../java/org/example/model/ClassInfo.java | 73 +++++++++ .../java/org/example/model/MethodInfo.java | 46 ++++++ src/main/java/org/example/model/Metrics.java | 68 ++++++++ .../output/ConsoleOutputFormatter.java | 37 +++++ .../example/output/JsonOutputFormatter.java | 35 +++++ .../org/example/visitor/AbcMetricVisitor.java | 55 +++++++ .../example/visitor/FieldCountVisitor.java | 22 +++ .../example/visitor/InheritanceVisitor.java | 65 ++++++++ .../visitor/OverriddenMethodsVisitor.java | 145 ++++++++++++++++++ 13 files changed, 752 insertions(+) create mode 100644 src/main/java/org/example/Main.java create mode 100644 src/main/java/org/example/analyzer/JarMetricsAnalyzer.java create mode 100644 src/main/java/org/example/analyzer/MetricsCollector.java create mode 100644 src/main/java/org/example/model/ClassInfo.java create mode 100644 src/main/java/org/example/model/MethodInfo.java create mode 100644 src/main/java/org/example/model/Metrics.java create mode 100644 src/main/java/org/example/output/ConsoleOutputFormatter.java create mode 100644 src/main/java/org/example/output/JsonOutputFormatter.java create mode 100644 src/main/java/org/example/visitor/AbcMetricVisitor.java create mode 100644 src/main/java/org/example/visitor/FieldCountVisitor.java create mode 100644 src/main/java/org/example/visitor/InheritanceVisitor.java create mode 100644 src/main/java/org/example/visitor/OverriddenMethodsVisitor.java diff --git a/build.gradle.kts b/build.gradle.kts index 2b92afd..466180c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,4 +22,13 @@ dependencies { tasks.test { useJUnitPlatform() +} + +tasks.jar { + manifest { + attributes["Main-Class"] = "org.example.Main" + } + + from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) }) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } \ No newline at end of file diff --git a/src/main/java/org/example/Main.java b/src/main/java/org/example/Main.java new file mode 100644 index 0000000..298d1b2 --- /dev/null +++ b/src/main/java/org/example/Main.java @@ -0,0 +1,65 @@ +package org.example; + +import org.example.analyzer.JarMetricsAnalyzer; +import org.example.model.Metrics; +import org.example.output.ConsoleOutputFormatter; +import org.example.output.JsonOutputFormatter; + +import java.io.File; +import java.io.IOException; + +public class Main { + + public static void main(String[] args) { + if (args.length == 0) { + System.exit(1); + } + + String jarFilePath = args[0]; + String outputFilePath = null; + + for (int i = 1; i < args.length; i++) { + if (args[i].equals("--output") && i + 1 < args.length) { + outputFilePath = args[i + 1]; + i++; + } + } + + File jarFile = new File(jarFilePath); + if (!jarFile.exists()) { + System.err.println("Ошибка: JAR файл не найден: " + jarFilePath); + System.exit(1); + } + + if (!jarFile.isFile()) { + System.err.println("Ошибка: Указанный путь не является файлом: " + jarFilePath); + System.exit(1); + } + + try { + System.out.println("Анализ JAR файла: " + jarFilePath); + System.out.println(); + + JarMetricsAnalyzer analyzer = new JarMetricsAnalyzer(); + Metrics metrics = analyzer.analyze(jarFilePath); + + ConsoleOutputFormatter consoleFormatter = new ConsoleOutputFormatter(); + consoleFormatter.print(metrics); + + if (outputFilePath != null) { + JsonOutputFormatter jsonFormatter = new JsonOutputFormatter(); + jsonFormatter.writeToFile(metrics, outputFilePath); + System.out.println("Метрики сохранены в файл: " + outputFilePath); + } + + } catch (IOException e) { + System.err.println("Ошибка при анализе JAR файла: " + e.getMessage()); + e.printStackTrace(); + System.exit(1); + } catch (Exception e) { + System.err.println("Неожиданная ошибка: " + e.getMessage()); + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/src/main/java/org/example/analyzer/JarMetricsAnalyzer.java b/src/main/java/org/example/analyzer/JarMetricsAnalyzer.java new file mode 100644 index 0000000..0a61662 --- /dev/null +++ b/src/main/java/org/example/analyzer/JarMetricsAnalyzer.java @@ -0,0 +1,41 @@ +package org.example.analyzer; + +import org.example.model.Metrics; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public class JarMetricsAnalyzer { + + public Metrics analyze(String jarFilePath) throws IOException { + MetricsCollector collector = new MetricsCollector(); + + try (JarFile jarFile = new JarFile(jarFilePath)) { + Enumeration entries = jarFile.entries(); + + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + + if (entry.getName().endsWith(".class")) { + collector.processClass(jarFile.getInputStream(entry)); + } + } + } + + try (JarFile jarFile = new JarFile(jarFilePath)) { + Enumeration entries = jarFile.entries(); + + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + + if (entry.getName().endsWith(".class")) { + collector.processOverriddenMethods(jarFile.getInputStream(entry)); + } + } + } + + return collector.calculateMetrics(); + } +} diff --git a/src/main/java/org/example/analyzer/MetricsCollector.java b/src/main/java/org/example/analyzer/MetricsCollector.java new file mode 100644 index 0000000..7409b07 --- /dev/null +++ b/src/main/java/org/example/analyzer/MetricsCollector.java @@ -0,0 +1,91 @@ +package org.example.analyzer; + +import org.example.model.ClassInfo; +import org.example.model.Metrics; +import org.example.model.MethodInfo; +import org.example.visitor.*; +import org.objectweb.asm.ClassReader; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +public class MetricsCollector { + private final Map classInfoMap; + private final InheritanceVisitor inheritanceVisitor; + + public MetricsCollector() { + this.classInfoMap = new HashMap<>(); + this.inheritanceVisitor = new InheritanceVisitor(classInfoMap); + } + + public void processClass(InputStream classStream) throws IOException { + ClassReader classReader = new ClassReader(classStream); + String className = classReader.getClassName(); + + ClassInfo classInfo = classInfoMap.computeIfAbsent(className, + k -> new ClassInfo(className, classReader.getSuperName())); + classReader.accept(inheritanceVisitor, 0); + + FieldCountVisitor fieldVisitor = new FieldCountVisitor(classInfo); + classReader.accept(fieldVisitor, 0); + + AbcMetricVisitor abcVisitor = new AbcMetricVisitor(classInfo); + classReader.accept(abcVisitor, 0); + } + + public void processOverriddenMethods(InputStream classStream) throws IOException { + ClassReader classReader = new ClassReader(classStream); + String className = classReader.getClassName(); + ClassInfo classInfo = classInfoMap.get(className); + + if (classInfo != null) { + OverriddenMethodsVisitor overriddenVisitor = new OverriddenMethodsVisitor(classInfo, classInfoMap); + classReader.accept(overriddenVisitor, 0); + classInfo.setOverriddenMethodsCount(overriddenVisitor.getOverriddenMethodsCount()); + } + } + + public Metrics calculateMetrics() { + Metrics metrics = new Metrics(); + + if (classInfoMap.isEmpty()) { + return metrics; + } + + int totalInheritanceDepth = 0; + int maxInheritanceDepth = 0; + int totalAbcMetric = 0; + int totalFields = 0; + int totalOverriddenMethods = 0; + int classCount = classInfoMap.size(); + + for (ClassInfo classInfo : classInfoMap.values()) { + int depth = inheritanceVisitor.calculateInheritanceDepth(classInfo.getName()); + classInfo.setInheritanceDepth(depth); + totalInheritanceDepth += depth; + maxInheritanceDepth = Math.max(maxInheritanceDepth, depth); + + for (MethodInfo method : classInfo.getMethods()) { + totalAbcMetric += method.getAssignmentCount(); + } + + totalFields += classInfo.getFieldCount(); + + totalOverriddenMethods += classInfo.getOverriddenMethodsCount(); + } + + metrics.setMaxInheritanceDepth(maxInheritanceDepth); + metrics.setAvgInheritanceDepth((double) totalInheritanceDepth / classCount); + metrics.setTotalAbcMetric(totalAbcMetric); + metrics.setAvgOverriddenMethods((double) totalOverriddenMethods / classCount); + metrics.setAvgFieldsPerClass((double) totalFields / classCount); + + return metrics; + } + + public Map getClassInfoMap() { + return classInfoMap; + } +} diff --git a/src/main/java/org/example/model/ClassInfo.java b/src/main/java/org/example/model/ClassInfo.java new file mode 100644 index 0000000..c7e05ac --- /dev/null +++ b/src/main/java/org/example/model/ClassInfo.java @@ -0,0 +1,73 @@ +package org.example.model; + +import java.util.ArrayList; +import java.util.List; + +public class ClassInfo { + private final String name; + private final String superName; + private final List methods; + private int fieldCount; + private int inheritanceDepth; + private int overriddenMethodsCount; + + public ClassInfo(String name, String superName) { + this.name = name; + this.superName = superName; + this.methods = new ArrayList<>(); + this.fieldCount = 0; + this.inheritanceDepth = -1; + this.overriddenMethodsCount = 0; + } + + public String getName() { + return name; + } + + public String getSuperName() { + return superName; + } + + public List getMethods() { + return methods; + } + + public void addMethod(MethodInfo method) { + this.methods.add(method); + } + + public int getFieldCount() { + return fieldCount; + } + + public void setFieldCount(int fieldCount) { + this.fieldCount = fieldCount; + } + + public int getInheritanceDepth() { + return inheritanceDepth; + } + + public void setInheritanceDepth(int inheritanceDepth) { + this.inheritanceDepth = inheritanceDepth; + } + + public int getOverriddenMethodsCount() { + return overriddenMethodsCount; + } + + public void setOverriddenMethodsCount(int overriddenMethodsCount) { + this.overriddenMethodsCount = overriddenMethodsCount; + } + + @Override + public String toString() { + return "ClassInfo{" + + "name='" + name + '\'' + + ", superName='" + superName + '\'' + + ", methods=" + methods.size() + + ", fieldCount=" + fieldCount + + ", inheritanceDepth=" + inheritanceDepth + + '}'; + } +} diff --git a/src/main/java/org/example/model/MethodInfo.java b/src/main/java/org/example/model/MethodInfo.java new file mode 100644 index 0000000..5a13f33 --- /dev/null +++ b/src/main/java/org/example/model/MethodInfo.java @@ -0,0 +1,46 @@ +package org.example.model; + +public class MethodInfo { + private final String name; + private final String descriptor; + private int assignmentCount; + + public MethodInfo(String name, String descriptor) { + this.name = name; + this.descriptor = descriptor; + this.assignmentCount = 0; + } + + public String getName() { + return name; + } + + public String getDescriptor() { + return descriptor; + } + + public int getAssignmentCount() { + return assignmentCount; + } + + public void setAssignmentCount(int assignmentCount) { + this.assignmentCount = assignmentCount; + } + + public void incrementAssignmentCount() { + this.assignmentCount++; + } + + public String getSignature() { + return name + descriptor; + } + + @Override + public String toString() { + return "MethodInfo{" + + "name='" + name + '\'' + + ", descriptor='" + descriptor + '\'' + + ", assignmentCount=" + assignmentCount + + '}'; + } +} diff --git a/src/main/java/org/example/model/Metrics.java b/src/main/java/org/example/model/Metrics.java new file mode 100644 index 0000000..9d58a87 --- /dev/null +++ b/src/main/java/org/example/model/Metrics.java @@ -0,0 +1,68 @@ +package org.example.model; + +public class Metrics { + private int maxInheritanceDepth; + private double avgInheritanceDepth; + private int totalAbcMetric; + private double avgOverriddenMethods; + private double avgFieldsPerClass; + + public Metrics() { + this.maxInheritanceDepth = 0; + this.avgInheritanceDepth = 0.0; + this.totalAbcMetric = 0; + this.avgOverriddenMethods = 0.0; + this.avgFieldsPerClass = 0.0; + } + + public int getMaxInheritanceDepth() { + return maxInheritanceDepth; + } + + public void setMaxInheritanceDepth(int maxInheritanceDepth) { + this.maxInheritanceDepth = maxInheritanceDepth; + } + + public double getAvgInheritanceDepth() { + return avgInheritanceDepth; + } + + public void setAvgInheritanceDepth(double avgInheritanceDepth) { + this.avgInheritanceDepth = avgInheritanceDepth; + } + + public int getTotalAbcMetric() { + return totalAbcMetric; + } + + public void setTotalAbcMetric(int totalAbcMetric) { + this.totalAbcMetric = totalAbcMetric; + } + + public double getAvgOverriddenMethods() { + return avgOverriddenMethods; + } + + public void setAvgOverriddenMethods(double avgOverriddenMethods) { + this.avgOverriddenMethods = avgOverriddenMethods; + } + + public double getAvgFieldsPerClass() { + return avgFieldsPerClass; + } + + public void setAvgFieldsPerClass(double avgFieldsPerClass) { + this.avgFieldsPerClass = avgFieldsPerClass; + } + + @Override + public String toString() { + return "Metrics{" + + "maxInheritanceDepth=" + maxInheritanceDepth + + ", avgInheritanceDepth=" + avgInheritanceDepth + + ", totalAbcMetric=" + totalAbcMetric + + ", avgOverriddenMethods=" + avgOverriddenMethods + + ", avgFieldsPerClass=" + avgFieldsPerClass + + '}'; + } +} diff --git a/src/main/java/org/example/output/ConsoleOutputFormatter.java b/src/main/java/org/example/output/ConsoleOutputFormatter.java new file mode 100644 index 0000000..4aa5412 --- /dev/null +++ b/src/main/java/org/example/output/ConsoleOutputFormatter.java @@ -0,0 +1,37 @@ +package org.example.output; + +import org.example.model.Metrics; + +public class ConsoleOutputFormatter { + + public void print(Metrics metrics) { + String separator = "============================================================"; + System.out.println(separator); + System.out.println(" МЕТРИКИ АНАЛИЗА БАЙТКОДА"); + System.out.println(separator); + System.out.println(); + + System.out.printf("%-45s %d%n", + "Максимальная глубина наследования:", + metrics.getMaxInheritanceDepth()); + + System.out.printf("%-45s %.2f%n", + "Средняя глубина наследования:", + metrics.getAvgInheritanceDepth()); + + System.out.printf("%-45s %d%n", + "Метрика ABC (присваивания):", + metrics.getTotalAbcMetric()); + + System.out.printf("%-45s %.2f%n", + "Среднее количество переопределенных методов:", + metrics.getAvgOverriddenMethods()); + + System.out.printf("%-45s %.2f%n", + "Среднее количество полей в классе:", + metrics.getAvgFieldsPerClass()); + + System.out.println(); + System.out.println(separator); + } +} diff --git a/src/main/java/org/example/output/JsonOutputFormatter.java b/src/main/java/org/example/output/JsonOutputFormatter.java new file mode 100644 index 0000000..180282a --- /dev/null +++ b/src/main/java/org/example/output/JsonOutputFormatter.java @@ -0,0 +1,35 @@ +package org.example.output; + +import org.example.model.Metrics; + +import java.io.FileWriter; +import java.io.IOException; + +public class JsonOutputFormatter { + + public void writeToFile(Metrics metrics, String outputPath) throws IOException { + String json = toJson(metrics); + + try (FileWriter writer = new FileWriter(outputPath)) { + writer.write(json); + } + } + + public String toJson(Metrics metrics) { + StringBuilder json = new StringBuilder(); + json.append("{\n"); + json.append(String.format(" \"maxInheritanceDepth\": %d,\n", + metrics.getMaxInheritanceDepth())); + json.append(String.format(" \"avgInheritanceDepth\": %.2f,\n", + metrics.getAvgInheritanceDepth())); + json.append(String.format(" \"totalAbcMetric\": %d,\n", + metrics.getTotalAbcMetric())); + json.append(String.format(" \"avgOverriddenMethods\": %.2f,\n", + metrics.getAvgOverriddenMethods())); + json.append(String.format(" \"avgFieldsPerClass\": %.2f\n", + metrics.getAvgFieldsPerClass())); + json.append("}"); + + return json.toString(); + } +} diff --git a/src/main/java/org/example/visitor/AbcMetricVisitor.java b/src/main/java/org/example/visitor/AbcMetricVisitor.java new file mode 100644 index 0000000..25a16f9 --- /dev/null +++ b/src/main/java/org/example/visitor/AbcMetricVisitor.java @@ -0,0 +1,55 @@ +package org.example.visitor; + +import org.example.model.ClassInfo; +import org.example.model.MethodInfo; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; + +import static org.objectweb.asm.Opcodes.*; + +/** + * Visitor для подсчета ABC метрики (количество присваиваний в локальные + * переменные). + * Делегирует работу внутреннему AbcMethodVisitor для каждого метода. + */ +public class AbcMetricVisitor extends ClassVisitor { + private final ClassInfo classInfo; + + public AbcMetricVisitor(ClassInfo classInfo) { + super(ASM9); + this.classInfo = classInfo; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, + String[] exceptions) { + MethodInfo methodInfo = new MethodInfo(name, descriptor); + classInfo.addMethod(methodInfo); + + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + return new AbcMethodVisitor(mv, methodInfo); + } + + /** + * Внутренний MethodVisitor для отслеживания инструкций присваивания в метод. + */ + private static class AbcMethodVisitor extends MethodVisitor { + private final MethodInfo methodInfo; + + public AbcMethodVisitor(MethodVisitor methodVisitor, MethodInfo methodInfo) { + super(ASM9, methodVisitor); + this.methodInfo = methodInfo; + } + + @Override + public void visitVarInsn(int opcode, int var) { + // Отслеживаем инструкции присваивания в локальные переменные + // ISTORE, LSTORE, FSTORE, DSTORE, ASTORE + if (opcode == ISTORE || opcode == LSTORE || opcode == FSTORE || + opcode == DSTORE || opcode == ASTORE) { + methodInfo.incrementAssignmentCount(); + } + super.visitVarInsn(opcode, var); + } + } +} diff --git a/src/main/java/org/example/visitor/FieldCountVisitor.java b/src/main/java/org/example/visitor/FieldCountVisitor.java new file mode 100644 index 0000000..0d9a8ed --- /dev/null +++ b/src/main/java/org/example/visitor/FieldCountVisitor.java @@ -0,0 +1,22 @@ +package org.example.visitor; + +import org.example.model.ClassInfo; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; + +import static org.objectweb.asm.Opcodes.ASM9; + +public class FieldCountVisitor extends ClassVisitor { + private final ClassInfo classInfo; + + public FieldCountVisitor(ClassInfo classInfo) { + super(ASM9); + this.classInfo = classInfo; + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + classInfo.setFieldCount(classInfo.getFieldCount() + 1); + return super.visitField(access, name, descriptor, signature, value); + } +} diff --git a/src/main/java/org/example/visitor/InheritanceVisitor.java b/src/main/java/org/example/visitor/InheritanceVisitor.java new file mode 100644 index 0000000..308a75b --- /dev/null +++ b/src/main/java/org/example/visitor/InheritanceVisitor.java @@ -0,0 +1,65 @@ +package org.example.visitor; + +import org.example.model.ClassInfo; +import org.objectweb.asm.ClassVisitor; + +import java.util.Map; + +import static org.objectweb.asm.Opcodes.ASM9; + +public class InheritanceVisitor extends ClassVisitor { + private final Map classInfoMap; + + public InheritanceVisitor(Map classInfoMap) { + super(ASM9); + this.classInfoMap = classInfoMap; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + ClassInfo classInfo = classInfoMap.computeIfAbsent(name, k -> new ClassInfo(name, superName)); + + if (classInfo.getSuperName() == null && superName != null) { + classInfoMap.put(name, new ClassInfo(name, superName)); + } + + super.visit(version, access, name, signature, superName, interfaces); + } + + public int calculateInheritanceDepth(String className) { + if (className == null || className.equals("java/lang/Object")) { + return 0; + } + + ClassInfo classInfo = classInfoMap.get(className); + + if (classInfo != null && classInfo.getInheritanceDepth() != -1) { + return classInfo.getInheritanceDepth(); + } + + if (classInfo == null) { + try { + String javaClassName = className.replace('/', '.'); + Class clazz = Class.forName(javaClassName); + Class superClass = clazz.getSuperclass(); + + if (superClass == null) { + return 0; + } + + String superClassName = superClass.getName().replace('.', '/'); + int depth = 1 + calculateInheritanceDepth(superClassName); + return depth; + } catch (ClassNotFoundException e) { + return 1; + } + } + + String superName = classInfo.getSuperName(); + int depth = 1 + calculateInheritanceDepth(superName); + + classInfo.setInheritanceDepth(depth); + + return depth; + } +} diff --git a/src/main/java/org/example/visitor/OverriddenMethodsVisitor.java b/src/main/java/org/example/visitor/OverriddenMethodsVisitor.java new file mode 100644 index 0000000..441a109 --- /dev/null +++ b/src/main/java/org/example/visitor/OverriddenMethodsVisitor.java @@ -0,0 +1,145 @@ +package org.example.visitor; + +import org.example.model.ClassInfo; +import org.example.model.MethodInfo; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; + +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static org.objectweb.asm.Opcodes.ASM9; + +/** + * Visitor для подсчета переопределенных методов в классе. + */ +public class OverriddenMethodsVisitor extends ClassVisitor { + private final ClassInfo classInfo; + private final Map classInfoMap; + private int overriddenMethodsCount; + + public OverriddenMethodsVisitor(ClassInfo classInfo, Map classInfoMap) { + super(ASM9); + this.classInfo = classInfo; + this.classInfoMap = classInfoMap; + this.overriddenMethodsCount = 0; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, + String[] exceptions) { + // Пропускаем конструкторы и статические инициализаторы + if (!name.equals("") && !name.equals("")) { + String methodSignature = name + descriptor; + + // Проверяем, переопределяет ли этот метод метод суперкласса + if (isMethodOverridden(methodSignature, classInfo.getSuperName())) { + overriddenMethodsCount++; + } + } + + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + + /** + * Проверяет, переопределяет ли метод с заданной сигнатурой метод из + * суперкласса. + * + * @param methodSignature сигнатура метода (имя + дескриптор) + * @param superClassName имя суперкласса + * @return true, если метод переопределяет метод суперкласса + */ + private boolean isMethodOverridden(String methodSignature, String superClassName) { + if (superClassName == null || superClassName.equals("java/lang/Object")) { + // Для Object проверяем только стандартные методы + return isObjectMethod(methodSignature); + } + + // Ищем суперкласс в нашем JAR + ClassInfo superClassInfo = classInfoMap.get(superClassName); + + if (superClassInfo != null) { + // Проверяем методы суперкласса из нашего JAR + Set superMethodSignatures = getSuperClassMethodSignatures(superClassInfo); + if (superMethodSignatures.contains(methodSignature)) { + return true; + } + + // Рекурсивно проверяем родителя суперкласса + return isMethodOverridden(methodSignature, superClassInfo.getSuperName()); + } else { + // Пытаемся загрузить класс через ClassLoader + try { + Set externalSuperMethods = getExternalClassMethods(superClassName); + return externalSuperMethods.contains(methodSignature); + } catch (Exception e) { + // Не удалось загрузить класс, пропускаем + return false; + } + } + } + + /** + * Проверяет, является ли метод одним из стандартных методов Object. + */ + private boolean isObjectMethod(String methodSignature) { + return methodSignature.equals("toString()Ljava/lang/String;") || + methodSignature.equals("equals(Ljava/lang/Object;)Z") || + methodSignature.equals("hashCode()I") || + methodSignature.equals("clone()Ljava/lang/Object;") || + methodSignature.equals("finalize()V"); + } + + /** + * Получает сигнатуры всех методов суперкласса из нашего JAR. + */ + private Set getSuperClassMethodSignatures(ClassInfo superClassInfo) { + Set signatures = new HashSet<>(); + for (MethodInfo method : superClassInfo.getMethods()) { + signatures.add(method.getSignature()); + } + return signatures; + } + + /** + * Получает сигнатуры методов внешнего класса через ClassReader. + */ + private Set getExternalClassMethods(String className) throws IOException { + Set methodSignatures = new HashSet<>(); + + // Пытаемся загрузить класс как ресурс + String resourceName = className + ".class"; + InputStream classStream = ClassLoader.getSystemResourceAsStream(resourceName); + + if (classStream != null) { + try { + ClassReader cr = new ClassReader(classStream); + cr.accept(new ClassVisitor(ASM9) { + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, + String signature, String[] exceptions) { + if (!name.equals("") && !name.equals("")) { + methodSignatures.add(name + descriptor); + } + return super.visitMethod(access, name, descriptor, signature, exceptions); + } + }, ClassReader.SKIP_CODE); + } finally { + classStream.close(); + } + } + + return methodSignatures; + } + + /** + * Возвращает количество переопределенных методов. + */ + public int getOverriddenMethodsCount() { + return overriddenMethodsCount; + } +} From a5593ac39d255d6f69450d63409e0cb339cbe664 Mon Sep 17 00:00:00 2001 From: Viktor Shamin Date: Tue, 2 Dec 2025 20:51:56 +0300 Subject: [PATCH 3/8] Refactor --- .../example/output/JsonOutputFormatter.java | 15 +++------ .../org/example/visitor/AbcMetricVisitor.java | 10 ------ .../visitor/OverriddenMethodsVisitor.java | 33 +------------------ 3 files changed, 6 insertions(+), 52 deletions(-) diff --git a/src/main/java/org/example/output/JsonOutputFormatter.java b/src/main/java/org/example/output/JsonOutputFormatter.java index 180282a..67e7bf1 100644 --- a/src/main/java/org/example/output/JsonOutputFormatter.java +++ b/src/main/java/org/example/output/JsonOutputFormatter.java @@ -18,16 +18,11 @@ public void writeToFile(Metrics metrics, String outputPath) throws IOException { public String toJson(Metrics metrics) { StringBuilder json = new StringBuilder(); json.append("{\n"); - json.append(String.format(" \"maxInheritanceDepth\": %d,\n", - metrics.getMaxInheritanceDepth())); - json.append(String.format(" \"avgInheritanceDepth\": %.2f,\n", - metrics.getAvgInheritanceDepth())); - json.append(String.format(" \"totalAbcMetric\": %d,\n", - metrics.getTotalAbcMetric())); - json.append(String.format(" \"avgOverriddenMethods\": %.2f,\n", - metrics.getAvgOverriddenMethods())); - json.append(String.format(" \"avgFieldsPerClass\": %.2f\n", - metrics.getAvgFieldsPerClass())); + json.append(" \"maxInheritanceDepth\": ").append(metrics.getMaxInheritanceDepth()).append(",\n"); + json.append(" \"avgInheritanceDepth\": ").append(metrics.getAvgInheritanceDepth()).append(",\n"); + json.append(" \"totalAbcMetric\": ").append(metrics.getTotalAbcMetric()).append(",\n"); + json.append(" \"avgOverriddenMethods\": ").append(metrics.getAvgOverriddenMethods()).append(",\n"); + json.append(" \"avgFieldsPerClass\": ").append(metrics.getAvgFieldsPerClass()).append("\n"); json.append("}"); return json.toString(); diff --git a/src/main/java/org/example/visitor/AbcMetricVisitor.java b/src/main/java/org/example/visitor/AbcMetricVisitor.java index 25a16f9..12d4611 100644 --- a/src/main/java/org/example/visitor/AbcMetricVisitor.java +++ b/src/main/java/org/example/visitor/AbcMetricVisitor.java @@ -7,11 +7,6 @@ import static org.objectweb.asm.Opcodes.*; -/** - * Visitor для подсчета ABC метрики (количество присваиваний в локальные - * переменные). - * Делегирует работу внутреннему AbcMethodVisitor для каждого метода. - */ public class AbcMetricVisitor extends ClassVisitor { private final ClassInfo classInfo; @@ -30,9 +25,6 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, Str return new AbcMethodVisitor(mv, methodInfo); } - /** - * Внутренний MethodVisitor для отслеживания инструкций присваивания в метод. - */ private static class AbcMethodVisitor extends MethodVisitor { private final MethodInfo methodInfo; @@ -43,8 +35,6 @@ public AbcMethodVisitor(MethodVisitor methodVisitor, MethodInfo methodInfo) { @Override public void visitVarInsn(int opcode, int var) { - // Отслеживаем инструкции присваивания в локальные переменные - // ISTORE, LSTORE, FSTORE, DSTORE, ASTORE if (opcode == ISTORE || opcode == LSTORE || opcode == FSTORE || opcode == DSTORE || opcode == ASTORE) { methodInfo.incrementAssignmentCount(); diff --git a/src/main/java/org/example/visitor/OverriddenMethodsVisitor.java b/src/main/java/org/example/visitor/OverriddenMethodsVisitor.java index 441a109..67a2d1e 100644 --- a/src/main/java/org/example/visitor/OverriddenMethodsVisitor.java +++ b/src/main/java/org/example/visitor/OverriddenMethodsVisitor.java @@ -14,9 +14,6 @@ import static org.objectweb.asm.Opcodes.ASM9; -/** - * Visitor для подсчета переопределенных методов в классе. - */ public class OverriddenMethodsVisitor extends ClassVisitor { private final ClassInfo classInfo; private final Map classInfoMap; @@ -32,11 +29,9 @@ public OverriddenMethodsVisitor(ClassInfo classInfo, Map clas @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { - // Пропускаем конструкторы и статические инициализаторы if (!name.equals("") && !name.equals("")) { String methodSignature = name + descriptor; - // Проверяем, переопределяет ли этот метод метод суперкласса if (isMethodOverridden(methodSignature, classInfo.getSuperName())) { overriddenMethodsCount++; } @@ -45,47 +40,31 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, Str return super.visitMethod(access, name, descriptor, signature, exceptions); } - /** - * Проверяет, переопределяет ли метод с заданной сигнатурой метод из - * суперкласса. - * - * @param methodSignature сигнатура метода (имя + дескриптор) - * @param superClassName имя суперкласса - * @return true, если метод переопределяет метод суперкласса - */ private boolean isMethodOverridden(String methodSignature, String superClassName) { if (superClassName == null || superClassName.equals("java/lang/Object")) { - // Для Object проверяем только стандартные методы return isObjectMethod(methodSignature); } - // Ищем суперкласс в нашем JAR ClassInfo superClassInfo = classInfoMap.get(superClassName); if (superClassInfo != null) { - // Проверяем методы суперкласса из нашего JAR + Set superMethodSignatures = getSuperClassMethodSignatures(superClassInfo); if (superMethodSignatures.contains(methodSignature)) { return true; } - // Рекурсивно проверяем родителя суперкласса return isMethodOverridden(methodSignature, superClassInfo.getSuperName()); } else { - // Пытаемся загрузить класс через ClassLoader try { Set externalSuperMethods = getExternalClassMethods(superClassName); return externalSuperMethods.contains(methodSignature); } catch (Exception e) { - // Не удалось загрузить класс, пропускаем return false; } } } - /** - * Проверяет, является ли метод одним из стандартных методов Object. - */ private boolean isObjectMethod(String methodSignature) { return methodSignature.equals("toString()Ljava/lang/String;") || methodSignature.equals("equals(Ljava/lang/Object;)Z") || @@ -94,9 +73,6 @@ private boolean isObjectMethod(String methodSignature) { methodSignature.equals("finalize()V"); } - /** - * Получает сигнатуры всех методов суперкласса из нашего JAR. - */ private Set getSuperClassMethodSignatures(ClassInfo superClassInfo) { Set signatures = new HashSet<>(); for (MethodInfo method : superClassInfo.getMethods()) { @@ -105,13 +81,9 @@ private Set getSuperClassMethodSignatures(ClassInfo superClassInfo) { return signatures; } - /** - * Получает сигнатуры методов внешнего класса через ClassReader. - */ private Set getExternalClassMethods(String className) throws IOException { Set methodSignatures = new HashSet<>(); - // Пытаемся загрузить класс как ресурс String resourceName = className + ".class"; InputStream classStream = ClassLoader.getSystemResourceAsStream(resourceName); @@ -136,9 +108,6 @@ public MethodVisitor visitMethod(int access, String name, String descriptor, return methodSignatures; } - /** - * Возвращает количество переопределенных методов. - */ public int getOverriddenMethodsCount() { return overriddenMethodsCount; } From ba45ab74a0db6730c62a6eb83702a15c4cda80d0 Mon Sep 17 00:00:00 2001 From: Viktor Shamin Date: Tue, 2 Dec 2025 20:54:05 +0300 Subject: [PATCH 4/8] Refactor v2 --- .../output/ConsoleOutputFormatter.java | 30 ++++--------------- .../example/output/JsonOutputFormatter.java | 15 ++++++---- 2 files changed, 16 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/example/output/ConsoleOutputFormatter.java b/src/main/java/org/example/output/ConsoleOutputFormatter.java index 4aa5412..9cfdc28 100644 --- a/src/main/java/org/example/output/ConsoleOutputFormatter.java +++ b/src/main/java/org/example/output/ConsoleOutputFormatter.java @@ -7,31 +7,13 @@ public class ConsoleOutputFormatter { public void print(Metrics metrics) { String separator = "============================================================"; System.out.println(separator); - System.out.println(" МЕТРИКИ АНАЛИЗА БАЙТКОДА"); + System.out.println("МЕТРИКИ АНАЛИЗА БАЙТКОДА"); System.out.println(separator); - System.out.println(); - - System.out.printf("%-45s %d%n", - "Максимальная глубина наследования:", - metrics.getMaxInheritanceDepth()); - - System.out.printf("%-45s %.2f%n", - "Средняя глубина наследования:", - metrics.getAvgInheritanceDepth()); - - System.out.printf("%-45s %d%n", - "Метрика ABC (присваивания):", - metrics.getTotalAbcMetric()); - - System.out.printf("%-45s %.2f%n", - "Среднее количество переопределенных методов:", - metrics.getAvgOverriddenMethods()); - - System.out.printf("%-45s %.2f%n", - "Среднее количество полей в классе:", - metrics.getAvgFieldsPerClass()); - - System.out.println(); + System.out.println("Максимальная глубина наследования: " + metrics.getMaxInheritanceDepth()); + System.out.println("Средняя глубина наследования: " + metrics.getAvgInheritanceDepth()); + System.out.println("Метрика ABC (присваивания): " + metrics.getTotalAbcMetric()); + System.out.println("Среднее количество переопределенных методов: " + metrics.getAvgOverriddenMethods()); + System.out.println("Среднее количество полей в классе: " + metrics.getAvgFieldsPerClass()); System.out.println(separator); } } diff --git a/src/main/java/org/example/output/JsonOutputFormatter.java b/src/main/java/org/example/output/JsonOutputFormatter.java index 67e7bf1..180282a 100644 --- a/src/main/java/org/example/output/JsonOutputFormatter.java +++ b/src/main/java/org/example/output/JsonOutputFormatter.java @@ -18,11 +18,16 @@ public void writeToFile(Metrics metrics, String outputPath) throws IOException { public String toJson(Metrics metrics) { StringBuilder json = new StringBuilder(); json.append("{\n"); - json.append(" \"maxInheritanceDepth\": ").append(metrics.getMaxInheritanceDepth()).append(",\n"); - json.append(" \"avgInheritanceDepth\": ").append(metrics.getAvgInheritanceDepth()).append(",\n"); - json.append(" \"totalAbcMetric\": ").append(metrics.getTotalAbcMetric()).append(",\n"); - json.append(" \"avgOverriddenMethods\": ").append(metrics.getAvgOverriddenMethods()).append(",\n"); - json.append(" \"avgFieldsPerClass\": ").append(metrics.getAvgFieldsPerClass()).append("\n"); + json.append(String.format(" \"maxInheritanceDepth\": %d,\n", + metrics.getMaxInheritanceDepth())); + json.append(String.format(" \"avgInheritanceDepth\": %.2f,\n", + metrics.getAvgInheritanceDepth())); + json.append(String.format(" \"totalAbcMetric\": %d,\n", + metrics.getTotalAbcMetric())); + json.append(String.format(" \"avgOverriddenMethods\": %.2f,\n", + metrics.getAvgOverriddenMethods())); + json.append(String.format(" \"avgFieldsPerClass\": %.2f\n", + metrics.getAvgFieldsPerClass())); json.append("}"); return json.toString(); From 5d5f008191c687db5d2d27ac68f148a575b0ed37 Mon Sep 17 00:00:00 2001 From: Viktor Shamin Date: Tue, 2 Dec 2025 20:55:05 +0300 Subject: [PATCH 5/8] Refactor v3 --- src/main/java/org/example/Main.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/example/Main.java b/src/main/java/org/example/Main.java index 298d1b2..c371fc6 100644 --- a/src/main/java/org/example/Main.java +++ b/src/main/java/org/example/Main.java @@ -38,7 +38,6 @@ public static void main(String[] args) { try { System.out.println("Анализ JAR файла: " + jarFilePath); - System.out.println(); JarMetricsAnalyzer analyzer = new JarMetricsAnalyzer(); Metrics metrics = analyzer.analyze(jarFilePath); From 8623b94f67f2c5cd444831f2b5b4f80682bb1725 Mon Sep 17 00:00:00 2001 From: Viktor Shamin Date: Thu, 4 Dec 2025 16:56:30 +0300 Subject: [PATCH 6/8] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D1=84=D0=BE?= =?UTF-8?q?=D1=80=D0=BC=D0=B0=D1=82=D1=82=D0=B5=D1=80=D0=B0=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D1=82=D0=BE=D1=87=D0=BA=D0=B8=20=D0=B2=20float=20?= =?UTF-8?q?=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=D1=85=20=D0=B2?= =?UTF-8?q?=20json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/example/output/JsonOutputFormatter.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/example/output/JsonOutputFormatter.java b/src/main/java/org/example/output/JsonOutputFormatter.java index 180282a..187f929 100644 --- a/src/main/java/org/example/output/JsonOutputFormatter.java +++ b/src/main/java/org/example/output/JsonOutputFormatter.java @@ -4,6 +4,7 @@ import java.io.FileWriter; import java.io.IOException; +import java.util.Locale; public class JsonOutputFormatter { @@ -18,15 +19,15 @@ public void writeToFile(Metrics metrics, String outputPath) throws IOException { public String toJson(Metrics metrics) { StringBuilder json = new StringBuilder(); json.append("{\n"); - json.append(String.format(" \"maxInheritanceDepth\": %d,\n", + json.append(String.format(Locale.US, " \"maxInheritanceDepth\": %d,\n", metrics.getMaxInheritanceDepth())); - json.append(String.format(" \"avgInheritanceDepth\": %.2f,\n", + json.append(String.format(Locale.US, " \"avgInheritanceDepth\": %.2f,\n", metrics.getAvgInheritanceDepth())); - json.append(String.format(" \"totalAbcMetric\": %d,\n", + json.append(String.format(Locale.US, " \"totalAbcMetric\": %d,\n", metrics.getTotalAbcMetric())); - json.append(String.format(" \"avgOverriddenMethods\": %.2f,\n", + json.append(String.format(Locale.US, " \"avgOverriddenMethods\": %.2f,\n", metrics.getAvgOverriddenMethods())); - json.append(String.format(" \"avgFieldsPerClass\": %.2f\n", + json.append(String.format(Locale.US, " \"avgFieldsPerClass\": %.2f\n", metrics.getAvgFieldsPerClass())); json.append("}"); From 2077263b2fcdd59afe4d73ad502cbdf35b213d22 Mon Sep 17 00:00:00 2001 From: Viktor Shamin Date: Sat, 6 Dec 2025 15:29:47 +0300 Subject: [PATCH 7/8] =?UTF-8?q?=D0=A3=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20?= =?UTF-8?q?=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=D0=BD=D1=8B=20=D0=B2=D0=B8=D0=B7=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/analyzer/JarMetricsAnalyzer.java | 12 ----- .../example/analyzer/MetricsCollector.java | 20 ++------- .../org/example/visitor/AbcMethodVisitor.java | 24 ++++++++++ .../org/example/visitor/AbcMetricVisitor.java | 45 ------------------- ...sVisitor.java => ClassMetricsVisitor.java} | 32 ++++++++++--- 5 files changed, 52 insertions(+), 81 deletions(-) create mode 100644 src/main/java/org/example/visitor/AbcMethodVisitor.java delete mode 100644 src/main/java/org/example/visitor/AbcMetricVisitor.java rename src/main/java/org/example/visitor/{OverriddenMethodsVisitor.java => ClassMetricsVisitor.java} (75%) diff --git a/src/main/java/org/example/analyzer/JarMetricsAnalyzer.java b/src/main/java/org/example/analyzer/JarMetricsAnalyzer.java index 0a61662..f146939 100644 --- a/src/main/java/org/example/analyzer/JarMetricsAnalyzer.java +++ b/src/main/java/org/example/analyzer/JarMetricsAnalyzer.java @@ -24,18 +24,6 @@ public Metrics analyze(String jarFilePath) throws IOException { } } - try (JarFile jarFile = new JarFile(jarFilePath)) { - Enumeration entries = jarFile.entries(); - - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - - if (entry.getName().endsWith(".class")) { - collector.processOverriddenMethods(jarFile.getInputStream(entry)); - } - } - } - return collector.calculateMetrics(); } } diff --git a/src/main/java/org/example/analyzer/MetricsCollector.java b/src/main/java/org/example/analyzer/MetricsCollector.java index 7409b07..d28da64 100644 --- a/src/main/java/org/example/analyzer/MetricsCollector.java +++ b/src/main/java/org/example/analyzer/MetricsCollector.java @@ -26,25 +26,11 @@ public void processClass(InputStream classStream) throws IOException { ClassInfo classInfo = classInfoMap.computeIfAbsent(className, k -> new ClassInfo(className, classReader.getSuperName())); - classReader.accept(inheritanceVisitor, 0); - FieldCountVisitor fieldVisitor = new FieldCountVisitor(classInfo); - classReader.accept(fieldVisitor, 0); + ClassMetricsVisitor metricsVisitor = new ClassMetricsVisitor(classInfo, classInfoMap); + classReader.accept(metricsVisitor, 0); - AbcMetricVisitor abcVisitor = new AbcMetricVisitor(classInfo); - classReader.accept(abcVisitor, 0); - } - - public void processOverriddenMethods(InputStream classStream) throws IOException { - ClassReader classReader = new ClassReader(classStream); - String className = classReader.getClassName(); - ClassInfo classInfo = classInfoMap.get(className); - - if (classInfo != null) { - OverriddenMethodsVisitor overriddenVisitor = new OverriddenMethodsVisitor(classInfo, classInfoMap); - classReader.accept(overriddenVisitor, 0); - classInfo.setOverriddenMethodsCount(overriddenVisitor.getOverriddenMethodsCount()); - } + classInfo.setOverriddenMethodsCount(metricsVisitor.getOverriddenMethodsCount()); } public Metrics calculateMetrics() { diff --git a/src/main/java/org/example/visitor/AbcMethodVisitor.java b/src/main/java/org/example/visitor/AbcMethodVisitor.java new file mode 100644 index 0000000..07a4343 --- /dev/null +++ b/src/main/java/org/example/visitor/AbcMethodVisitor.java @@ -0,0 +1,24 @@ +package org.example.visitor; + +import org.example.model.MethodInfo; +import org.objectweb.asm.MethodVisitor; + +import static org.objectweb.asm.Opcodes.*; + +public class AbcMethodVisitor extends MethodVisitor { + private final MethodInfo methodInfo; + + public AbcMethodVisitor(MethodVisitor methodVisitor, MethodInfo methodInfo) { + super(ASM9, methodVisitor); + this.methodInfo = methodInfo; + } + + @Override + public void visitVarInsn(int opcode, int var) { + if (opcode == ISTORE || opcode == LSTORE || opcode == FSTORE || + opcode == DSTORE || opcode == ASTORE) { + methodInfo.incrementAssignmentCount(); + } + super.visitVarInsn(opcode, var); + } +} diff --git a/src/main/java/org/example/visitor/AbcMetricVisitor.java b/src/main/java/org/example/visitor/AbcMetricVisitor.java deleted file mode 100644 index 12d4611..0000000 --- a/src/main/java/org/example/visitor/AbcMetricVisitor.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.example.visitor; - -import org.example.model.ClassInfo; -import org.example.model.MethodInfo; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; - -import static org.objectweb.asm.Opcodes.*; - -public class AbcMetricVisitor extends ClassVisitor { - private final ClassInfo classInfo; - - public AbcMetricVisitor(ClassInfo classInfo) { - super(ASM9); - this.classInfo = classInfo; - } - - @Override - public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, - String[] exceptions) { - MethodInfo methodInfo = new MethodInfo(name, descriptor); - classInfo.addMethod(methodInfo); - - MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); - return new AbcMethodVisitor(mv, methodInfo); - } - - private static class AbcMethodVisitor extends MethodVisitor { - private final MethodInfo methodInfo; - - public AbcMethodVisitor(MethodVisitor methodVisitor, MethodInfo methodInfo) { - super(ASM9, methodVisitor); - this.methodInfo = methodInfo; - } - - @Override - public void visitVarInsn(int opcode, int var) { - if (opcode == ISTORE || opcode == LSTORE || opcode == FSTORE || - opcode == DSTORE || opcode == ASTORE) { - methodInfo.incrementAssignmentCount(); - } - super.visitVarInsn(opcode, var); - } - } -} diff --git a/src/main/java/org/example/visitor/OverriddenMethodsVisitor.java b/src/main/java/org/example/visitor/ClassMetricsVisitor.java similarity index 75% rename from src/main/java/org/example/visitor/OverriddenMethodsVisitor.java rename to src/main/java/org/example/visitor/ClassMetricsVisitor.java index 67a2d1e..27d97be 100644 --- a/src/main/java/org/example/visitor/OverriddenMethodsVisitor.java +++ b/src/main/java/org/example/visitor/ClassMetricsVisitor.java @@ -4,6 +4,7 @@ import org.example.model.MethodInfo; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import java.io.IOException; @@ -12,32 +13,51 @@ import java.util.Map; import java.util.Set; -import static org.objectweb.asm.Opcodes.ASM9; +import static org.objectweb.asm.Opcodes.*; -public class OverriddenMethodsVisitor extends ClassVisitor { +public class ClassMetricsVisitor extends ClassVisitor { private final ClassInfo classInfo; private final Map classInfoMap; private int overriddenMethodsCount; - public OverriddenMethodsVisitor(ClassInfo classInfo, Map classInfoMap) { + public ClassMetricsVisitor(ClassInfo classInfo, Map classInfoMap) { super(ASM9); this.classInfo = classInfo; this.classInfoMap = classInfoMap; this.overriddenMethodsCount = 0; } + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + ClassInfo existingClassInfo = classInfoMap.computeIfAbsent(name, k -> new ClassInfo(name, superName)); + if (existingClassInfo.getSuperName() == null && superName != null) { + classInfoMap.put(name, new ClassInfo(name, superName)); + } + + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + classInfo.setFieldCount(classInfo.getFieldCount() + 1); + return super.visitField(access, name, descriptor, signature, value); + } + @Override public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + MethodInfo methodInfo = new MethodInfo(name, descriptor); + classInfo.addMethod(methodInfo); + if (!name.equals("") && !name.equals("")) { String methodSignature = name + descriptor; - if (isMethodOverridden(methodSignature, classInfo.getSuperName())) { overriddenMethodsCount++; } } - return super.visitMethod(access, name, descriptor, signature, exceptions); + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + return new AbcMethodVisitor(mv, methodInfo); } private boolean isMethodOverridden(String methodSignature, String superClassName) { @@ -48,12 +68,10 @@ private boolean isMethodOverridden(String methodSignature, String superClassName ClassInfo superClassInfo = classInfoMap.get(superClassName); if (superClassInfo != null) { - Set superMethodSignatures = getSuperClassMethodSignatures(superClassInfo); if (superMethodSignatures.contains(methodSignature)) { return true; } - return isMethodOverridden(methodSignature, superClassInfo.getSuperName()); } else { try { From feb4665da92ea64180d8dd9dd72ddb02fa3c1984 Mon Sep 17 00:00:00 2001 From: Viktor Shamin Date: Sat, 6 Dec 2025 15:30:52 +0300 Subject: [PATCH 8/8] =?UTF-8?q?=D0=A3=D0=B4=D0=B0=D0=BB=D0=B8=D1=82=D1=8C?= =?UTF-8?q?=20=D0=BB=D0=B8=D1=88=D0=BD=D0=B8=D0=B9=20=D0=B2=D0=B8=D0=B7?= =?UTF-8?q?=D0=B8=D1=82=D0=BE=D1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../example/visitor/FieldCountVisitor.java | 22 ------------------- 1 file changed, 22 deletions(-) delete mode 100644 src/main/java/org/example/visitor/FieldCountVisitor.java diff --git a/src/main/java/org/example/visitor/FieldCountVisitor.java b/src/main/java/org/example/visitor/FieldCountVisitor.java deleted file mode 100644 index 0d9a8ed..0000000 --- a/src/main/java/org/example/visitor/FieldCountVisitor.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.example.visitor; - -import org.example.model.ClassInfo; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.FieldVisitor; - -import static org.objectweb.asm.Opcodes.ASM9; - -public class FieldCountVisitor extends ClassVisitor { - private final ClassInfo classInfo; - - public FieldCountVisitor(ClassInfo classInfo) { - super(ASM9); - this.classInfo = classInfo; - } - - @Override - public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { - classInfo.setFieldCount(classInfo.getFieldCount() + 1); - return super.visitField(access, name, descriptor, signature, value); - } -}