From b627fe80504b57d9e0d463472714e86ac82591a5 Mon Sep 17 00:00:00 2001 From: "github-classroom[bot]" <66690702+github-classroom[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:51:13 +0000 Subject: [PATCH 1/2] 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 277734461fe068922c205eff1c67e31ab3ab2f04 Mon Sep 17 00:00:00 2001 From: Vadim Date: Thu, 20 Nov 2025 11:29:45 +0300 Subject: [PATCH 2/2] =?UTF-8?q?=D0=9B=D0=B0=D0=B1=D0=BE=D1=80=D0=B0=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=BD=D0=B0=D1=8F=20=E2=84=963?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/example/Abc.java | 51 ++++++++ src/main/java/org/example/Example.java | 161 +++++++++++++++++++++++-- 2 files changed, 199 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/example/Abc.java diff --git a/src/main/java/org/example/Abc.java b/src/main/java/org/example/Abc.java new file mode 100644 index 0000000..4d5967e --- /dev/null +++ b/src/main/java/org/example/Abc.java @@ -0,0 +1,51 @@ +package org.example; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; + +import static org.objectweb.asm.Opcodes.*; + +public class Abc { + public int a; + public int b; + public int c; + + public Abc(int a, int b, int c) { + this.a = a; + this.b = b; + this.c = c; + } + + public Abc add(Abc o) { + return new Abc(a + o.a, b + o.b, c + o.c); + } + + static Abc countAbc(MethodNode m) { + int a = 0, b = 0, cCnt = 0; + for (AbstractInsnNode insn = m.instructions.getFirst(); insn != null; insn = insn.getNext()) { + int op = insn.getOpcode(); + if (op < 0) continue; + + if (op == Opcodes.ISTORE || op == LSTORE || op == Opcodes.FSTORE + || op == Opcodes.DSTORE || op == Opcodes.ASTORE + || op == Opcodes.IINC) { + a++; + } + + if (insn instanceof JumpInsnNode || insn instanceof TableSwitchInsnNode + || insn instanceof LookupSwitchInsnNode || op == Opcodes.ATHROW) { + b++; + } + + if (isIfOpcode(op) || insn instanceof TableSwitchInsnNode || insn instanceof LookupSwitchInsnNode) { + cCnt++; + } + } + return new Abc(a, b, cCnt); + } + + static boolean isIfOpcode(int op) { + return (op >= Opcodes.IFEQ && op <= Opcodes.IF_ACMPNE) + || op == Opcodes.IFNULL || op == Opcodes.IFNONNULL; + } +} diff --git a/src/main/java/org/example/Example.java b/src/main/java/org/example/Example.java index 52d0abe..a248248 100644 --- a/src/main/java/org/example/Example.java +++ b/src/main/java/org/example/Example.java @@ -1,29 +1,164 @@ package org.example; -import org.example.visitor.ClassPrinter; import org.objectweb.asm.ClassReader; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.MethodNode; import java.io.IOException; -import java.util.Enumeration; +import java.io.InputStream; +import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import static org.example.Abc.countAbc; + 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(); - - while (enumeration.hasMoreElements()) { - JarEntry entry = enumeration.nextElement(); - if (entry.getName().endsWith(".class")) { - ClassPrinter cp = new ClassPrinter(); - ClassReader cr = new ClassReader(sampleJar.getInputStream(entry)); - cr.accept(cp, 0); + Map classes = new HashMap<>(); + + try (JarFile jf = new JarFile("src/main/resources/sample.jar")) { + Enumeration e = jf.entries(); + while (e.hasMoreElements()) { + JarEntry je = e.nextElement(); + if (je.getName().endsWith(".class")) { + try (InputStream in = jf.getInputStream(je)) { + ClassReader cr = new ClassReader(in); + ClassNode cn = new ClassNode(); + cr.accept(cn, 0); + classes.put(cn.name, cn); + } + } + } + } + + int classCount = 0; + int maxDepth = 0; + long sumDepth = 0; + long sumFields = 0; + long sumOverridden = 0; + + Abc totalAbc = new Abc(0,0,0); + + for (ClassNode cn : classes.values()) { + boolean isInterface = (cn.access & Opcodes.ACC_INTERFACE) != 0; + if (isInterface) continue; + + classCount++; + + int d = depthOf(cn.name, classes); + maxDepth = Math.max(maxDepth, d); + sumDepth += d; + + long fieldsHere = cn.fields.stream() + .filter(fn -> (fn.access & Opcodes.ACC_SYNTHETIC) == 0) + .count(); + sumFields += fieldsHere; + + long overriddenHere = cn.methods.stream() + .filter(m -> overridesSomewhere(cn, m, classes)) + .count(); + sumOverridden += overriddenHere; + + for (MethodNode o : cn.methods) { + totalAbc = totalAbc.add(countAbc(o)); + } + } + + double avgDepth = classCount == 0 ? 0.0 : (double) sumDepth / classCount; + double avgFields = classCount == 0 ? 0.0 : (double) sumFields / classCount; + double avgOverrides = classCount == 0 ? 0.0 : (double) sumOverridden / classCount; + + System.out.printf("Classes: %d%n", classCount); + System.out.printf("Inheritance: max=%d, avg=%.2f%n", maxDepth, avgDepth); + System.out.printf("ABC: A=%d, B=%d, C=%d%n", totalAbc.a, totalAbc.b, totalAbc.c); + System.out.printf("Avg overrides per class: %.2f%n", avgOverrides); + System.out.printf("Avg fields per class: %.2f%n", avgFields); + + String json = String.format( + java.util.Locale.US, + """ + { + "classesAnalyzed": %d, + "inheritance": { "maxDepth": %d, "avgDepth": %.4f }, + "abc": { "assignments": %d, "branches": %d, "conditions": %d }, + "overrides": { "avgOverriddenMethodsPerClass": %.4f }, + "fields": { "avgFieldsPerClass": %.4f } } + """, + classCount, maxDepth, avgDepth, + totalAbc.a, totalAbc.b, totalAbc.c, + avgOverrides, avgFields + ); + + java.nio.file.Files.writeString( + java.nio.file.Path.of("src/main/resources/result.json"), + json, + java.nio.charset.StandardCharsets.UTF_8 + ); + + } + + static int depthOf(String internalName, Map classes) { + int depth = 0; + String cur = internalName; + while (true) { + ClassNode cn = classes.get(cur); + String superName = (cn != null) ? cn.superName : "java/lang/Object"; + if (superName == null || "java/lang/Object".equals(superName)) { + return depth; } + depth++; + + ClassNode sup = classes.get(superName); + if (sup == null) { + return depth + 1; + } + cur = superName; + } + } + + static boolean overridesSomewhere(ClassNode cn, MethodNode m, Map classes) { + if ((m.access & (Opcodes.ACC_STATIC | Opcodes.ACC_PRIVATE)) != 0) return false; + if (m.name.equals("") || m.name.equals("")) return false; + if ((m.access & (Opcodes.ACC_BRIDGE | Opcodes.ACC_SYNTHETIC)) != 0) return false; + + if (declares(cn.superName, m.name, m.desc, classes)) return true; + + for (String itf : cn.interfaces) { + if (declares(itf, m.name, m.desc, classes)) return true; + } + return false; + } + + static boolean declares(String owner, String name, String desc, Map classes) { + if (owner == null) return false; + if ("java/lang/Object".equals(owner)) { + return declaresInObject(name, desc); + } + + ClassNode n = classes.get(owner); + if (n == null) { + return false; + } + + for (MethodNode o : n.methods) { + MethodNode mm = o; + if (mm.name.equals(name) && mm.desc.equals(desc)) return true; } + + if (declares(n.superName, name, desc, classes)) return true; + for (String itf : n.interfaces) if (declares(itf, name, desc, classes)) return true; + return false; } + + static boolean declaresInObject(String name, String desc) { + return ("equals".equals(name) && "(Ljava/lang/Object;)Z".equals(desc)) + || ("hashCode".equals(name) && "()I".equals(desc)) + || ("toString".equals(name) && "()Ljava/lang/String;".equals(desc)) + || ("clone".equals(name) && "()Ljava/lang/Object;".equals(desc)) + || ("finalize".equals(name) && "()V".equals(desc)); + } + }