Skip to content
Open
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
3 changes: 3 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ dependencies {
implementation("org.ow2.asm:asm-analysis:9.5")
implementation("org.ow2.asm:asm-util:9.5")

implementation("com.fasterxml.jackson.core:jackson-core:2.20.1")
implementation("com.fasterxml.jackson.core:jackson-databind:2.20.1")

testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
Expand Down
40 changes: 40 additions & 0 deletions src/main/java/org/example/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.example;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.example.analyzer.Analyzer;
import org.example.analyzer.JarMetrics;

public class App {
private static final String defaultJar = "src/main/resources/sample.jar";
private static final String defaultOutput = "./output.json";

public static void main(String[] args) throws IOException {
String jar = defaultJar;
String outFile = defaultOutput;

if (args.length > 0) {
jar = args[0];
}

if (args.length > 1) {
outFile = args[1];
}

JarMetrics metrics = new Analyzer().analyze(jar);
ObjectMapper mapper = new ObjectMapper();
byte[] stringifiedMetrics = mapper.writeValueAsBytes(metrics);
System.out.write(stringifiedMetrics);
System.out.println();

if (outFile != null) {
try (OutputStream outputStream = new FileOutputStream(outFile)) {
outputStream.write(stringifiedMetrics);
}
}
}
}
29 changes: 0 additions & 29 deletions src/main/java/org/example/Example.java

This file was deleted.

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

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarFile;

import org.example.analyzer.visitor.ABCVisitor;
import org.example.analyzer.visitor.InheritanceVisitor;
import org.example.analyzer.visitor.OverrideVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;

public class Analyzer {
private Map<String, ClassMetrics> classMetircsMap = new HashMap<>();

private InheritanceVisitor inheritanceVisitor = new InheritanceVisitor();
private OverrideVisitor overrideVisitor = new OverrideVisitor();

public JarMetrics analyze(String jarPath) {
try (JarFile jar = new JarFile(jarPath)) {
jar.stream()
.filter(entry -> entry.getName().endsWith(".class"))
.forEach(entry -> {
try (InputStream is = jar.getInputStream(entry)) {
ClassReader reader = new ClassReader(is);
processClass(reader);
} catch (IOException e) {
e.printStackTrace();
}
});
} catch (IOException e) {
e.printStackTrace();
}

return calcMetrics();
}

private void processClass(ClassReader reader) {
reader.accept(inheritanceVisitor, 0);
reader.accept(overrideVisitor, 0);

ClassNode node = new ClassNode();
reader.accept(node, 0);

ABCVisitor abcVisitor = new ABCVisitor();
reader.accept(abcVisitor, 0);

ClassMetrics classMetrics = new ClassMetrics();

classMetrics.assignments = abcVisitor.getAssignments();
classMetrics.branches = abcVisitor.getBranches();
classMetrics.conditionals = abcVisitor.getConditionals();
classMetrics.fields = node.fields.size();

classMetircsMap.put(node.name, classMetrics);
}

private JarMetrics calcMetrics() {
Long totalAssigments = Long.valueOf(0);
Long totalBranches = Long.valueOf(0);
Long totalConditions = Long.valueOf(0);

Integer totalFields = 0;

for (ClassMetrics classMetrics : classMetircsMap.values()) {
totalFields += classMetrics.fields;
totalAssigments += classMetrics.assignments;
totalBranches += classMetrics.branches;
totalConditions += classMetrics.conditionals;
}

Double ABC = Math.sqrt(
totalAssigments * totalAssigments + totalBranches * totalBranches + totalConditions + totalConditions);

return new JarMetrics(
inheritanceVisitor.getMaxInheritanceDepth(),
inheritanceVisitor.getAvgInheritanceDepth(),
ABC,
overrideVisitor.getAvgMethodOverrides(),
Double.valueOf(totalFields) / classMetircsMap.size());
}
}
9 changes: 9 additions & 0 deletions src/main/java/org/example/analyzer/ClassMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.example.analyzer;

class ClassMetrics {
Long assignments;
Long branches;
Long conditionals;
Integer overrides;
Integer fields;
}
9 changes: 9 additions & 0 deletions src/main/java/org/example/analyzer/JarMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.example.analyzer;

public record JarMetrics(
Integer maxInheritanceDepth,
Double avgInheritanceDepth,
Double ABC,
Double avgOverrides,
Double avgClassFields) {
}
115 changes: 115 additions & 0 deletions src/main/java/org/example/analyzer/visitor/ABCVisitor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package org.example.analyzer.visitor;

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

import java.util.Set;

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

public class ABCVisitor extends ClassVisitor {
public ABCVisitor() {
super(ASM8);
}

private Long assignments = Long.valueOf(0);
private Long branches = Long.valueOf(0);
private Long conditionals = Long.valueOf(0);

public Long getAssignments() {
return assignments;
}

public Long getBranches() {
return branches;
}

public Long getConditionals() {
return conditionals;
}

@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
return new ABCMethodVisitor();
}

class ABCMethodVisitor extends MethodVisitor {
private static final Set<Integer> assignmentOpCodes = Set.of(
IASTORE, LASTORE, FASTORE, DASTORE,
AASTORE, BASTORE, CASTORE, SASTORE,
PUTSTATIC, PUTFIELD);

public ABCMethodVisitor() {
super(ASM8);
}

@Override
public void visitIincInsn(final int varIndex, final int increment) {
assignments++;
}

@Override
public void visitVarInsn(final int opcode, final int varIndex) {
if (assignmentOpCodes.contains(opcode)) {
assignments++;
}
}

@Override
public void visitTypeInsn(final int opcode, final String type) {
if (opcode == NEW) {
branches++;
}
}

@Override
public void visitFieldInsn(final int opcode, final String owner, final String name, final String descriptor) {
if (assignmentOpCodes.contains(opcode)) {
assignments++;
}
}

@Override
public void visitJumpInsn(final int opcode, final Label label) {
if (opcode != GOTO) {
conditionals++;
}
}

@Override
public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) {
conditionals++;
}

@Override
public void visitInvokeDynamicInsn(
final String name,
final String descriptor,
final Handle bootstrapMethodHandle,
final Object... bootstrapMethodArguments) {
branches++;
}

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

@Override
public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) {
conditionals += labels.length + 1;
}

@Override
public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) {
conditionals += labels.length + 1;
}
}
}
85 changes: 85 additions & 0 deletions src/main/java/org/example/analyzer/visitor/InheritanceVisitor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package org.example.analyzer.visitor;

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

import java.util.HashMap;
import java.util.Map;

import org.objectweb.asm.ClassVisitor;

public class InheritanceVisitor extends ClassVisitor {
private static class ClassInfo {
String name;
String superName;

Integer inharitanceDepth;

ClassInfo(String name, String superName) {
this.name = name;
this.superName = superName;

inharitanceDepth = -1;
}
}

private final Map<String, ClassInfo> classesInfoMap = new HashMap<>();

public InheritanceVisitor() {
super(ASM8);
}

private Integer calcInharitanceDepth(String name) {
String currentClass = name;
Integer depth = 0;

while (true) {
if (currentClass == null) {
break;
}

ClassInfo classInfo = classesInfoMap.get(currentClass);
if (classInfo == null) {
break;
}

if (classInfo.inharitanceDepth > -1) {
depth += classInfo.inharitanceDepth;
break;
}

depth++;
currentClass = classInfo.superName;
}

return depth;
}

public Integer getMaxInheritanceDepth() {
Integer maxDepth = -1;
for (ClassInfo classInfo : classesInfoMap.values()) {
if (classInfo.inharitanceDepth == -1) {
classInfo.inharitanceDepth = calcInharitanceDepth(classInfo.name);
}

maxDepth = Math.max(maxDepth, classInfo.inharitanceDepth);
}
return maxDepth;
}

public Double getAvgInheritanceDepth() {
Integer depthSum = 0;
for (ClassInfo classInfo : classesInfoMap.values()) {
if (classInfo.inharitanceDepth == -1) {
classInfo.inharitanceDepth = calcInharitanceDepth(classInfo.name);
}

depthSum += classInfo.inharitanceDepth;
}
return Double.valueOf(depthSum) / classesInfoMap.size();
}

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
classesInfoMap.putIfAbsent(name, new ClassInfo(name, superName));
}
}
Loading