Skip to content

Commit fcb1d9e

Browse files
Grace Tangkhatchad
authored andcommitted
Fix #108
* slicing * add comments * add comments * visitor * delete test output * requested changes * Some changes to @saledouble's code #196. * Forgot uses for #196. * Revert "Add bundle to build." This reverts commit 5cc28ac. * move to a class * streamOp? * merge * Forgot uses for #196. * delete * fix casting * add null checker * fix infinite calls * fix format * fix ilegal argument * check class * improve * improve * fix logic * delete unused code * use pruned callgraph * Revert "use pruned callgraph" This reverts commit 335c21e. * Remove space. * Re-add space. * improve * improve * revert if condition * clear * implement base stream * rename * rename * make private
1 parent c556c03 commit fcb1d9e

File tree

4 files changed

+231
-8
lines changed

4 files changed

+231
-8
lines changed

edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/StreamAnalyzer.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,8 +405,6 @@ protected Collection<Entrypoint> buildCallGraph(EclipseProjectAnalysisEngine<Ins
405405
+ engine.getProject().getElementName());
406406
throw e;
407407
}
408-
// TODO: Can I slice the graph so that only nodes relevant to the
409-
// instance in question are present?
410408
this.enginesWithBuiltCallGraphsToEntrypointsUsed.put(engine, entryPoints);
411409
}
412410
return this.enginesWithBuiltCallGraphsToEntrypointsUsed.get(engine);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package edu.cuny.hunter.streamrefactoring.core.analysis;
2+
3+
import java.util.Arrays;
4+
5+
import com.ibm.wala.ipa.cha.IClassHierarchy;
6+
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
7+
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
8+
import com.ibm.wala.ssa.SSACheckCastInstruction;
9+
import com.ibm.wala.ssa.SSAConditionalBranchInstruction;
10+
import com.ibm.wala.ssa.SSAConversionInstruction;
11+
import com.ibm.wala.ssa.SSAGetCaughtExceptionInstruction;
12+
import com.ibm.wala.ssa.SSAGetInstruction;
13+
import com.ibm.wala.ssa.SSAInstanceofInstruction;
14+
import com.ibm.wala.ssa.SSAInstruction.Visitor;
15+
import com.ibm.wala.ssa.SSAInvokeInstruction;
16+
import com.ibm.wala.ssa.SSALoadMetadataInstruction;
17+
import com.ibm.wala.ssa.SSANewInstruction;
18+
import com.ibm.wala.ssa.SSAPutInstruction;
19+
import com.ibm.wala.types.TypeReference;
20+
21+
public class StreamFindingVisitor extends Visitor {
22+
23+
private IClassHierarchy classHierarchy;
24+
25+
private boolean foundStream = false;
26+
27+
public StreamFindingVisitor(IClassHierarchy classHierarchy) {
28+
this.classHierarchy = classHierarchy;
29+
}
30+
31+
protected IClassHierarchy getClassHierarchy() {
32+
return this.classHierarchy;
33+
}
34+
35+
public boolean hasFoundStream() {
36+
return this.foundStream;
37+
}
38+
39+
private void processType(TypeReference type) {
40+
if (Util.implementsBaseStream(type, this.getClassHierarchy()))
41+
this.setFoundStream(true);
42+
}
43+
44+
protected void setFoundStream(boolean foundStream) {
45+
this.foundStream = foundStream;
46+
}
47+
48+
@Override
49+
public void visitArrayLoad(SSAArrayLoadInstruction instruction) {
50+
this.processType(instruction.getElementType());
51+
}
52+
53+
@Override
54+
public void visitArrayStore(SSAArrayStoreInstruction instruction) {
55+
this.processType(instruction.getElementType());
56+
}
57+
58+
@Override
59+
public void visitCheckCast(SSACheckCastInstruction instruction) {
60+
Arrays.stream(instruction.getDeclaredResultTypes()).forEach(this::processType);
61+
}
62+
63+
@Override
64+
public void visitConditionalBranch(SSAConditionalBranchInstruction instruction) {
65+
this.processType(instruction.getType());
66+
}
67+
68+
@Override
69+
public void visitConversion(SSAConversionInstruction instruction) {
70+
this.processType(instruction.getToType());
71+
this.processType(instruction.getFromType());
72+
}
73+
74+
@Override
75+
public void visitGet(SSAGetInstruction instruction) {
76+
this.processType(instruction.getDeclaredFieldType());
77+
}
78+
79+
@Override
80+
public void visitGetCaughtException(SSAGetCaughtExceptionInstruction instruction) {
81+
}
82+
83+
@Override
84+
public void visitInstanceof(SSAInstanceofInstruction instruction) {
85+
this.processType(instruction.getCheckedType());
86+
}
87+
88+
@Override
89+
public void visitInvoke(SSAInvokeInstruction instruction) {
90+
this.processType(instruction.getDeclaredResultType());
91+
}
92+
93+
@Override
94+
public void visitLoadMetadata(SSALoadMetadataInstruction instruction) {
95+
this.processType(instruction.getType());
96+
}
97+
98+
@Override
99+
public void visitNew(SSANewInstruction instruction) {
100+
this.processType(instruction.getConcreteType());
101+
}
102+
103+
@Override
104+
public void visitPut(SSAPutInstruction instruction) {
105+
this.processType(instruction.getDeclaredFieldType());
106+
}
107+
}

edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/StreamStateMachine.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
5959
import com.ibm.wala.ipa.callgraph.propagation.NormalAllocationInNode;
6060
import com.ibm.wala.ipa.callgraph.propagation.PointerKey;
61+
import com.ibm.wala.ipa.callgraph.pruned.PrunedCallGraph;
6162
import com.ibm.wala.ipa.cfg.BasicBlockInContext;
6263
import com.ibm.wala.ipa.cha.IClassHierarchy;
6364
import com.ibm.wala.ipa.modref.ModRef;
@@ -982,7 +983,8 @@ public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjec
982983
SubMonitor subMonitor = SubMonitor.convert(monitor, "Performing typestate analysis (may take a while)", 100);
983984
Map<TypestateRule, Statistics> ret = new HashMap<>();
984985

985-
BenignOracle ora = new ModifiedBenignOracle(engine.getCallGraph(), engine.getPointerAnalysis());
986+
CallGraph prunedCallGraph = pruneCallGraph(engine.getCallGraph(), engine.getClassHierarchy());
987+
BenignOracle ora = new ModifiedBenignOracle(prunedCallGraph, engine.getPointerAnalysis());
986988

987989
PropertiesManager manager = PropertiesManager.initFromMap(Collections.emptyMap());
988990
PropertiesManager.registerProperties(
@@ -1006,7 +1008,7 @@ public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjec
10061008

10071009
// this gets a solver that tracks all streams.
10081010
LOGGER.info(() -> "Starting " + rule.getName() + " solver for: " + engine.getProject().getElementName());
1009-
ISafeSolver solver = TypestateSolverFactory.getSolver(engine.getOptions(), engine.getCallGraph(),
1011+
ISafeSolver solver = TypestateSolverFactory.getSolver(engine.getOptions(), prunedCallGraph,
10101012
engine.getPointerAnalysis(), engine.getHeapGraph(), dfa, ora, typeStateOptions, null, null, null);
10111013

10121014
AggregateSolverResult result;
@@ -1043,7 +1045,7 @@ public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjec
10431045
// TODO: Can this be somehow rewritten to get blocks corresponding to terminal
10441046
// operations?
10451047
// for each call graph node in the call graph.
1046-
for (CGNode cgNode : engine.getCallGraph())
1048+
for (CGNode cgNode : prunedCallGraph)
10471049
// separating client from library code, improving performance #103.
10481050
if (cgNode.getMethod().getDeclaringClass().getClassLoader().getReference()
10491051
.equals(ClassLoaderReference.Application)) {
@@ -1228,7 +1230,7 @@ public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjec
12281230

12291231
// discover whether any stateful intermediate operations are
12301232
// present.
1231-
this.discoverPossibleStatefulIntermediateOperations(engine.getClassHierarchy(), engine.getCallGraph(),
1233+
this.discoverPossibleStatefulIntermediateOperations(engine.getClassHierarchy(), prunedCallGraph,
12321234
subMonitor.split(5, SubMonitor.SUPPRESS_NONE));
12331235

12341236
// does reduction order matter?
@@ -1291,4 +1293,30 @@ public Map<TypestateRule, Statistics> start(Set<Stream> streamSet, EclipseProjec
12911293
}
12921294
return ret;
12931295
}
1296+
1297+
/**
1298+
* This method is used to prune call graph. For each CGNode in the callGraph, it
1299+
* check whether it is a stream node. If it is, then keep it. If it not, then
1300+
* remove it.
1301+
*
1302+
* @param callGraph
1303+
* @param classHierarchy
1304+
* @return A pruned callGraph
1305+
*/
1306+
private static CallGraph pruneCallGraph(CallGraph callGraph, IClassHierarchy classHierarchy) {
1307+
int numberOfNodesInCallGraph = callGraph.getNumberOfNodes();
1308+
LOGGER.info("The number of nodes in the call graph: " + numberOfNodesInCallGraph);
1309+
HashSet<CGNode> keep = new HashSet<>();
1310+
for (CGNode node : callGraph) {
1311+
if (Util.isStreamNode(node, classHierarchy))
1312+
keep.add(node);
1313+
}
1314+
1315+
PrunedCallGraph prunedCallGraph = new PrunedCallGraph(callGraph, keep);
1316+
int numberOfNodesInPrunedCallGraph = prunedCallGraph.getNumberOfNodes();
1317+
LOGGER.info("The number of nodes in partial graph: " + numberOfNodesInPrunedCallGraph
1318+
+ ". The number of saved nodes: " + (numberOfNodesInCallGraph - numberOfNodesInPrunedCallGraph));
1319+
1320+
return prunedCallGraph;
1321+
}
12941322
}

edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/Util.java

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import java.util.logging.Logger;
1818
import java.util.stream.BaseStream;
1919
import java.util.stream.Collectors;
20+
import java.util.stream.IntStream;
21+
import java.util.stream.Stream;
2022

2123
import org.eclipse.jdt.core.IJavaProject;
2224
import org.eclipse.jdt.core.IType;
@@ -407,21 +409,46 @@ static int getLineNumberFromIR(IBytecodeMethod method, SSAInstruction instructio
407409
return lineNumberFromIR;
408410
}
409411

412+
/**
413+
* This set is used to store visited values. Without this set, the method
414+
* getPossibleTypes could be infinitely recursively called and would never
415+
* return. So adding this set means to add a base case.
416+
*/
417+
static private HashSet<Value> seenValues = new HashSet<>();
418+
410419
static Collection<TypeAbstraction> getPossibleTypes(int valueNumber, TypeInference inference) {
420+
seenValues.clear();
421+
return getPossibleTypesInternal(valueNumber, inference);
422+
}
423+
424+
static private Collection<TypeAbstraction> getPossibleTypesInternal(int valueNumber, TypeInference inference) {
411425
Set<TypeAbstraction> ret = new HashSet<>();
412-
Value value = inference.getIR().getSymbolTable().getValue(valueNumber);
426+
Value value;
427+
try {
428+
value = inference.getIR().getSymbolTable().getValue(valueNumber);
429+
} catch (IllegalArgumentException exception) {
430+
LOGGER.info("The value number is invalid for getting possible types.");
431+
return ret;
432+
}
413433

414434
// TODO: Should really be using a pointer analysis here rather than
415435
// re-implementing one using PhiValue.
416436
if (value instanceof PhiValue) {
437+
438+
// avoid infinite recursion here
439+
if (seenValues.contains(value))
440+
return ret;
441+
else
442+
seenValues.add(value);
443+
417444
// multiple possible types.
418445
PhiValue phiValue = (PhiValue) value;
419446
SSAPhiInstruction phiInstruction = phiValue.getPhiInstruction();
420447
int numberOfUses = phiInstruction.getNumberOfUses();
421448
// get the possible types for each use.
422449
for (int i = 0; i < numberOfUses; i++) {
423450
int use = phiInstruction.getUse(i);
424-
Collection<TypeAbstraction> possibleTypes = getPossibleTypes(use, inference);
451+
Collection<TypeAbstraction> possibleTypes = getPossibleTypesInternal(use, inference);
425452
ret.addAll(possibleTypes);
426453
}
427454
} else
@@ -458,6 +485,7 @@ public static Collection<TypeAbstraction> getPossibleTypesInterprocedurally(Coll
458485
// type is java.lang.Object.
459486
// Find the return type of the instruction.
460487
TypeInference inference = TypeInference.make(node.getIR(), false);
488+
// Get all possible types
461489
Collection<TypeAbstraction> returnTypes = Util.getPossibleTypes(valueNumber, inference);
462490

463491
// for each return type.
@@ -611,6 +639,9 @@ public static boolean implementsIterable(TypeReference reference, IClassHierarch
611639

612640
public static boolean implementsType(TypeReference typeReference, IClassHierarchy classHierarchy,
613641
Predicate<IClass> predicate) {
642+
if (typeReference == null)
643+
return false;
644+
614645
IClass clazz = classHierarchy.lookupClass(typeReference);
615646

616647
if (clazz == null)
@@ -761,4 +792,63 @@ private static boolean wouldOrderingBeConsistent(final Collection<TypeAbstractio
761792

762793
private Util() {
763794
}
795+
796+
/**
797+
* This method is used to check whether the CGNode is a "stream node". It checks
798+
* each instruction in the node. If the type of instruction could implement base
799+
* stream, then it is a stream node.
800+
*
801+
* @param node:
802+
* CGNode
803+
* @param classHierarchy
804+
*/
805+
public static boolean isStreamNode(CGNode node, IClassHierarchy classHierarchy) {
806+
if (isDeclaredStreamClass(node, classHierarchy))
807+
return true;
808+
809+
IR ir = node.getIR();
810+
811+
if (ir == null || ir.isEmptyIR())
812+
return true;
813+
814+
for (SSAInstruction instruction : ir.getInstructions()) {
815+
if (instruction == null)
816+
continue;
817+
818+
// Most of instruction APIs provide the methods to get the return types.
819+
StreamFindingVisitor visitor = new StreamFindingVisitor(classHierarchy);
820+
instruction.visit(visitor);
821+
822+
if (visitor.hasFoundStream())
823+
return true;
824+
825+
// otherwise, let's check the defs and uses.
826+
TypeInference inference = TypeInference.make(ir, false);
827+
828+
Stream<TypeAbstraction> defs = IntStream.range(0, instruction.getNumberOfDefs())
829+
.mapToObj(i -> instruction.getDef(i)).flatMap(d -> getPossibleTypes(d, inference).stream());
830+
831+
Stream<TypeAbstraction> uses = IntStream.range(0, instruction.getNumberOfUses())
832+
.mapToObj(i -> instruction.getUse(i)).flatMap(u -> getPossibleTypes(u, inference).stream());
833+
834+
if (Stream.concat(defs, uses).anyMatch(t -> implementsBaseStream(t.getTypeReference(), classHierarchy)))
835+
return true;
836+
}
837+
838+
return false;
839+
}
840+
841+
/**
842+
* Check declared class for CGNode
843+
*
844+
* @param node:
845+
* CGNode in the CallGraph
846+
*/
847+
private static boolean isDeclaredStreamClass(CGNode node, IClassHierarchy classHierarchy) {
848+
IMethod method = node.getMethod();
849+
if (implementsBaseStream(method.getDeclaringClass().getReference(), classHierarchy))
850+
return true;
851+
else
852+
return false;
853+
}
764854
}

0 commit comments

Comments
 (0)