Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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 файла
Expand Down
21 changes: 11 additions & 10 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
plugins {
id("java")
id("java")
}

group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
mavenCentral()
mavenCentral()
}

dependencies {
implementation("org.ow2.asm:asm:9.5")
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.16.1")
implementation("org.ow2.asm:asm:9.5")
implementation("org.ow2.asm:asm-tree:9.5")
implementation("org.ow2.asm:asm-analysis:9.5")
implementation("org.ow2.asm:asm-util:9.5")

testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

tasks.test {
useJUnitPlatform()
useJUnitPlatform()
}
29 changes: 0 additions & 29 deletions src/main/java/org/example/Example.java

This file was deleted.

79 changes: 79 additions & 0 deletions src/main/java/org/example/JarAnalyzer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.example;

import org.example.visitor.*;
import org.objectweb.asm.ClassReader;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class JarAnalyzer {

public static void main(String[] args) throws IOException {
PrettyPrinter printer = new PrettyPrinter();
MetricsCounter metricsCounter = new MetricsCounter();
Map<String, ArrayList<ClassInfo>> graph = new HashMap<>();
Map<String, ClassInfo> classesByNames = new HashMap<>();
var abcMetrics = new MethodMetrics(0, 0, 0);
var nClasses = 0;
var classFields = 0L;

try (JarFile sampleJar = new JarFile("src/main/resources/sample.jar")) {
Enumeration<JarEntry> enumeration = sampleJar.entries();

while (enumeration.hasMoreElements()) {
JarEntry entry = enumeration.nextElement();
if (entry.getName().endsWith(".class")) {
var classInfo = getClassInfo(sampleJar, entry, classesByNames, graph);
abcMetrics = abcMetrics.merge(classInfo.methodMetrics());
if (!classInfo.isInterface()) {
nClasses++;
classFields += classInfo.fields().size();
}
}
}

var outputMetrics = new OutputMetrics(
metricsCounter.maxDepth(graph),
metricsCounter.averageDepth(graph),
(double) classFields / nClasses,
metricsCounter.overridenMethodsCount(graph, classesByNames),
abcMetrics,
abcMetrics.abc()
);

var reportFile = new File("metrics/metrics.json");
printer.print(outputMetrics, System.out, new FileOutputStream(reportFile));
}
}

private static ClassInfo getClassInfo(
JarFile sampleJar,
JarEntry entry,
Map<String, ClassInfo> classesByNames,
Map<String, ArrayList<ClassInfo>> graph
) throws IOException {
ClassAnalyzer cp = new ClassAnalyzer();
ClassReader cr = new ClassReader(sampleJar.getInputStream(entry));
cr.accept(cp, 0);
var classInfo = cp.toClassInfo();
classesByNames.put(classInfo.name(), classInfo);
addInheritanceEdges(graph, classInfo);
return classInfo;
}

private static void addInheritanceEdges(Map<String, ArrayList<ClassInfo>> graph, ClassInfo classInfo) {
graph.computeIfAbsent(
classInfo.superName(),
k -> new ArrayList<>()).add(classInfo);
classInfo.interfaces().forEach(i -> {
graph.computeIfAbsent(i, k -> new ArrayList<>()).add(classInfo);
});
}
}
69 changes: 69 additions & 0 deletions src/main/java/org/example/visitor/ClassAnalyzer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package org.example.visitor;

import org.objectweb.asm.*;

import java.util.*;

import static org.objectweb.asm.Opcodes.*;

public class ClassAnalyzer extends ClassVisitor {
private String name;
private String superName;
private Boolean isInterface;
private HashSet<String> interfaces;
private final List<MethodSignature> methods = new ArrayList<>();
private final List<String> fields = new ArrayList<>();
private final MethodAnalyzer methodAnalyzer = new MethodAnalyzer(ASM8);

public ClassAnalyzer() {
super(ASM8);
}

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.name = name;
this.superName = superName;
this.interfaces = new HashSet<>(List.of(interfaces));
this.isInterface = (access & Opcodes.ACC_INTERFACE) != 0;

var interfacesAsString = String.join(", ", interfaces);
if (!interfacesAsString.isEmpty()) {
interfacesAsString = "implements " + interfacesAsString;
}
System.out.printf("\n %s extends %s %s {%n", name, superName, interfacesAsString);
}

@Override
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
System.out.println(" " + desc + " " + name);
fields.add(name);
return null;
}

@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
System.out.println(" " + name + desc);
if (!Objects.equals(name, "<init>")) {
methods.add(new MethodSignature(name, desc));
}
return methodAnalyzer;
}

@Override
public void visitEnd() {
System.out.println("}");
}

public ClassInfo toClassInfo() {
return new ClassInfo(
name,
isInterface,
superName,
interfaces,
methods,
fields,
methodAnalyzer.methodMetrics()
);
}
}

14 changes: 14 additions & 0 deletions src/main/java/org/example/visitor/ClassInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.example.visitor;

import java.util.HashSet;
import java.util.List;

public record ClassInfo (
String name,
Boolean isInterface,
String superName,
HashSet<String> interfaces,
List<MethodSignature> methods,
List<String> fields,
MethodMetrics methodMetrics
) {}
46 changes: 0 additions & 46 deletions src/main/java/org/example/visitor/ClassPrinter.java

This file was deleted.

4 changes: 4 additions & 0 deletions src/main/java/org/example/visitor/Leaf.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.example.visitor;

public record Leaf(String className, int depth) {
}
78 changes: 78 additions & 0 deletions src/main/java/org/example/visitor/MethodAnalyzer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.example.visitor;

import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import java.util.List;

import static org.objectweb.asm.Opcodes.*;

public class MethodAnalyzer extends MethodVisitor {
int assignments;
int conditionals;
int invocations;

public MethodAnalyzer(int api) {
super(api);
}

@Override
public void visitVarInsn(int opcode, int var) {
if (List.of(ISTORE, LSTORE, FSTORE, DSTORE, ASTORE).contains(opcode)) {
assignments++;
}
super.visitVarInsn(opcode, var);
}

@Override
public void visitIntInsn(int opcode, int operand) {
if (opcode == NEWARRAY) {
invocations++;
}
super.visitIntInsn(opcode, operand);
}

@Override
public void visitTypeInsn(int opcode, String type) {
if (List.of(NEW, NEWARRAY).contains(opcode)) {
invocations++;
}
super.visitTypeInsn(opcode, type);
}

@Override
public void visitJumpInsn(int opcode, Label label) {
conditionals++;
super.visitJumpInsn(opcode, label);
}


@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
invocations++;
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}

@Override
public void visitTryCatchBlock(org.objectweb.asm.Label start, org.objectweb.asm.Label end, org.objectweb.asm.Label
handler, String type) {
conditionals++;
super.visitTryCatchBlock(start, end, handler, type);
}

@Override
public void visitLookupSwitchInsn(org.objectweb.asm.Label dflt, int[] keys, Label[] labels) {
conditionals += keys.length;
super.visitLookupSwitchInsn(dflt, keys, labels);
}

@Override
public void visitTableSwitchInsn(int min, int max, Label dflt, org.objectweb.asm.Label... labels) {
conditionals += labels.length;
super.visitTableSwitchInsn(min, max, dflt, labels);
}

public MethodMetrics methodMetrics() {
return new MethodMetrics(assignments, invocations, conditionals);
}
}
20 changes: 20 additions & 0 deletions src/main/java/org/example/visitor/MethodMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.example.visitor;

public record MethodMetrics(
int assignments,
int branches,
int conditionals
) {

public MethodMetrics merge(MethodMetrics other) {
return new MethodMetrics(
assignments + other.assignments,
branches + other.branches,
conditionals + other.conditionals
);
}

public double abc() {
return Math.sqrt(assignments * assignments + branches * branches + conditionals * conditionals);
}
}
3 changes: 3 additions & 0 deletions src/main/java/org/example/visitor/MethodSignature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.example.visitor;

public record MethodSignature(String name, String desc) {}
Loading