From c499a9b1016833fb220854f54de0ae81c1070356 Mon Sep 17 00:00:00 2001 From: jbisss Date: Wed, 26 Nov 2025 15:40:07 +0300 Subject: [PATCH 1/3] [lab_3] code --- src/main/java/org/example/Example.java | 2 - src/main/java/org/example/LabMain.java | 42 ++++++++++++++ .../java/org/example/visitor/AbcVisitor.java | 49 ++++++++++++++++ .../org/example/visitor/ClassMapVisitor.java | 56 +++++++++++++++++++ .../java/org/example/workers/AbcWorker.java | 28 ++++++++++ .../workers/AverageDepthCounterWorker.java | 49 ++++++++++++++++ .../workers/AverageFieldsCountWorker.java | 28 ++++++++++ .../AverageOverridesCounterWorker.java | 49 ++++++++++++++++ .../java/org/example/workers/BaseWorker.java | 30 ++++++++++ .../workers/MaxDepthCounterWorker.java | 52 +++++++++++++++++ 10 files changed, 383 insertions(+), 2 deletions(-) create mode 100644 src/main/java/org/example/LabMain.java create mode 100644 src/main/java/org/example/visitor/AbcVisitor.java create mode 100644 src/main/java/org/example/visitor/ClassMapVisitor.java create mode 100644 src/main/java/org/example/workers/AbcWorker.java create mode 100644 src/main/java/org/example/workers/AverageDepthCounterWorker.java create mode 100644 src/main/java/org/example/workers/AverageFieldsCountWorker.java create mode 100644 src/main/java/org/example/workers/AverageOverridesCounterWorker.java create mode 100644 src/main/java/org/example/workers/BaseWorker.java create mode 100644 src/main/java/org/example/workers/MaxDepthCounterWorker.java diff --git a/src/main/java/org/example/Example.java b/src/main/java/org/example/Example.java index 52d0abe..78df736 100644 --- a/src/main/java/org/example/Example.java +++ b/src/main/java/org/example/Example.java @@ -11,8 +11,6 @@ public class Example { public static void main(String[] args) throws IOException { -// var printer = new ByteCodePrinter(); -// printer.printBubbleSortBytecode(); try (JarFile sampleJar = new JarFile("src/main/resources/sample.jar")) { Enumeration enumeration = sampleJar.entries(); diff --git a/src/main/java/org/example/LabMain.java b/src/main/java/org/example/LabMain.java new file mode 100644 index 0000000..4a87db6 --- /dev/null +++ b/src/main/java/org/example/LabMain.java @@ -0,0 +1,42 @@ +package org.example; + +import org.example.visitor.AbcVisitor; +import org.example.visitor.ClassMapVisitor; +import org.example.workers.*; + +import java.io.IOException; + +public class LabMain { + + private static final String PATH_TO_JAR = "src/main/resources/sample.jar"; + + public static void main(String[] args) throws IOException { + ClassMapVisitor classMapVisitor = new ClassMapVisitor(); + AbcVisitor abcVisitor = new AbcVisitor(); + + System.out.println("-----------------------------------------------------"); + MaxDepthCounterWorker maxDepthCounterWorker = new MaxDepthCounterWorker(); + maxDepthCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + + System.out.println("-----------------------------------------------------"); + + AverageDepthCounterWorker averageDepthCounterWorker = new AverageDepthCounterWorker(); + averageDepthCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + + System.out.println("-----------------------------------------------------"); + + AverageOverridesCounterWorker averageOverridesCounterWorker = new AverageOverridesCounterWorker(); + averageOverridesCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + + System.out.println("-----------------------------------------------------"); + + AverageFieldsCountWorker averageFieldsCountWorker = new AverageFieldsCountWorker(); + averageFieldsCountWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + + System.out.println("-----------------------------------------------------"); + + AbcWorker abcWorker = new AbcWorker(); + abcWorker.doTheJob(PATH_TO_JAR, abcVisitor); + System.out.println("-----------------------------------------------------"); + } +} diff --git a/src/main/java/org/example/visitor/AbcVisitor.java b/src/main/java/org/example/visitor/AbcVisitor.java new file mode 100644 index 0000000..d136759 --- /dev/null +++ b/src/main/java/org/example/visitor/AbcVisitor.java @@ -0,0 +1,49 @@ +package org.example.visitor; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.util.HashMap; +import java.util.Map; + +public class AbcVisitor extends ClassVisitor { + + private String className; + private final Map assignmentCount = new HashMap<>(); + + public AbcVisitor() { + super(Opcodes.ASM9); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + className = name; + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions); + return new MethodVisitor(Opcodes.ASM9, mv) { + + @Override + public void visitVarInsn(int opcode, int var) { + if ((opcode >= Opcodes.ISTORE && opcode <= 78) || opcode == Opcodes.IINC) { + assignmentCount.merge(className, 1, Integer::sum); + } + super.visitVarInsn(opcode, var); + } + + @Override + public void visitIincInsn(int var, int increment) { + assignmentCount.merge(className, 1, Integer::sum); + super.visitIincInsn(var, increment); + } + }; + } + + public Map getAssignmentCount() { + return assignmentCount; + } +} diff --git a/src/main/java/org/example/visitor/ClassMapVisitor.java b/src/main/java/org/example/visitor/ClassMapVisitor.java new file mode 100644 index 0000000..dff9ddf --- /dev/null +++ b/src/main/java/org/example/visitor/ClassMapVisitor.java @@ -0,0 +1,56 @@ +package org.example.visitor; + +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.objectweb.asm.Opcodes.ASM8; + +public class ClassMapVisitor extends ClassVisitor { + + private final Map superMap = new HashMap<>(); + + private final Map fieldCount = new HashMap<>(); + + private final Map> methods = new HashMap<>(); + + public ClassMapVisitor() { + super(ASM8); + } + + public Map getSuperMap() { + return superMap; + } + + public Map getFieldCount() { + return fieldCount; + } + + public Map> getMethods() { + return methods; + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + superMap.put(name, superName); + } + + @Override + public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { + fieldCount.merge(name, 1, Integer::sum); + return super.visitField(access, name, descriptor, signature, value); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { + methods.computeIfAbsent(name, k -> new ArrayList<>()) + .add(name + descriptor); + + return super.visitMethod(access, name, descriptor, signature, exceptions); + } +} diff --git a/src/main/java/org/example/workers/AbcWorker.java b/src/main/java/org/example/workers/AbcWorker.java new file mode 100644 index 0000000..170f94d --- /dev/null +++ b/src/main/java/org/example/workers/AbcWorker.java @@ -0,0 +1,28 @@ +package org.example.workers; + +import org.example.visitor.AbcVisitor; +import org.objectweb.asm.ClassVisitor; + +import java.io.IOException; +import java.util.Map; + +public class AbcWorker extends BaseWorker { + + @Override + public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { + loadJar(pathToJar, visitor); + Map assignmentCount = ((AbcVisitor) visitor).getAssignmentCount(); + + printAbc(assignmentCount); + } + + private void printAbc(Map assignmentCount) { + double avgAssignments = assignmentCount.values() + .stream() + .mapToInt(i -> i) + .average() + .orElse(0.0); + + System.out.println("Average assignments count: " + avgAssignments); + } +} diff --git a/src/main/java/org/example/workers/AverageDepthCounterWorker.java b/src/main/java/org/example/workers/AverageDepthCounterWorker.java new file mode 100644 index 0000000..6a2a135 --- /dev/null +++ b/src/main/java/org/example/workers/AverageDepthCounterWorker.java @@ -0,0 +1,49 @@ +package org.example.workers; + +import org.example.visitor.ClassMapVisitor; +import org.objectweb.asm.ClassVisitor; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class AverageDepthCounterWorker extends BaseWorker { + + @Override + public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { + loadJar(pathToJar, visitor); + Map classMap = ((ClassMapVisitor) visitor).getSuperMap(); + Map depths = computeDepths(classMap); + + printAverageDepth(depths); + } + + private Map computeDepths(Map classMap) { + Map result = new HashMap<>(); + for (String clazz : classMap.keySet()) { + result.put(clazz, depthOf(clazz, classMap)); + } + return result; + } + + private int depthOf(String cls, Map classMap) { + int depth = 0; + String current = cls; + + while (true) { + String parent = classMap.get(current); + if (parent == null) return depth; + depth++; + current = parent; + } + } + + private void printAverageDepth(Map depths) { + double average = depths.values().stream() + .mapToDouble(i -> i) + .average() + .orElse(0); + + System.out.println("Average depth: " + average); + } +} diff --git a/src/main/java/org/example/workers/AverageFieldsCountWorker.java b/src/main/java/org/example/workers/AverageFieldsCountWorker.java new file mode 100644 index 0000000..ce93c04 --- /dev/null +++ b/src/main/java/org/example/workers/AverageFieldsCountWorker.java @@ -0,0 +1,28 @@ +package org.example.workers; + +import org.example.visitor.ClassMapVisitor; +import org.objectweb.asm.ClassVisitor; + +import java.io.IOException; +import java.util.Map; + +public class AverageFieldsCountWorker extends BaseWorker { + + @Override + public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { + loadJar(pathToJar, visitor); + Map fieldCount = ((ClassMapVisitor) visitor).getFieldCount(); + + printAverageFieldsCount(fieldCount); + } + + private void printAverageFieldsCount(Map fieldCount) { + double avgFields = fieldCount.values() + .stream() + .mapToInt(i -> i) + .average() + .orElse(0); + + System.out.println("Average fields amount: " + avgFields); + } +} diff --git a/src/main/java/org/example/workers/AverageOverridesCounterWorker.java b/src/main/java/org/example/workers/AverageOverridesCounterWorker.java new file mode 100644 index 0000000..a1d0eb1 --- /dev/null +++ b/src/main/java/org/example/workers/AverageOverridesCounterWorker.java @@ -0,0 +1,49 @@ +package org.example.workers; + +import org.example.visitor.ClassMapVisitor; +import org.objectweb.asm.ClassVisitor; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class AverageOverridesCounterWorker extends BaseWorker { + + private final Map overriddenCount = new HashMap<>(); + + @Override + public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { + loadJar(pathToJar, visitor); + Map> methods = ((ClassMapVisitor) visitor).getMethods(); + Map superMap = ((ClassMapVisitor) visitor).getSuperMap(); + for (String cls : methods.keySet()) { + int count = 0; + String current = superMap.get(cls); + + while (current != null) { + + List parentMethods = methods.get(current); + if (parentMethods != null) { + for (String m : methods.get(cls)) { + if (parentMethods.contains(m)) { + count++; + } + } + } + + current = superMap.get(current); + } + + overriddenCount.put(cls, count); + } + printOverriddenCounts(); + } + + private void printOverriddenCounts() { + System.out.println("Overrides counts:"); + for (String cls : overriddenCount.keySet()) { + System.out.println(cls + " - " + overriddenCount.get(cls)); + } + } +} diff --git a/src/main/java/org/example/workers/BaseWorker.java b/src/main/java/org/example/workers/BaseWorker.java new file mode 100644 index 0000000..d6727e3 --- /dev/null +++ b/src/main/java/org/example/workers/BaseWorker.java @@ -0,0 +1,30 @@ +package org.example.workers; + +import org.example.visitor.ClassMapVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; + +abstract public class BaseWorker { + + abstract public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException; + + void loadJar(String jarPath, ClassVisitor visitor) throws IOException { + try (InputStream in = new FileInputStream(jarPath); + JarInputStream jar = new JarInputStream(in)) { + + JarEntry entry; + while ((entry = jar.getNextJarEntry()) != null) { + if (!entry.getName().endsWith(".class")) continue; + + ClassReader cr = new ClassReader(jar); + cr.accept(visitor, 0); + } + } + } +} diff --git a/src/main/java/org/example/workers/MaxDepthCounterWorker.java b/src/main/java/org/example/workers/MaxDepthCounterWorker.java new file mode 100644 index 0000000..b736a26 --- /dev/null +++ b/src/main/java/org/example/workers/MaxDepthCounterWorker.java @@ -0,0 +1,52 @@ +package org.example.workers; + +import org.example.visitor.ClassMapVisitor; +import org.objectweb.asm.ClassVisitor; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class MaxDepthCounterWorker extends BaseWorker { + + @Override + public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { + loadJar(pathToJar, visitor); + Map classMap = ((ClassMapVisitor) visitor).getSuperMap(); + Map depths = computeDepths(classMap); + + printMaxDepth(depths); + } + + private Map computeDepths(Map classMap) { + Map result = new HashMap<>(); + for (String clazz : classMap.keySet()) { + result.put(clazz, depthOf(clazz, classMap)); + } + return result; + } + + private int depthOf(String cls, Map classMap) { + int depth = 0; + String current = cls; + + while (true) { + String parent = classMap.get(current); + if (parent == null) return depth; + depth++; + current = parent; + } + } + + private void printMaxDepth(Map depths) { + Map.Entry maxEntry = depths.entrySet() + .stream() + .max(Map.Entry.comparingByValue()) + .orElse(null); + + if (maxEntry != null) { + System.out.println("Class with max depth: " + maxEntry.getKey()); + System.out.println("Max depth: " + maxEntry.getValue()); + } + } +} From 76e5831162fc424a6393250c01522d49a04e0f5b Mon Sep 17 00:00:00 2001 From: jbisss Date: Wed, 26 Nov 2025 17:29:45 +0300 Subject: [PATCH 2/3] [lab_3] PrintStream --- output.txt | 16 ++++++ src/main/java/org/example/LabMain.java | 51 +++++++++++++++---- .../java/org/example/workers/AbcWorker.java | 10 ++-- .../workers/AverageDepthCounterWorker.java | 10 ++-- .../workers/AverageFieldsCountWorker.java | 10 ++-- .../AverageOverridesCounterWorker.java | 12 +++-- .../java/org/example/workers/BaseWorker.java | 7 +-- .../workers/MaxDepthCounterWorker.java | 12 +++-- 8 files changed, 90 insertions(+), 38 deletions(-) create mode 100644 output.txt diff --git a/output.txt b/output.txt new file mode 100644 index 0000000..0993f75 --- /dev/null +++ b/output.txt @@ -0,0 +1,16 @@ +----------------------------------------------------- +Class with max depth: P1 +Max depth: 2 +----------------------------------------------------- +Average depth: 1.5 +----------------------------------------------------- +Overrides counts: +getX - 0 +getY - 0 +box - 0 + - 0 +----------------------------------------------------- +Average fields amount: 9.333333333333334 +----------------------------------------------------- +Average assignments count: 6.0 +----------------------------------------------------- diff --git a/src/main/java/org/example/LabMain.java b/src/main/java/org/example/LabMain.java index 4a87db6..23efc69 100644 --- a/src/main/java/org/example/LabMain.java +++ b/src/main/java/org/example/LabMain.java @@ -4,39 +4,68 @@ import org.example.visitor.ClassMapVisitor; import org.example.workers.*; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; public class LabMain { private static final String PATH_TO_JAR = "src/main/resources/sample.jar"; public static void main(String[] args) throws IOException { + boolean toFile; + try { + toFile = Boolean.parseBoolean(args[0]); + } catch (Exception e) { + toFile = false; + } + ClassMapVisitor classMapVisitor = new ClassMapVisitor(); AbcVisitor abcVisitor = new AbcVisitor(); - System.out.println("-----------------------------------------------------"); + PrintStream ps = null; + try { + OutputStream os; + if (toFile) { + os = new FileOutputStream("output.txt"); + } else { + os = System.out; + } + + ps = new PrintStream(os); + } catch (IOException e) { + e.printStackTrace(); + } + + assert ps != null; + ps.println("-----------------------------------------------------"); MaxDepthCounterWorker maxDepthCounterWorker = new MaxDepthCounterWorker(); - maxDepthCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + maxDepthCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor, ps); - System.out.println("-----------------------------------------------------"); + ps.println("-----------------------------------------------------"); AverageDepthCounterWorker averageDepthCounterWorker = new AverageDepthCounterWorker(); - averageDepthCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + averageDepthCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor, ps); - System.out.println("-----------------------------------------------------"); + ps.println("-----------------------------------------------------"); AverageOverridesCounterWorker averageOverridesCounterWorker = new AverageOverridesCounterWorker(); - averageOverridesCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + averageOverridesCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor, ps); - System.out.println("-----------------------------------------------------"); + ps.println("-----------------------------------------------------"); AverageFieldsCountWorker averageFieldsCountWorker = new AverageFieldsCountWorker(); - averageFieldsCountWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + averageFieldsCountWorker.doTheJob(PATH_TO_JAR, classMapVisitor, ps); - System.out.println("-----------------------------------------------------"); + ps.println("-----------------------------------------------------"); AbcWorker abcWorker = new AbcWorker(); - abcWorker.doTheJob(PATH_TO_JAR, abcVisitor); - System.out.println("-----------------------------------------------------"); + abcWorker.doTheJob(PATH_TO_JAR, abcVisitor, ps); + ps.println("-----------------------------------------------------"); + + if (toFile) { + ps.close(); + } } } diff --git a/src/main/java/org/example/workers/AbcWorker.java b/src/main/java/org/example/workers/AbcWorker.java index 170f94d..306106b 100644 --- a/src/main/java/org/example/workers/AbcWorker.java +++ b/src/main/java/org/example/workers/AbcWorker.java @@ -4,25 +4,27 @@ import org.objectweb.asm.ClassVisitor; import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; import java.util.Map; public class AbcWorker extends BaseWorker { @Override - public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { + public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException { loadJar(pathToJar, visitor); Map assignmentCount = ((AbcVisitor) visitor).getAssignmentCount(); - printAbc(assignmentCount); + printAbc(assignmentCount, ps); } - private void printAbc(Map assignmentCount) { + private void printAbc(Map assignmentCount, PrintStream ps) { double avgAssignments = assignmentCount.values() .stream() .mapToInt(i -> i) .average() .orElse(0.0); - System.out.println("Average assignments count: " + avgAssignments); + ps.println("Average assignments count: " + avgAssignments); } } diff --git a/src/main/java/org/example/workers/AverageDepthCounterWorker.java b/src/main/java/org/example/workers/AverageDepthCounterWorker.java index 6a2a135..7c011a7 100644 --- a/src/main/java/org/example/workers/AverageDepthCounterWorker.java +++ b/src/main/java/org/example/workers/AverageDepthCounterWorker.java @@ -4,18 +4,20 @@ import org.objectweb.asm.ClassVisitor; import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; import java.util.HashMap; import java.util.Map; public class AverageDepthCounterWorker extends BaseWorker { @Override - public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { + public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException { loadJar(pathToJar, visitor); Map classMap = ((ClassMapVisitor) visitor).getSuperMap(); Map depths = computeDepths(classMap); - printAverageDepth(depths); + printAverageDepth(depths, ps); } private Map computeDepths(Map classMap) { @@ -38,12 +40,12 @@ private int depthOf(String cls, Map classMap) { } } - private void printAverageDepth(Map depths) { + private void printAverageDepth(Map depths, PrintStream ps) { double average = depths.values().stream() .mapToDouble(i -> i) .average() .orElse(0); - System.out.println("Average depth: " + average); + ps.println("Average depth: " + average); } } diff --git a/src/main/java/org/example/workers/AverageFieldsCountWorker.java b/src/main/java/org/example/workers/AverageFieldsCountWorker.java index ce93c04..7bc5449 100644 --- a/src/main/java/org/example/workers/AverageFieldsCountWorker.java +++ b/src/main/java/org/example/workers/AverageFieldsCountWorker.java @@ -4,25 +4,27 @@ import org.objectweb.asm.ClassVisitor; import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; import java.util.Map; public class AverageFieldsCountWorker extends BaseWorker { @Override - public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { + public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException { loadJar(pathToJar, visitor); Map fieldCount = ((ClassMapVisitor) visitor).getFieldCount(); - printAverageFieldsCount(fieldCount); + printAverageFieldsCount(fieldCount, ps); } - private void printAverageFieldsCount(Map fieldCount) { + private void printAverageFieldsCount(Map fieldCount, PrintStream ps) { double avgFields = fieldCount.values() .stream() .mapToInt(i -> i) .average() .orElse(0); - System.out.println("Average fields amount: " + avgFields); + ps.println("Average fields amount: " + avgFields); } } diff --git a/src/main/java/org/example/workers/AverageOverridesCounterWorker.java b/src/main/java/org/example/workers/AverageOverridesCounterWorker.java index a1d0eb1..00b9ca3 100644 --- a/src/main/java/org/example/workers/AverageOverridesCounterWorker.java +++ b/src/main/java/org/example/workers/AverageOverridesCounterWorker.java @@ -4,6 +4,8 @@ import org.objectweb.asm.ClassVisitor; import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -13,7 +15,7 @@ public class AverageOverridesCounterWorker extends BaseWorker { private final Map overriddenCount = new HashMap<>(); @Override - public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { + public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException { loadJar(pathToJar, visitor); Map> methods = ((ClassMapVisitor) visitor).getMethods(); Map superMap = ((ClassMapVisitor) visitor).getSuperMap(); @@ -37,13 +39,13 @@ public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException overriddenCount.put(cls, count); } - printOverriddenCounts(); + printOverriddenCounts(ps); } - private void printOverriddenCounts() { - System.out.println("Overrides counts:"); + private void printOverriddenCounts(PrintStream ps) { + ps.println("Overrides counts:"); for (String cls : overriddenCount.keySet()) { - System.out.println(cls + " - " + overriddenCount.get(cls)); + ps.println(cls + " - " + overriddenCount.get(cls)); } } } diff --git a/src/main/java/org/example/workers/BaseWorker.java b/src/main/java/org/example/workers/BaseWorker.java index d6727e3..4100056 100644 --- a/src/main/java/org/example/workers/BaseWorker.java +++ b/src/main/java/org/example/workers/BaseWorker.java @@ -1,18 +1,15 @@ package org.example.workers; -import org.example.visitor.ClassMapVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; abstract public class BaseWorker { - abstract public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException; + abstract public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException; void loadJar(String jarPath, ClassVisitor visitor) throws IOException { try (InputStream in = new FileInputStream(jarPath); diff --git a/src/main/java/org/example/workers/MaxDepthCounterWorker.java b/src/main/java/org/example/workers/MaxDepthCounterWorker.java index b736a26..1af03ef 100644 --- a/src/main/java/org/example/workers/MaxDepthCounterWorker.java +++ b/src/main/java/org/example/workers/MaxDepthCounterWorker.java @@ -4,18 +4,20 @@ import org.objectweb.asm.ClassVisitor; import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; import java.util.HashMap; import java.util.Map; public class MaxDepthCounterWorker extends BaseWorker { @Override - public void doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { + public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException { loadJar(pathToJar, visitor); Map classMap = ((ClassMapVisitor) visitor).getSuperMap(); Map depths = computeDepths(classMap); - printMaxDepth(depths); + printMaxDepth(depths, ps); } private Map computeDepths(Map classMap) { @@ -38,15 +40,15 @@ private int depthOf(String cls, Map classMap) { } } - private void printMaxDepth(Map depths) { + private void printMaxDepth(Map depths, PrintStream ps) { Map.Entry maxEntry = depths.entrySet() .stream() .max(Map.Entry.comparingByValue()) .orElse(null); if (maxEntry != null) { - System.out.println("Class with max depth: " + maxEntry.getKey()); - System.out.println("Max depth: " + maxEntry.getValue()); + ps.println("Class with max depth: " + maxEntry.getKey()); + ps.println("Max depth: " + maxEntry.getValue()); } } } From 9d52b960ee75115779a99ad5170c4d238f604c51 Mon Sep 17 00:00:00 2001 From: jbisss Date: Tue, 2 Dec 2025 12:55:01 +0300 Subject: [PATCH 3/3] [lab_3] B and C to ABC, output as JSON --- build.gradle.kts | 1 + output.txt | 62 ++++++++++++++----- src/main/java/org/example/LabMain.java | 43 ++++++++----- src/main/java/org/example/StatItemDto.java | 43 +++++++++++++ .../java/org/example/visitor/AbcVisitor.java | 61 ++++++++++++++++++ .../java/org/example/workers/AbcWorker.java | 34 +++++++--- .../workers/AverageDepthCounterWorker.java | 13 ++-- .../workers/AverageFieldsCountWorker.java | 11 ++-- .../AverageOverridesCounterWorker.java | 15 +++-- .../java/org/example/workers/BaseWorker.java | 3 +- .../workers/MaxDepthCounterWorker.java | 15 +++-- .../java/org/example/workers/MetricEnum.java | 10 +++ 12 files changed, 249 insertions(+), 62 deletions(-) create mode 100644 src/main/java/org/example/StatItemDto.java create mode 100644 src/main/java/org/example/workers/MetricEnum.java diff --git a/build.gradle.kts b/build.gradle.kts index 2b92afd..046922b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,6 +14,7 @@ dependencies { implementation("org.ow2.asm:asm-tree:9.5") implementation("org.ow2.asm:asm-analysis:9.5") implementation("org.ow2.asm:asm-util:9.5") + implementation("com.fasterxml.jackson.core:jackson-databind:2.17.0") testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/output.txt b/output.txt index 0993f75..46c6bca 100644 --- a/output.txt +++ b/output.txt @@ -1,16 +1,46 @@ ------------------------------------------------------ -Class with max depth: P1 -Max depth: 2 ------------------------------------------------------ -Average depth: 1.5 ------------------------------------------------------ -Overrides counts: -getX - 0 -getY - 0 -box - 0 - - 0 ------------------------------------------------------ -Average fields amount: 9.333333333333334 ------------------------------------------------------ -Average assignments count: 6.0 ------------------------------------------------------ +[ { + "metricType" : "MAX_DEPTH_COUNT", + "items" : [ { + "description" : "Max depth", + "value" : "P1: 2" + } ] +}, { + "metricType" : "AVERAGE_DEPTH_COUNT", + "items" : [ { + "description" : "Average depth", + "value" : "1.5" + } ] +}, { + "metricType" : "AVERAGE_OVERRIDES_COUNT", + "items" : [ { + "description" : "getX", + "value" : "0" + }, { + "description" : "getY", + "value" : "0" + }, { + "description" : "box", + "value" : "0" + }, { + "description" : "", + "value" : "0" + } ] +}, { + "metricType" : "AVERAGE_FIELDS_COUNT", + "items" : [ { + "description" : "Average fields amount", + "value" : "9.333333333333334" + } ] +}, { + "metricType" : "ABC_COUNT", + "items" : [ { + "description" : "Average assignments count", + "value" : "6.0" + }, { + "description" : "Average branches count", + "value" : "5.375" + }, { + "description" : "Average conditions count", + "value" : "7.0" + } ] +} ] diff --git a/src/main/java/org/example/LabMain.java b/src/main/java/org/example/LabMain.java index 23efc69..bc8d335 100644 --- a/src/main/java/org/example/LabMain.java +++ b/src/main/java/org/example/LabMain.java @@ -1,5 +1,6 @@ package org.example; +import com.fasterxml.jackson.databind.ObjectMapper; import org.example.visitor.AbcVisitor; import org.example.visitor.ClassMapVisitor; import org.example.workers.*; @@ -8,6 +9,8 @@ import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; public class LabMain { @@ -39,33 +42,43 @@ public static void main(String[] args) throws IOException { } assert ps != null; - ps.println("-----------------------------------------------------"); - MaxDepthCounterWorker maxDepthCounterWorker = new MaxDepthCounterWorker(); - maxDepthCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor, ps); + List resultStats = new ArrayList<>(); - ps.println("-----------------------------------------------------"); + MaxDepthCounterWorker maxDepthCounterWorker = new MaxDepthCounterWorker(); + StatItemDto maxDepthCounterWorkerStat = maxDepthCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + resultStats.add(maxDepthCounterWorkerStat); AverageDepthCounterWorker averageDepthCounterWorker = new AverageDepthCounterWorker(); - averageDepthCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor, ps); - - ps.println("-----------------------------------------------------"); + StatItemDto averageDepthCounterWorkerStat = averageDepthCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + resultStats.add(averageDepthCounterWorkerStat); AverageOverridesCounterWorker averageOverridesCounterWorker = new AverageOverridesCounterWorker(); - averageOverridesCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor, ps); - - ps.println("-----------------------------------------------------"); + StatItemDto averageOverridesCounterWorkerStat = averageOverridesCounterWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + resultStats.add(averageOverridesCounterWorkerStat); AverageFieldsCountWorker averageFieldsCountWorker = new AverageFieldsCountWorker(); - averageFieldsCountWorker.doTheJob(PATH_TO_JAR, classMapVisitor, ps); - - ps.println("-----------------------------------------------------"); + StatItemDto averageFieldsCountWorkerStat = averageFieldsCountWorker.doTheJob(PATH_TO_JAR, classMapVisitor); + resultStats.add(averageFieldsCountWorkerStat); AbcWorker abcWorker = new AbcWorker(); - abcWorker.doTheJob(PATH_TO_JAR, abcVisitor, ps); - ps.println("-----------------------------------------------------"); + StatItemDto abcWorkerStat = abcWorker.doTheJob(PATH_TO_JAR, abcVisitor); + resultStats.add(abcWorkerStat); + + printAsJson(resultStats, ps); if (toFile) { ps.close(); } } + + private static void printAsJson(List list, PrintStream out) { + try { + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(list); + out.println(json); + } catch (Exception e) { + e.printStackTrace(out); + } + } } diff --git a/src/main/java/org/example/StatItemDto.java b/src/main/java/org/example/StatItemDto.java new file mode 100644 index 0000000..5495014 --- /dev/null +++ b/src/main/java/org/example/StatItemDto.java @@ -0,0 +1,43 @@ +package org.example; + +import org.example.workers.MetricEnum; + +import java.util.List; + +public class StatItemDto { + + public static class Item { + + private final String description; + private final String value; + + public Item(String description, String value) { + this.description = description; + this.value = value; + } + + public String getDescription() { + return description; + } + + public String getValue() { + return value; + } + } + + private final MetricEnum metricType; + private final List items; + + public StatItemDto(MetricEnum metricType, List items) { + this.metricType = metricType; + this.items = items; + } + + public MetricEnum getMetricType() { + return metricType; + } + + public List getItems() { + return items; + } +} diff --git a/src/main/java/org/example/visitor/AbcVisitor.java b/src/main/java/org/example/visitor/AbcVisitor.java index d136759..76692de 100644 --- a/src/main/java/org/example/visitor/AbcVisitor.java +++ b/src/main/java/org/example/visitor/AbcVisitor.java @@ -1,6 +1,7 @@ package org.example.visitor; import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; @@ -10,7 +11,10 @@ public class AbcVisitor extends ClassVisitor { private String className; + private final Map assignmentCount = new HashMap<>(); + private final Map branchCount = new HashMap<>(); + private final Map conditionCount = new HashMap<>(); public AbcVisitor() { super(Opcodes.ASM9); @@ -40,10 +44,67 @@ public void visitIincInsn(int var, int increment) { assignmentCount.merge(className, 1, Integer::sum); super.visitIincInsn(var, increment); } + + // ------------------------------ + // B — Branches + // ------------------------------ + @Override + public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { + branchCount.merge(className, 1, Integer::sum); + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + } + + @Override + public void visitTypeInsn(int opcode, String type) { + if (opcode == Opcodes.NEW) { + branchCount.merge(className, 1, Integer::sum); + } + super.visitTypeInsn(opcode, type); + } + + + @Override + public void visitJumpInsn(int opcode, Label label) { + if (opcode >= Opcodes.IFEQ && opcode <= Opcodes.IF_ACMPNE) { + conditionCount.merge(className, 1, Integer::sum); + } + + if (opcode == Opcodes.GOTO) { + conditionCount.merge(className, 1, Integer::sum); + } + + super.visitJumpInsn(opcode, label); + } + + @Override + public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + conditionCount.merge(className, 1, Integer::sum); + super.visitLookupSwitchInsn(dflt, keys, labels); + } + + @Override + public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { + conditionCount.merge(className, 1, Integer::sum); + super.visitTableSwitchInsn(min, max, dflt, labels); + } + + @Override + public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { + conditionCount.merge(className, 1, Integer::sum); + super.visitTryCatchBlock(start, end, handler, type); + } }; } public Map getAssignmentCount() { return assignmentCount; } + + public Map getBranchCount() { + return branchCount; + } + + public Map getConditionCount() { + return conditionCount; + } } diff --git a/src/main/java/org/example/workers/AbcWorker.java b/src/main/java/org/example/workers/AbcWorker.java index 306106b..5e88106 100644 --- a/src/main/java/org/example/workers/AbcWorker.java +++ b/src/main/java/org/example/workers/AbcWorker.java @@ -1,30 +1,50 @@ package org.example.workers; +import org.example.StatItemDto; import org.example.visitor.AbcVisitor; import org.objectweb.asm.ClassVisitor; import java.io.IOException; -import java.io.OutputStream; import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; import java.util.Map; public class AbcWorker extends BaseWorker { @Override - public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException { + public StatItemDto doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { loadJar(pathToJar, visitor); - Map assignmentCount = ((AbcVisitor) visitor).getAssignmentCount(); - - printAbc(assignmentCount, ps); + return collectAbc(visitor); } - private void printAbc(Map assignmentCount, PrintStream ps) { + private StatItemDto collectAbc(ClassVisitor visitor) { + Map assignmentCount = ((AbcVisitor) visitor).getAssignmentCount(); double avgAssignments = assignmentCount.values() .stream() .mapToInt(i -> i) .average() .orElse(0.0); - ps.println("Average assignments count: " + avgAssignments); + Map branchCount = ((AbcVisitor) visitor).getBranchCount(); + double avgBranches = branchCount.values() + .stream() + .mapToInt(i -> i) + .average() + .orElse(0.0); + + Map conditionCount = ((AbcVisitor) visitor).getConditionCount(); + double avgConditions = conditionCount.values() + .stream() + .mapToInt(i -> i) + .average() + .orElse(0.0); + + List items = new ArrayList<>(); + items.add(new StatItemDto.Item("Average assignments count", Double.toString(avgAssignments))); + items.add(new StatItemDto.Item("Average branches count", Double.toString(avgBranches))); + items.add(new StatItemDto.Item("Average conditions count", Double.toString(avgConditions))); + + return new StatItemDto(MetricEnum.ABC_COUNT, items); } } diff --git a/src/main/java/org/example/workers/AverageDepthCounterWorker.java b/src/main/java/org/example/workers/AverageDepthCounterWorker.java index 7c011a7..ea9b3f2 100644 --- a/src/main/java/org/example/workers/AverageDepthCounterWorker.java +++ b/src/main/java/org/example/workers/AverageDepthCounterWorker.java @@ -1,23 +1,25 @@ package org.example.workers; +import org.example.StatItemDto; import org.example.visitor.ClassMapVisitor; import org.objectweb.asm.ClassVisitor; import java.io.IOException; -import java.io.OutputStream; import java.io.PrintStream; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; public class AverageDepthCounterWorker extends BaseWorker { @Override - public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException { + public StatItemDto doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { loadJar(pathToJar, visitor); Map classMap = ((ClassMapVisitor) visitor).getSuperMap(); Map depths = computeDepths(classMap); - printAverageDepth(depths, ps); + return collectAverageDepth(depths); } private Map computeDepths(Map classMap) { @@ -40,12 +42,11 @@ private int depthOf(String cls, Map classMap) { } } - private void printAverageDepth(Map depths, PrintStream ps) { + private StatItemDto collectAverageDepth(Map depths) { double average = depths.values().stream() .mapToDouble(i -> i) .average() .orElse(0); - - ps.println("Average depth: " + average); + return new StatItemDto(MetricEnum.AVERAGE_DEPTH_COUNT, List.of(new StatItemDto.Item("Average depth", Double.toString(average)))); } } diff --git a/src/main/java/org/example/workers/AverageFieldsCountWorker.java b/src/main/java/org/example/workers/AverageFieldsCountWorker.java index 7bc5449..115b7f0 100644 --- a/src/main/java/org/example/workers/AverageFieldsCountWorker.java +++ b/src/main/java/org/example/workers/AverageFieldsCountWorker.java @@ -1,30 +1,31 @@ package org.example.workers; +import org.example.StatItemDto; import org.example.visitor.ClassMapVisitor; import org.objectweb.asm.ClassVisitor; import java.io.IOException; -import java.io.OutputStream; import java.io.PrintStream; +import java.util.List; import java.util.Map; public class AverageFieldsCountWorker extends BaseWorker { @Override - public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException { + public StatItemDto doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { loadJar(pathToJar, visitor); Map fieldCount = ((ClassMapVisitor) visitor).getFieldCount(); - printAverageFieldsCount(fieldCount, ps); + return collectAverageFieldsCount(fieldCount); } - private void printAverageFieldsCount(Map fieldCount, PrintStream ps) { + private StatItemDto collectAverageFieldsCount(Map fieldCount) { double avgFields = fieldCount.values() .stream() .mapToInt(i -> i) .average() .orElse(0); - ps.println("Average fields amount: " + avgFields); + return new StatItemDto(MetricEnum.AVERAGE_FIELDS_COUNT, List.of(new StatItemDto.Item("Average fields amount", Double.toString(avgFields)))); } } diff --git a/src/main/java/org/example/workers/AverageOverridesCounterWorker.java b/src/main/java/org/example/workers/AverageOverridesCounterWorker.java index 00b9ca3..ab790c1 100644 --- a/src/main/java/org/example/workers/AverageOverridesCounterWorker.java +++ b/src/main/java/org/example/workers/AverageOverridesCounterWorker.java @@ -1,11 +1,12 @@ package org.example.workers; +import org.example.StatItemDto; import org.example.visitor.ClassMapVisitor; import org.objectweb.asm.ClassVisitor; import java.io.IOException; -import java.io.OutputStream; import java.io.PrintStream; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -15,7 +16,7 @@ public class AverageOverridesCounterWorker extends BaseWorker { private final Map overriddenCount = new HashMap<>(); @Override - public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException { + public StatItemDto doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { loadJar(pathToJar, visitor); Map> methods = ((ClassMapVisitor) visitor).getMethods(); Map superMap = ((ClassMapVisitor) visitor).getSuperMap(); @@ -39,13 +40,15 @@ public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) thr overriddenCount.put(cls, count); } - printOverriddenCounts(ps); + return collectOverriddenCounts(); } - private void printOverriddenCounts(PrintStream ps) { - ps.println("Overrides counts:"); + private StatItemDto collectOverriddenCounts() { + List items = new ArrayList<>(); + for (String cls : overriddenCount.keySet()) { - ps.println(cls + " - " + overriddenCount.get(cls)); + items.add(new StatItemDto.Item(cls, Integer.toString(overriddenCount.get(cls)))); } + return new StatItemDto(MetricEnum.AVERAGE_OVERRIDES_COUNT, items); } } diff --git a/src/main/java/org/example/workers/BaseWorker.java b/src/main/java/org/example/workers/BaseWorker.java index 4100056..3da26a7 100644 --- a/src/main/java/org/example/workers/BaseWorker.java +++ b/src/main/java/org/example/workers/BaseWorker.java @@ -1,5 +1,6 @@ package org.example.workers; +import org.example.StatItemDto; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; @@ -9,7 +10,7 @@ abstract public class BaseWorker { - abstract public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException; + abstract public StatItemDto doTheJob(String pathToJar, ClassVisitor visitor) throws IOException; void loadJar(String jarPath, ClassVisitor visitor) throws IOException { try (InputStream in = new FileInputStream(jarPath); diff --git a/src/main/java/org/example/workers/MaxDepthCounterWorker.java b/src/main/java/org/example/workers/MaxDepthCounterWorker.java index 1af03ef..981eedf 100644 --- a/src/main/java/org/example/workers/MaxDepthCounterWorker.java +++ b/src/main/java/org/example/workers/MaxDepthCounterWorker.java @@ -1,23 +1,25 @@ package org.example.workers; +import org.example.StatItemDto; import org.example.visitor.ClassMapVisitor; import org.objectweb.asm.ClassVisitor; import java.io.IOException; -import java.io.OutputStream; import java.io.PrintStream; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; public class MaxDepthCounterWorker extends BaseWorker { @Override - public void doTheJob(String pathToJar, ClassVisitor visitor, PrintStream ps) throws IOException { + public StatItemDto doTheJob(String pathToJar, ClassVisitor visitor) throws IOException { loadJar(pathToJar, visitor); Map classMap = ((ClassMapVisitor) visitor).getSuperMap(); Map depths = computeDepths(classMap); - printMaxDepth(depths, ps); + return collectMaxDepth(depths); } private Map computeDepths(Map classMap) { @@ -40,15 +42,16 @@ private int depthOf(String cls, Map classMap) { } } - private void printMaxDepth(Map depths, PrintStream ps) { + private StatItemDto collectMaxDepth(Map depths) { Map.Entry maxEntry = depths.entrySet() .stream() .max(Map.Entry.comparingByValue()) .orElse(null); if (maxEntry != null) { - ps.println("Class with max depth: " + maxEntry.getKey()); - ps.println("Max depth: " + maxEntry.getValue()); + return new StatItemDto(MetricEnum.MAX_DEPTH_COUNT, List.of(new StatItemDto.Item("Max depth", maxEntry.getKey() + ": " + maxEntry.getValue()))); } + + return new StatItemDto(MetricEnum.MAX_DEPTH_COUNT, Collections.emptyList()); } } diff --git a/src/main/java/org/example/workers/MetricEnum.java b/src/main/java/org/example/workers/MetricEnum.java new file mode 100644 index 0000000..c87e21d --- /dev/null +++ b/src/main/java/org/example/workers/MetricEnum.java @@ -0,0 +1,10 @@ +package org.example.workers; + +public enum MetricEnum { + + AVERAGE_DEPTH_COUNT, + AVERAGE_FIELDS_COUNT, + AVERAGE_OVERRIDES_COUNT, + MAX_DEPTH_COUNT, + ABC_COUNT +}