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
11 changes: 6 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id("java")
id("io.freefair.lombok") version "9.1.0"
}

group = "org.example"
Expand All @@ -10,10 +11,10 @@ repositories {
}

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

testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")
Expand All @@ -22,4 +23,4 @@ dependencies {

tasks.test {
useJUnitPlatform()
}
}
Empty file modified gradlew
100644 → 100755
Empty file.
8 changes: 8 additions & 0 deletions results/fescar.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"maxInheritanceDepth": 5,
"averageInheritanceDepth": 1.44,
"abc": 7463.05,
"averageOverriddenMethodsNumber": 0.28,
"averageFieldsNumber": 2.61
}

8 changes: 8 additions & 0 deletions results/guava.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"maxInheritanceDepth": 7,
"averageInheritanceDepth": 1.81,
"abc": 189362.57,
"averageOverriddenMethodsNumber": 0.87,
"averageFieldsNumber": 1.75
}

8 changes: 8 additions & 0 deletions results/sample.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"maxInheritanceDepth": 2,
"averageInheritanceDepth": 1.50,
"abc": 29.48,
"averageOverriddenMethodsNumber": 0.00,
"averageFieldsNumber": 0.70
}

29 changes: 0 additions & 29 deletions src/main/java/org/example/Example.java

This file was deleted.

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

import org.example.metrics.ClassMetrics;
import org.example.metrics.TotalMetrics;
import org.example.visitor.ClassMetricsVisitor;
import org.objectweb.asm.ClassReader;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class Main {

public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.out.println("Expected args: <jar path> <destination file path>");
}

var jarPath = args[0];
var destinationFilePath = args[1];

TotalMetrics totalMetrics = new TotalMetrics();

try (JarFile sampleJar = new JarFile(jarPath)) {
Enumeration<JarEntry> enumeration = sampleJar.entries();

while (enumeration.hasMoreElements()) {
JarEntry entry = enumeration.nextElement();
if (entry.getName().endsWith(".class")) {
ClassReader cr = new ClassReader(sampleJar.getInputStream(entry));
ClassMetricsVisitor cmv = new ClassMetricsVisitor();
cr.accept(cmv, 0);
totalMetrics.addClassMetrics(cmv.getClassMetrics());
}
}
}

totalMetrics.print(destinationFilePath);
}
}
29 changes: 0 additions & 29 deletions src/main/java/org/example/example/BubbleSort.java

This file was deleted.

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

import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Getter
public class ClassMetrics {

private final List<MethodMetrics> methodMetricsList = new ArrayList<>();

private long fields;

@Setter
private String name;
@Setter
private String superName;

public void addMethod(MethodMetrics methodMetrics) {
methodMetricsList.add(methodMetrics);
}

public void incFields() {
fields++;
}

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

import lombok.Getter;
import lombok.Setter;

@Getter
public class MethodMetrics {

private long assignments;
private long branches;
private long conditions;

@Setter
private String name;
@Setter
private String descriptor;

public void incAssignments() {
assignments++;
}

public void incBranches() {
branches++;
}

public void incConditions() {
conditions++;
}

public void incConditions(long amount) {
conditions += amount;
}
}
171 changes: 171 additions & 0 deletions src/main/java/org/example/metrics/TotalMetrics.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package org.example.metrics;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.stream.Collectors;

public class TotalMetrics {

private final List<ClassMetrics> classesMetrics = new ArrayList<>();

public void addClassMetrics(ClassMetrics classMetrics) {
classesMetrics.add(classMetrics);
}

public void print(String destinationFile) throws IOException {
var classToSuper = getClassToSuper();
var classToItsMethodSignatures = getClassToItsMethodDescriptors();

int maxInheritanceDepth = getMaxInheritanceDepth(classToSuper);
double avgInheritanceDepth = getAvgInheritanceDepth(classToSuper);
double abc = getAbcMetric();
double avgOverriddenMethods = getAvgOverriddenMethods(classToSuper, classToItsMethodSignatures);
double avgFields = getAvgFields();

String json = """
{
"maxInheritanceDepth": %d,
"averageInheritanceDepth": %.2f,
"abc": %.2f,
"averageOverriddenMethodsNumber": %.2f,
"averageFieldsNumber": %.2f
}
""".formatted(
maxInheritanceDepth,
avgInheritanceDepth,
abc,
avgOverriddenMethods,
avgFields
);

System.out.println(json);

try (PrintWriter writer = new PrintWriter(new FileWriter(destinationFile))) {
writer.println(json);
}
}

private Map<String, String> getClassToSuper() {
Map<String, String> classToSuper = new HashMap<>();
for (ClassMetrics cm : classesMetrics) {
classToSuper.put(cm.getName(), cm.getSuperName());
}
return classToSuper;
}

private Map<String, Set<String>> getClassToItsMethodDescriptors() {
Map<String, Set<String>> classToMethods = new HashMap<>();
for (ClassMetrics cm : classesMetrics) {
String className = cm.getName();
Set<String> methodSignatures = cm.getMethodMetricsList().stream()
.filter(m -> !m.getName().equals("<init>") && !m.getName().equals("<clinit>"))
.map(m -> m.getName() + m.getDescriptor())
.collect(Collectors.toSet());
classToMethods.put(className, methodSignatures);
}
return classToMethods;
}

private int getMaxInheritanceDepth(Map<String, String> classToSuper) {
int maxDepth = 0;
for (String className : classToSuper.keySet()) {
int depth = calculateInheritanceDepth(className, classToSuper);
maxDepth = Math.max(maxDepth, depth);
}
return maxDepth;
}

private double getAvgInheritanceDepth(Map<String, String> classToSuper) {
if (classToSuper.isEmpty()) {
return 0.0;
}
List<Integer> depths = new ArrayList<>();
for (String className : classToSuper.keySet()) {
depths.add(calculateInheritanceDepth(className, classToSuper));
}
return depths.stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0.0);
}

private int calculateInheritanceDepth(String className, Map<String, String> classToSuper) {
int depth = 0;
String current = className;
while (true) {
String parent = classToSuper.get(current);
if (parent == null) {
break;
}
depth++;
current = parent;
}
return depth;
}

private double getAbcMetric() {
long totalAssignments = 0;
long totalBranches = 0;
long totalConditions = 0;

for (ClassMetrics cm : classesMetrics) {
for (MethodMetrics mm : cm.getMethodMetricsList()) {
totalAssignments += mm.getAssignments();
totalBranches += mm.getBranches();
totalConditions += mm.getConditions();
}
}

// ABC = sqrt(A^2 + B^2 + C^2)
return Math.sqrt(
totalAssignments * totalAssignments +
totalBranches * totalBranches +
totalConditions * totalConditions
);
}

private double getAvgOverriddenMethods(Map<String, String> classToSuper, Map<String, Set<String>> classToMethods) {
if (classesMetrics.isEmpty()) {
return 0.0;
}

int totalOverridden = 0;

for (ClassMetrics cm : classesMetrics) {
String className = cm.getName();
Set<String> currentMethods = classToMethods.get(className);
if (currentMethods == null || currentMethods.isEmpty()) {
continue;
}

Set<String> parentMethods = new HashSet<>();
String parent = classToSuper.get(className);

while (parent != null) {
Set<String> parentMethodSet = classToMethods.get(parent);
if (parentMethodSet != null) {
parentMethods.addAll(parentMethodSet);
}
parent = classToSuper.get(parent);
}

Set<String> overridden = new HashSet<>(currentMethods);
overridden.retainAll(parentMethods);
totalOverridden += overridden.size();
}

return (double) totalOverridden / classesMetrics.size();
}

private double getAvgFields() {
if (classesMetrics.isEmpty()) {
return 0.0;
}
long totalFields = classesMetrics.stream()
.mapToLong(ClassMetrics::getFields)
.sum();
return (double) totalFields / classesMetrics.size();
}
}
Loading