Skip to content
Merged
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
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ dependencies {

implementation 'org.apache.logging.log4j:log4j-api:2.18.0'
implementation 'org.apache.logging.log4j:log4j-core:2.18.0'
def walaVersion = '1.6.6'
def walaVersion = '1.6.7'

compileOnly 'org.projectlombok:lombok:1.18.30'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=1.0.1
version=1.1.0
13 changes: 2 additions & 11 deletions src/main/java/com/ibm/cldk/CodeAnalyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -174,17 +174,8 @@ private static void analyze() throws Exception {
build = build == null ? "auto" : build;
// Is noBuild is true, we will not build the project
build = noBuild ? null : build;
String sdgAsJSONString = SystemDependencyGraph.construct(input, dependencies, build);
JsonElement sdgAsJSONElement = gson.fromJson(sdgAsJSONString, JsonElement.class);
JsonObject sdgAsJSONObject = sdgAsJSONElement.getAsJsonObject();
JsonElement edges = sdgAsJSONObject.get("edges");

// We don't really need these fields, so we'll remove it.
sdgAsJSONObject.remove("nodes");
sdgAsJSONObject.remove("creator");
sdgAsJSONObject.remove("version");
// Remove the 'edges' element and move the list of edges up one level
combinedJsonObject.add("system_dependency_graph", edges);
List<Dependency> sdgEdges = SystemDependencyGraph.construct(input, dependencies, build);
combinedJsonObject.add("system_dependency_graph", gson.toJsonTree(sdgEdges));
}
}
// Cleanup library dependencies directory
Expand Down
7 changes: 5 additions & 2 deletions src/main/java/com/ibm/cldk/SymbolTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ private static JavaCompilationUnit processCompilationUnit(CompilationUnit parseR
typeNode.setCallableDeclarations(typeDecl.findAll(CallableDeclaration.class).stream()
.filter(c -> c.getParentNode().isPresent() && c.getParentNode().get() == typeDecl)
.map(meth -> {
Pair<String, Callable> callableDeclaration = processCallableDeclaration(meth, fieldNames, typeName);
Pair<String, Callable> callableDeclaration = processCallableDeclaration(meth, fieldNames, typeName, parseResult.getStorage().map(s -> s.getPath().toString()).orElse("<in-memory>"));
declaredMethodsAndConstructors.put(typeName, callableDeclaration.getLeft(), callableDeclaration.getRight());
return callableDeclaration;
}).collect(Collectors.toMap(p -> p.getLeft(), p -> p.getRight())));
Expand Down Expand Up @@ -249,9 +249,12 @@ private static ParameterInCallable processParameterDeclaration(Parameter paramDe
*/
@SuppressWarnings("unchecked")
private static Pair<String, Callable> processCallableDeclaration(CallableDeclaration callableDecl,
List<String> classFields, String typeName) {
List<String> classFields, String typeName, String filePath) {
Callable callableNode = new Callable();

// Set file path
callableNode.setFilePath(filePath);

// add callable signature
callableNode.setSignature(callableDecl.getSignature().asString());

Expand Down
148 changes: 93 additions & 55 deletions src/main/java/com/ibm/cldk/SystemDependencyGraph.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import com.ibm.cldk.entities.AbstractGraphEdge;
import com.ibm.cldk.entities.CallEdge;
import com.ibm.cldk.entities.Callable;
import com.ibm.cldk.entities.CallableVertex;
import com.ibm.cldk.entities.SystemDepEdge;
import com.ibm.cldk.utils.AnalysisUtils;
import com.ibm.cldk.utils.Log;
Expand All @@ -40,20 +40,60 @@
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.GraphSlicer;
import com.ibm.wala.util.graph.traverse.DFS;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.io.output.NullOutputStream;
import org.apache.commons.lang3.tuple.Pair;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.nio.json.JSONExporter;

import java.io.IOException;
import java.io.PrintStream;
import java.io.StringWriter;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Supplier;

import static com.ibm.cldk.CodeAnalyzer.gson;
import static com.ibm.cldk.utils.AnalysisUtils.*;
import java.util.stream.Collectors;

import static com.ibm.cldk.utils.AnalysisUtils.createAndPutNewCallableInSymbolTable;
import static com.ibm.cldk.utils.AnalysisUtils.getCallableFromSymbolTable;


@Data
abstract class Dependency {
public CallableVertex source;
public CallableVertex target;
}

@Data
@EqualsAndHashCode(callSuper = true)
class SDGDependency extends Dependency {
public String sourceKind;
public String destinationKind;
public String type;
public String weight;

public SDGDependency(CallableVertex source, CallableVertex target, SystemDepEdge edge) {
super.source = source;
super.target = target;
this.sourceKind = edge.getSourceKind();
this.destinationKind = edge.getDestinationKind();
this.type = edge.getType();
this.weight = String.valueOf(edge.getWeight());
}
}

@Data
@EqualsAndHashCode(callSuper = true)
class CallDependency extends Dependency {
public String type;
public String weight;

public CallDependency(CallableVertex source, CallableVertex target, AbstractGraphEdge edge) {
this.source = source;
this.target = target;
this.type = edge.toString();
this.weight = String.valueOf(edge.getWeight());
}
}

/**
* The type Sdg 2 json.
Expand All @@ -66,18 +106,10 @@ public class SystemDependencyGraph {
* @return the graph exporter
*/


private static JSONExporter<Pair<String, Callable>, AbstractGraphEdge> getGraphExporter() {
JSONExporter<Pair<String, Callable>, AbstractGraphEdge> exporter = new JSONExporter<>(
pair -> {
Map <String, String> vertex = new HashMap<>();
vertex.put("class_interface_declarations", pair.getLeft());
vertex.put("callable", gson.toJson(pair.getRight()));
return gson.toJson(vertex);
}
);
// exporter.setVertexAttributeProvider(v -> v.getRight().getAttributes());
private static JSONExporter<CallableVertex, AbstractGraphEdge> getGraphExporter() {
JSONExporter<CallableVertex, AbstractGraphEdge> exporter = new JSONExporter<>();
exporter.setEdgeAttributeProvider(AbstractGraphEdge::getAttributes);
exporter.setVertexAttributeProvider(CallableVertex::getAttributes);
return exporter;
}

Expand All @@ -90,12 +122,12 @@ private static JSONExporter<Pair<String, Callable>, AbstractGraphEdge> getGraphE
* @param edgeLabels
* @return
*/
private static org.jgrapht.Graph<Pair<String, Callable>, AbstractGraphEdge> buildGraph(
private static org.jgrapht.Graph<CallableVertex, AbstractGraphEdge> buildGraph(
Supplier<Iterator<Statement>> entryPoints,
Graph<Statement> sdg, CallGraph callGraph,
BiFunction<Statement, Statement, String> edgeLabels) {

org.jgrapht.Graph<Pair<String, Callable>, AbstractGraphEdge> graph = new DefaultDirectedGraph<>(
org.jgrapht.Graph<CallableVertex, AbstractGraphEdge> graph = new DefaultDirectedGraph<>(
AbstractGraphEdge.class);

// We'll use forward and backward search on the DFS to identify which CFG nodes
Expand Down Expand Up @@ -130,21 +162,22 @@ private static org.jgrapht.Graph<Pair<String, Callable>, AbstractGraphEdge> buil
&& !p.getNode().getMethod().equals(s.getNode().getMethod())) {

// Add the source nodes to the graph as vertices
Pair<String, Callable> source = Optional.ofNullable(getCallableFromSymbolTable(p.getNode().getMethod())).orElseGet(() -> createAndPutNewCallableInSymbolTable(p.getNode().getMethod()));
graph.addVertex(source);

Map<String, String> source = Optional.ofNullable(getCallableFromSymbolTable(p.getNode().getMethod())).orElseGet(() -> createAndPutNewCallableInSymbolTable(p.getNode().getMethod()));
// Add the target nodes to the graph as vertices
Pair<String, Callable> target = Optional.ofNullable(getCallableFromSymbolTable(s.getNode().getMethod())).orElseGet(() -> createAndPutNewCallableInSymbolTable(s.getNode().getMethod()));
graph.addVertex(target);

String edgeType = edgeLabels.apply(p, s);
SystemDepEdge graphEdge = new SystemDepEdge(p, s, edgeType);
SystemDepEdge cgEdge = (SystemDepEdge) graph.getEdge(source, target);
if (source.getRight() != null && target.getRight() != null) {
Map<String, String> target = Optional.ofNullable(getCallableFromSymbolTable(s.getNode().getMethod())).orElseGet(() -> createAndPutNewCallableInSymbolTable(s.getNode().getMethod()));

if (source != null && target != null) {
CallableVertex source_vertex = new CallableVertex(source);
CallableVertex target_vertex = new CallableVertex(target);
graph.addVertex(source_vertex);
graph.addVertex(target_vertex);
String edgeType = edgeLabels.apply(p, s);
SystemDepEdge graphEdge = new SystemDepEdge(p, s, edgeType);
SystemDepEdge cgEdge = (SystemDepEdge) graph.getEdge(source_vertex, target_vertex);
if (cgEdge == null || !cgEdge.equals(graphEdge)) {
graph.addEdge(
source,
target,
source_vertex,
target_vertex,
graphEdge);
} else {
graphEdge.incrementWeight();
Expand All @@ -163,21 +196,22 @@ private static org.jgrapht.Graph<Pair<String, Callable>, AbstractGraphEdge> buil
.forEach(o -> {

// Add the source nodes to the graph as vertices
Pair<String, Callable> source = Optional.ofNullable(getCallableFromSymbolTable(p.getMethod())).orElseGet(() -> createAndPutNewCallableInSymbolTable(p.getMethod()));
graph.addVertex(source);
Map<String, String> source = Optional.ofNullable(getCallableFromSymbolTable(p.getMethod())).orElseGet(() -> createAndPutNewCallableInSymbolTable(p.getMethod()));
CallableVertex source_vertex = new CallableVertex(source);

// Add the target nodes to the graph as vertices
Pair<String, Callable> target = Optional.ofNullable(getCallableFromSymbolTable(o.getMethod())).orElseGet(() -> createAndPutNewCallableInSymbolTable(o.getMethod()));
graph.addVertex(target);

if (!source.equals(target) && source.getRight() != null && target.getRight() != null) {
Map<String, String> target = Optional.ofNullable(getCallableFromSymbolTable(o.getMethod())).orElseGet(() -> createAndPutNewCallableInSymbolTable(o.getMethod()));
CallableVertex target_vertex = new CallableVertex(target);

if (!source.equals(target) && target != null) {
// Get the edge between the source and the target
AbstractGraphEdge cgEdge = graph.getEdge(source, target);
graph.addVertex(source_vertex);
graph.addVertex(target_vertex);
AbstractGraphEdge cgEdge = graph.getEdge(source_vertex, target_vertex);
if (cgEdge instanceof CallEdge) {
((CallEdge) cgEdge).incrementWeight();
} else {
graph.addEdge(source, target, new CallEdge());
graph.addEdge(source_vertex, target_vertex, new CallEdge());
}
}
});
Expand All @@ -192,15 +226,15 @@ private static org.jgrapht.Graph<Pair<String, Callable>, AbstractGraphEdge> buil
*
* @param input the input
* @param dependencies the dependencies
* @param build The build options
* @param build The build options
* @return A List of triples containing the source, destination, and edge type
* @throws IOException the io exception
* @throws ClassHierarchyException the class hierarchy exception
* @throws IllegalArgumentException the illegal argument exception
* @throws CallGraphBuilderCancelException the call graph builder cancel
* exception
*/
public static String construct(
public static List<Dependency> construct(
String input, String dependencies, String build)
throws IOException, ClassHierarchyException, IllegalArgumentException, CallGraphBuilderCancelException {

Expand Down Expand Up @@ -243,12 +277,7 @@ public static String construct(
+ Math.ceil((double) (System.currentTimeMillis() - start_time) / 1000) + " seconds.");

// set cyclomatic complexity for callables in the symbol table
callGraph.forEach(cgNode -> {
Callable callable = getCallableFromSymbolTable(cgNode.getMethod()).getRight();
if (callable != null) {
callable.setCyclomaticComplexity(getCyclomaticComplexity(cgNode.getIR()));
}
});
AnalysisUtils.setCyclomaticComplexity(callGraph);

// Build SDG graph
Log.info("Building System Dependency Graph.");
Expand All @@ -266,22 +295,31 @@ public static String construct(
.getDeclaringClass()
.getClassLoader()
.getReference()
.equals(ClassLoaderReference.Application)));
.equals(ClassLoaderReference.Application))
);

// A supplier to get entries
Supplier<Iterator<Statement>> sdgEntryPointsSupplier = () -> callGraph.getEntrypointNodes().stream()
.map(n -> (Statement) new MethodEntryStatement(n)).iterator();

org.jgrapht.Graph<Pair<String, Callable>, AbstractGraphEdge> sdgGraph = buildGraph(
org.jgrapht.Graph<CallableVertex, AbstractGraphEdge> sdgGraph = buildGraph(
sdgEntryPointsSupplier,
prunedGraph, callGraph,
(p, s) -> String.valueOf(sdg.getEdgeLabels(p, s).iterator().next()));

JSONExporter<Pair<String, Callable>, AbstractGraphEdge> graphExporter = getGraphExporter();
(p, s) -> String.valueOf(sdg.getEdgeLabels(p, s).iterator().next())
);

StringWriter sdgWriter = new StringWriter();
graphExporter.exportGraph(sdgGraph, sdgWriter);
List<Dependency> edges = sdgGraph.edgeSet().stream()
.map(abstractGraphEdge -> {
CallableVertex source = sdgGraph.getEdgeSource(abstractGraphEdge);
CallableVertex target = sdgGraph.getEdgeTarget(abstractGraphEdge);
if (abstractGraphEdge instanceof CallEdge) {
return new CallDependency(source, target, abstractGraphEdge);
} else {
return new SDGDependency(source, target, (SystemDepEdge) abstractGraphEdge);
}
})
.collect(Collectors.toList());

return sdgWriter.toString();
return edges;
}
}
40 changes: 21 additions & 19 deletions src/main/java/com/ibm/cldk/entities/AbstractGraphEdge.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,34 @@
import com.ibm.wala.shrike.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSAInstruction;
import lombok.Getter;
import org.jgrapht.nio.Attribute;

import java.io.Serializable;
import java.util.Map;

import static com.ibm.cldk.CodeAnalyzer.gson;

/**
* The type Abstract graph edge.
*/
@Getter
public abstract class AbstractGraphEdge implements Serializable {
/**
* The Context.
* -- GETTER --
* Gets context.
*
* @return the context

*/
public final String context;
/**
* The Weight.
* -- GETTER --
* Gets weight.
*
* @return the weight

*/
public Integer weight = 1;

Expand All @@ -59,15 +72,6 @@ public void incrementWeight() {
this.weight += 1;
}

/**
* Gets context.
*
* @return the context
*/
public String getContext() {
return this.context;
}

/**
* Gets id.
*
Expand All @@ -77,15 +81,6 @@ public Integer getId() {
return this.hashCode();
}

/**
* Gets weight.
*
* @return the weight
*/
public Integer getWeight() {
return this.weight;
}

/**
* Gets statement position.
*
Expand All @@ -112,10 +107,17 @@ Integer getStatementPosition(Statement statement) {
return pos;
}

@Override
public String toString() {
return gson.toJson(this);
}

/**
* Gets attributes.
*
* @return the attributes
*/
public abstract Map<String, Attribute> getAttributes();

public abstract Map<String, String> getAttributesMap();
}
Loading