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
18 changes: 18 additions & 0 deletions analyzer/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
plugins {
id("java-library")
}

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")

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()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.quicklybly.itmo.bytecodeanalyzer;

import com.quicklybly.itmo.bytecodeanalyzer.api.Statistic;
import com.quicklybly.itmo.bytecodeanalyzer.util.ClassRepository;
import com.quicklybly.itmo.bytecodeanalyzer.util.provider.ClassProvider;
import com.quicklybly.itmo.bytecodeanalyzer.util.provider.impl.CompositeClassProvider;
import com.quicklybly.itmo.bytecodeanalyzer.util.provider.impl.JarClassProvider;
import com.quicklybly.itmo.bytecodeanalyzer.util.provider.impl.SystemClassProvider;
import com.quicklybly.itmo.bytecodeanalyzer.visitor.ClassStatisticVisitor;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.List;
import java.util.jar.JarFile;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;

public class Analyzer {

public static void main(String[] args) throws IOException {
var path = Path.of("analyzer/src/main/resources/sample.jar");
var analyzer = new Analyzer();

var statistic = analyzer.analyzeJar(path);
System.out.println(statistic);
}

public Statistic analyzeJar(Path path) throws IOException {
Statistic statistic = new Statistic();
try (JarFile jar = new JarFile(path.toFile())) {

ClassProvider jarProvider = new JarClassProvider(jar);
ClassProvider systemProvider = new SystemClassProvider();
ClassProvider compositeProvider = new CompositeClassProvider(List.of(jarProvider, systemProvider));

ClassRepository repository = new ClassRepository(compositeProvider);


jar.stream()
.filter(entry -> entry.getName().endsWith(".class"))
.forEach(entry -> {
String internalName = entry.getName().replace(".class", "");

try (InputStream is = jar.getInputStream(entry)) {
ClassReader cr = new ClassReader(is);

ClassVisitor visitor = new ClassStatisticVisitor(statistic, repository);

cr.accept(visitor, 0);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
return statistic;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.quicklybly.itmo.bytecodeanalyzer.api;

import com.quicklybly.itmo.bytecodeanalyzer.model.AbcMetric;
import com.quicklybly.itmo.bytecodeanalyzer.model.AverageHolder;

public class Statistic {

private final AverageHolder fieldAverageHolder = new AverageHolder();
private final AverageHolder averageInheritanceDepthHolder = new AverageHolder();
private final AverageHolder averageOverriddenCountHolder = new AverageHolder();
private int maxInheritanceDepth = 0;
private final AbcMetric abcMetric = new AbcMetric(0, 0, 0);

public void updateFieldStatistic(Integer fieldCount) {
fieldAverageHolder.updateAverage(fieldCount);
}

public void writeDepth(int depth) {
averageInheritanceDepthHolder.updateAverage(depth);
maxInheritanceDepth = Math.max(maxInheritanceDepth, depth);
}

public void writeOverriddenCount(int overriddenCount) {
averageOverriddenCountHolder.updateAverage(overriddenCount);
}

@Override
public String toString() {
return "Statistic{" +
"fieldAverageHolder=" + fieldAverageHolder +
", averageInheritanceDepthHolder=" + averageInheritanceDepthHolder +
", averageOverriddenCountHolder=" + averageOverriddenCountHolder +
", maxInheritanceDepth=" + maxInheritanceDepth +
", abcMetric=" + abcMetric +
'}';
}

public AbcMetric getAbcMetric() {
return abcMetric;
}

public double getAverageFieldCount() {
return fieldAverageHolder.getAverage();
}

public double getAverageInheritanceDepth() {
return averageInheritanceDepthHolder.getAverage();
}

public double getAverageOverriddenCount() {
return averageOverriddenCountHolder.getAverage();
}

public int getMaxInheritanceDepth() {
return maxInheritanceDepth;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.quicklybly.itmo.bytecodeanalyzer.api;

public class StatisticMapper {

private StatisticMapper() {
throw new IllegalStateException("Utility class");
}

public static String toJson(Statistic statistic) {
return String.format(
"{"
+ "\"fieldAverage\":%s,"
+ "\"averageInheritanceDepth\":%s,"
+ "\"averageOverriddenCountHolder\":%s,"
+ "\"maxInheritanceDepth\":%d,"
+ "\"abc\":%s"
+ "}",
statistic.getAverageFieldCount(),
statistic.getAverageInheritanceDepth(),
statistic.getAverageOverriddenCount(),
statistic.getMaxInheritanceDepth(),
statistic.getAbcMetric().getAbc()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.quicklybly.itmo.bytecodeanalyzer.model;

/*
ABC rules for Java
The following rules give the count of Assignments, Branches, Conditionals in the ABC metric for Java:

1. Add one to the assignment count when:
Occurrence of an assignment operator (exclude constant declarations and default parameter assignments) (**=, *=, /=, %=, +=, -=, <<=, >>=, &=, !=, ^=, >>>=**).
Occurrence of an increment or a decrement operator (prefix or postfix) (++, --).
2. Add one to branch count when
Occurrence of a function call or a class method call.
Occurrence of a ‘new’ operator.
3. Add one to condition count when:
Occurrence of a conditional operator (<, >, <=, >=, ==, !=).
Occurrence of the following keywords (‘else’, ‘case’, ‘default’, ‘?’, ‘try’, ‘catch’).
Occurrence of a unary conditional operator.
*/
public class AbcMetric {

private long assignment;
private long branch;
private long condition;

public AbcMetric(long assignment, long branch, long condition) {
this.assignment = assignment;
this.branch = branch;
this.condition = condition;
}

public AbcMetric(AbcMetric other) {
this.assignment = other.assignment;
this.branch = other.branch;
this.condition = other.condition;
}

public void add(AbcMetric other) {
this.assignment += other.assignment;
this.branch += other.branch;
this.condition += other.condition;
}

public double getAbc() {
return Math.sqrt(assignment * assignment + branch * branch + condition * condition);
}

@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;

AbcMetric abcMetric = (AbcMetric) o;
return assignment == abcMetric.assignment && branch == abcMetric.branch && condition == abcMetric.condition;
}

@Override
public int hashCode() {
int result = Long.hashCode(assignment);
result = 31 * result + Long.hashCode(branch);
result = 31 * result + Long.hashCode(condition);
return result;
}

@Override
public String toString() {
return "AbcMetric{" +
"abc=" + getAbc() +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.quicklybly.itmo.bytecodeanalyzer.model;

public class AverageHolder {
private final Object lock = new Object();
private Integer sum = 0;
private Integer count = 0;

public Double getAverage() {
return count.equals(0) ? 0.0 : sum.doubleValue() / count;
}

public void updateAverage(Integer value) {
synchronized (lock) {
sum += value;
count++;
}
}

@Override
public String toString() {
return "AverageHolder{" +
"average=" + getAverage() +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.quicklybly.itmo.bytecodeanalyzer.model.hierarchy;

import java.util.Collections;
import java.util.Set;

public class ClassHierarchyNode {

private final String name;
private final String superName;
private final Set<String> methods; // name + descriptor

private Integer cachedDepth = null;

public ClassHierarchyNode(String name, String superName, Set<String> methods) {
if (name == null || superName == null) {
throw new IllegalArgumentException("Name and superName must not be null");
}
this.name = name;
this.superName = superName;
this.methods = methods != null ? methods : Collections.emptySet();
}

public String getName() {
return name;
}

public String getSuperName() {
return superName;
}

public boolean hasMethod(String signature) {
return methods.contains(signature);
}

public void setCachedDepth(int depth) {
this.cachedDepth = depth;
}

public Integer getCachedDepth() {
return cachedDepth;
}

@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;

ClassHierarchyNode that = (ClassHierarchyNode) o;
return name.equals(that.name);
}

@Override
public int hashCode() {
return name.hashCode();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.example.util;
package com.quicklybly.itmo.bytecodeanalyzer.util;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.ClassNode;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.example.util;
package com.quicklybly.itmo.bytecodeanalyzer.util;

// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
Expand Down
Loading