Skip to content

Commit

Permalink
Merge pull request #60 from IBM/1.1.0
Browse files Browse the repository at this point in the history
1.1.0
  • Loading branch information
rahlk authored Oct 31, 2024
2 parents e2bbf7d + 270a1d4 commit fe59c40
Show file tree
Hide file tree
Showing 14 changed files with 293 additions and 98 deletions.
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

0 comments on commit fe59c40

Please sign in to comment.