Skip to content

Commit

Permalink
Basic full working operations for non-annotation class detection
Browse files Browse the repository at this point in the history
  • Loading branch information
romeara committed Nov 14, 2019
1 parent 27bd557 commit 3575ecc
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 71 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ Provides utilities for finding unused code within a self-complete application co

Information for how to contribute can be found in [the contribution guidelines](./docs/CONTRIBUTING.md)

## Use

Helsing is currently in initial (alpha) development. In current provides a single command, `dead-class`, which analyzes `*.class` files within an application and determines which, if any, are not currently referenced within the available source

To use, get the standalone jar from the latest alpha tag, and run

```
java -jar <jar name> dead-classes --directory <project directory>
```

on a built project (built meaning the project directory contains compiled *.class files). Additionally, the `--trace` argument may be given a full-qualified class name, which will log additional information about the structure discovered for that class (if a use of another class isn't being detected within it), an the discovered uses of that class

## Legal

This project is distributed under the [MIT License](https://opensource.org/licenses/MIT). There are no requirements for using it in your own project (a line in a NOTICES file is appreciated but not necessary for use)
Expand Down
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ buildscript {
}
dependencies {
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4'
classpath 'it.gianluz:gradle-capsule-plugin:1.0.3'
}
}

Expand All @@ -27,7 +28,7 @@ allprojects {
}

subprojects {
apply plugin: 'java-library'
apply plugin: 'java'
apply plugin: 'checkstyle'
apply plugin: 'jacoco'
apply plugin: 'maven-publish'
Expand Down
2 changes: 2 additions & 0 deletions dependencies.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ org.ow2.asm:asm:7.2,compile

org.slf4j:slf4j-api:1.7.26,compile
org.slf4j:slf4j-simple:1.7.26,runtime

org.starchartlabs.alloy:alloy-core:0.5.0
31 changes: 30 additions & 1 deletion helsing-cli/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
apply plugin: 'it.gianluz.capsule'

description = 'Command line interface for running necromancer utilities on a code set'

//Dependency versions managed in ${rootDir}/dependencies.properties
Expand All @@ -7,6 +9,33 @@ dependencies {
compile 'args4j:args4j'
compile 'org.ow2.asm:asm'
compile 'org.slf4j:slf4j-api'
compile 'org.starchartlabs.alloy:alloy-core'

runtime 'org.slf4j:slf4j-simple'
}
}

//Apply module naming to all projects
//Add LICENSE so it is included in all JARs, as well as dependent licenses, fulfilling the "distributions include license" requirement
jar {
from("${rootDir}"){ include 'LICENSE' }
}

//All projects should provide source code and javadoc, and upload these with any released artifacts
sourcesJar {
from("${rootDir}"){ include 'LICENSE' }
}

javadocJar{
from("${rootDir}"){ include 'LICENSE' }
}

task distCapsule(type: FatCapsule) {
applicationClass 'org.starchartlabs.helsing.cli.CommandLineInterface'
embedConfiguration configurations.runtime
}

tasks.assemble.dependsOn distCapsule

artifacts {
archives distCapsule
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@
import java.util.Optional;
import java.util.Set;

import javax.annotation.Nullable;

import org.kohsuke.args4j.Option;
import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.starchartlabs.helsing.cli.impl.AvailableSourceVisitor;
import org.starchartlabs.helsing.cli.impl.BulkClassFileVisitor;
import org.starchartlabs.helsing.cli.impl.ClassUseTracer;
import org.starchartlabs.helsing.cli.impl.ReferencedClassVisitor;

//TODO romeara
Expand All @@ -45,9 +48,7 @@ public class DeadClassesCommand implements Runnable {

@Override
public void run() {
String directoryStyleTrace = Optional.ofNullable(traceClassName)
.map(trace -> trace.replace('.', '/'))
.orElse(null);
Tracer tracer = new Tracer(traceClassName, traceClassName);

try {
// Walk class files and compile a full list of available source
Expand All @@ -56,20 +57,14 @@ public void run() {

Set<String> sourceClassNames = sourceVisitor.getSourceClassNames();

// TODO romeara switch to not-info
sourceClassNames.stream()
.forEach(name -> logger.debug("Found source class {}", name));

if (directoryStyleTrace != null) {
// TODO Make [CLASS TRACE] a constant?
sourceClassNames.stream()
.filter(name -> Objects.equals(directoryStyleTrace, name))
.forEach(name -> logger.info("[CLASS TRACE] Found source class {}", name));
}
sourceClassNames.stream()
.forEach(name -> tracer.traceClassFeature(name, "(source file found)"));

// Find references to known source files
ReferencedClassVisitor referenceVisitor = new ReferencedClassVisitor(ASM_API, sourceClassNames,
directoryStyleTrace);
ReferencedClassVisitor referenceVisitor = new ReferencedClassVisitor(ASM_API, sourceClassNames, tracer);
Files.walkFileTree(directory.toPath(), new BulkClassFileVisitor(referenceVisitor));

sourceClassNames.removeAll(referenceVisitor.getReferencedClasses());
Expand All @@ -80,4 +75,44 @@ public void run() {
}
}

private static final class Tracer implements ClassUseTracer {

/** Logger reference to output information to the application log files */
private final Logger logger = LoggerFactory.getLogger(getClass());

// Class to trace when found, and what it contains
@Nullable
private final String structureTraceClass;

// Class to trace discovered uses of
@Nullable
private final String useTraceClass;

public Tracer(String structureTraceClass, String useTraceClass) {
this.structureTraceClass = Optional.ofNullable(structureTraceClass)
.map(trace -> trace.replace('.', '/'))
.orElse(null);
;
this.useTraceClass = Optional.ofNullable(useTraceClass)
.map(trace -> trace.replace('.', '/'))
.orElse(null);
;
}

@Override
public void traceClassFeature(String className, String feature) {
if (Objects.equals(structureTraceClass, className)) {
logger.info("[SOURCE] {}: {}", className, feature);
}
}

@Override
public void traceClassUse(String className, String usedIn) {
if (Objects.equals(useTraceClass, className)) {
logger.info("[USE] {}:{}", className, usedIn);
}
}

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) Nov 14, 2019 StarChart Labs Authors.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* romeara - initial API and implementation and/or initial documentation
*/
package org.starchartlabs.helsing.cli.impl;

//TODO romeara
public interface ClassUseTracer {

// Thing found in class in question
void traceClassFeature(String className, String feature);

void traceClassUse(String className, String usedIn);

}
Loading

0 comments on commit 3575ecc

Please sign in to comment.