Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimization to building OPAL Class Hierarchies #490

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,16 @@

package eu.fasten.analyzer.javacgopal.data;

import com.google.common.collect.Lists;
import eu.fasten.analyzer.javacgopal.data.analysis.OPALClassHierarchy;
import eu.fasten.analyzer.javacgopal.data.analysis.OPALMethod;
import eu.fasten.analyzer.javacgopal.data.analysis.OPALType;
import eu.fasten.core.data.Constants;
import eu.fasten.core.data.JavaGraph;
import eu.fasten.core.data.PartialJavaCallGraph;
import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

import eu.fasten.core.data.opal.MavenArtifactDownloader;
import eu.fasten.core.data.opal.MavenCoordinate;
import eu.fasten.core.data.opal.exceptions.OPALException;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.text.StringEscapeUtils;
import org.opalj.br.Annotation;
Expand All @@ -45,20 +43,20 @@
import org.opalj.value.ValueInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;

import eu.fasten.analyzer.javacgopal.data.analysis.OPALClassHierarchy;
import eu.fasten.analyzer.javacgopal.data.analysis.OPALMethod;
import eu.fasten.analyzer.javacgopal.data.analysis.OPALType;
import eu.fasten.core.data.Constants;
import eu.fasten.core.data.JavaGraph;
import eu.fasten.core.data.opal.MavenArtifactDownloader;
import eu.fasten.core.data.opal.MavenCoordinate;
import eu.fasten.core.data.opal.exceptions.OPALException;
import scala.Function1;
import scala.collection.JavaConverters;

import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
* Call graphs that are not still fully resolved. i.e. isolated call graphs which within-artifact
* calls (edges) are known as internal calls and Cross-artifact calls are known as external calls.
Expand All @@ -85,7 +83,7 @@ public OPALPartialCallGraph construct(OPALCallGraph ocg, CallPreservationStrateg
createGraphWithExternalCHA(ocg, cha, callSiteOnly);

pcg.nodeCount = cha.getNodeCount();
pcg.classHierarchy = cha.asURIHierarchy(ocg.project.classHierarchy());
pcg.classHierarchy = cha.asURIHierarchyParallel(ocg.project.classHierarchy());
} catch (Exception e) {
if (e.getStackTrace().length > 0) {
var stackTrace = e.getStackTrace()[0];
Expand Down Expand Up @@ -225,37 +223,35 @@ private Map<Method, Integer> getMethodsMap(final int keyStartsFrom,
private void createGraphWithExternalCHA(final OPALCallGraph ocg,
final OPALClassHierarchy cha, CallPreservationStrategy callSiteOnly) {
// TODO instead of relying on pcg field, use parameter
final var cg = ocg.callGraph;
final var cg = ocg.callGraph;
final var tac = ocg.project.get(ComputeTACAIKey$.MODULE$);

for (final var sourceDeclaration : JavaConverters
.asJavaIterable(cg.reachableMethods().toIterable())) {

JavaConverters.asJavaCollection(cg.reachableMethods().toIterable()).parallelStream().forEach(sourceDeclaration -> {
final List<Integer> incompeletes = new ArrayList<>();
if (cg.incompleteCallSitesOf(sourceDeclaration) != null) {
JavaConverters.asJavaIterator(cg.incompleteCallSitesOf(sourceDeclaration))
.forEachRemaining(pc -> incompeletes.add((int) pc));
.forEachRemaining(pc -> incompeletes.add((int) pc));
}
final Set<Integer> visitedPCs = new HashSet<>();

if (sourceDeclaration.hasMultipleDefinedMethods()) {
for (final var source : JavaConverters
.asJavaIterable(sourceDeclaration.definedMethods())) {
.asJavaIterable(sourceDeclaration.definedMethods())) {
cha.appendGraph(source, cg.calleesOf(sourceDeclaration),
getStmts(tac, sourceDeclaration.definedMethod()), pcg.graph, incompeletes,
visitedPCs, callSiteOnly);
getStmts(tac, sourceDeclaration.definedMethod()), pcg.graph, incompeletes,
visitedPCs, callSiteOnly);
}
} else if (sourceDeclaration.hasSingleDefinedMethod()) {
final var definedMethod = sourceDeclaration.definedMethod();
cha.appendGraph(definedMethod, cg.calleesOf(sourceDeclaration),
getStmts(tac, definedMethod), pcg.graph, incompeletes, visitedPCs, callSiteOnly);
getStmts(tac, definedMethod), pcg.graph, incompeletes, visitedPCs, callSiteOnly);

} else if (sourceDeclaration.isVirtualOrHasSingleDefinedMethod()) {

cha.appendGraph(sourceDeclaration, cg.calleesOf(sourceDeclaration), getStmts(tac,
null), pcg.graph, incompeletes, visitedPCs, callSiteOnly);
null), pcg.graph, incompeletes, visitedPCs, callSiteOnly);
}

}
});
}

private Stmt<DUVar<ValueInformation>>[] getStmts(Function1<Method, AITACode<TACMethodParameter, ValueInformation>> tac,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,12 @@

package eu.fasten.analyzer.javacgopal.data.analysis;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import eu.fasten.analyzer.javacgopal.data.CallPreservationStrategy;
import eu.fasten.core.data.FastenURI;
import eu.fasten.core.data.JavaGraph;
import eu.fasten.core.data.JavaScope;
import eu.fasten.core.data.JavaType;
import it.unimi.dsi.fastutil.ints.IntIntPair;
import org.opalj.br.ClassHierarchy;
import org.opalj.br.DeclaredMethod;
import org.opalj.br.Method;
Expand All @@ -38,17 +34,21 @@
import org.opalj.tac.Stmt;
import org.opalj.tac.UVar;
import org.opalj.value.ValueInformation;

import eu.fasten.analyzer.javacgopal.data.CallPreservationStrategy;
import eu.fasten.core.data.FastenURI;
import eu.fasten.core.data.JavaGraph;
import eu.fasten.core.data.JavaScope;
import eu.fasten.core.data.JavaType;
import it.unimi.dsi.fastutil.ints.IntIntPair;
import scala.Tuple2;
import scala.collection.Iterator;
import scala.collection.JavaConverters;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
* Class hierarchy class containing two types of CHA - internal and external CHA
* and also keeping track of node count.
Expand Down Expand Up @@ -111,6 +111,34 @@ public EnumMap<JavaScope, Map<String, JavaType>> asURIHierarchy(ClassHierarchy p
JavaScope.resolvedTypes, new HashMap<>()));
}

/**
* An optimized version of `asURIHierarchy`.
* Converts all of the members of the classHierarchy to {@link FastenURI}.
*
* @param projectHierarchy OPAL class hierarchy
* @return A {@link Map} of {@link FastenURI} and {@link JavaType}
*/
public EnumMap<JavaScope, Map<String, JavaType>> asURIHierarchyParallel(ClassHierarchy projectHierarchy) {

final Map<String, JavaType> internalResult = new ConcurrentHashMap<>();
final Map<String, JavaType> externalResult = new ConcurrentHashMap<>();
final var internals = this.getInternalCHA();

internals.keySet().stream().parallel().forEach(aClass -> {
final var klass = OPALType.getType(internals.get(aClass), aClass);
internalResult.put(klass.getLeft(), klass.getRight());
});

final var externals = this.getExternalCHA();
externals.keySet().stream().parallel().forEach(aClass -> {
externalResult
.putAll(OPALType.getType(projectHierarchy, externals.get(aClass), aClass));
});

return new EnumMap<>(Map.of(JavaScope.internalTypes, internalResult, JavaScope.externalTypes, externalResult,
JavaScope.resolvedTypes, new HashMap<>()));
}

/**
* Adds a method to the external CHA if the method doesn't already exist.
* Otherwise returns and ID of the existing method.
Expand Down Expand Up @@ -234,17 +262,18 @@ public Map<Object, Object> getInternalMetadata(final Map<List<Integer>, Map<Obje
}

/**
* Append a sub-graph to already existing PartialJavaCallGraph.
* @param source source method
* @param targets list of targets
* @param resultGraph already existing PartialJavaCallGraph
* Append a sub-graph to already existing PartialJavaCallGraph. It is thread-safe.
*
* @param source source method
* @param targets list of targets
* @param resultGraph already existing PartialJavaCallGraph
* @param callSiteOnly
*/
public void appendGraph(final Object source,
final Iterator<Tuple2<Object, Iterator<DeclaredMethod>>> targets,
final Stmt<DUVar<ValueInformation>>[] stmts,
final JavaGraph resultGraph, List<Integer> incompeletes,
final Set<Integer> visitedPCs, CallPreservationStrategy callSiteOnly) {
public synchronized void appendGraph(final Object source,
mir-am marked this conversation as resolved.
Show resolved Hide resolved
final Iterator<Tuple2<Object, Iterator<DeclaredMethod>>> targets,
final Stmt<DUVar<ValueInformation>>[] stmts,
final JavaGraph resultGraph, List<Integer> incompeletes,
final Set<Integer> visitedPCs, CallPreservationStrategy callSiteOnly) {
final var edges = this.getSubGraph(source, targets, stmts, incompeletes, visitedPCs, callSiteOnly);
resultGraph.append(edges);
}
Expand Down