Skip to content

Commit 3a10355

Browse files
authored
Merge pull request #181 from ponder-lab/issue_80
- Fix #80. - Fix #176.
2 parents 6a32928 + fbf1605 commit 3a10355

File tree

20 files changed

+528
-133
lines changed

20 files changed

+528
-133
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ This prototype refactoring plug-in for [Eclipse](http://eclipse.org) represents
1515

1616
Explicit entry points may be marked using the appropriate annotation found in the corresponding [annotation library][annotations].
1717

18-
Explicit entry points can also be marked using importing entry points from a txt file. Each time we run the tool, a txt file named "entry_points.txt" is generated and contained in the current directory of workspace. Then, before next time we run tool to evaluate the same project, move or copy "entry_points.txt" into project directory or workspace directory of the project. While evaluating the project, the tool will ignore the explicit entry points which are added manually and regonize the explicit entry points through the file automatically.
18+
Explicit entry points can also be marked using importing entry points from a txt file. Each time we run the tool, a txt file named "entry_points.txt" is generated and contained in the current directory of workspace. Then, before next time we run tool to evaluate the same project, move or copy "entry_points.txt" into project directory or workspace directory of the project. While evaluating the project, the tool will ignore the explicit entry points which are added manually and recognize the explicit entry points through the file automatically.
1919

2020
### Limitations
2121

@@ -41,10 +41,17 @@ You should have the following projects in your workspace:
4141

4242
### Running the Evaluator
4343

44-
[annotations]: https://github.com/ponder-lab/edu.cuny.hunter.streamrefactoring.annotations
44+
#### Configuring the Evaluation
45+
46+
A file named `eval.properties` can be placed at the project root. The following keys are available:
47+
48+
Key | Value Type | Description
49+
---------------- | ---------- | ----------
50+
nToUseForStreams | Integer | The value of N to use while building the nCFA for stream types.
4551

4652
### Further Information
4753

4854
See the [wiki][wiki] for further information.
4955

5056
[wiki]: https://github.com/ponder-lab/Java-8-Stream-Refactoring/wiki
57+
[annotations]: https://github.com/ponder-lab/edu.cuny.hunter.streamrefactoring.annotations

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ public enum PreconditionFailure {
1919
CURRENTLY_NOT_HANDLED(14), // should just be #97 currently.
2020
STREAM_CODE_NOT_REACHABLE(15), // either pivotal code isn't reachable or
2121
// entry points are misconfigured.
22-
NO_ENTRY_POINT(16); // user didn't specify entry points.
22+
NO_ENTRY_POINT(16), // user didn't specify entry points.
23+
NO_APPLICATION_CODE_IN_CALL_STRINGS(17); // N may be too small.
2324

2425
private int code;
2526

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

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
import com.ibm.wala.types.TypeReference;
6060
import com.ibm.wala.util.CancelException;
6161

62+
import edu.cuny.hunter.streamrefactoring.core.safe.NoApplicationCodeExistsInCallStringsException;
6263
import edu.cuny.hunter.streamrefactoring.core.utils.LoggerNames;
6364
import edu.cuny.hunter.streamrefactoring.core.utils.Util;
6465
import edu.cuny.hunter.streamrefactoring.core.wala.EclipseProjectAnalysisEngine;
@@ -422,18 +423,33 @@ protected Ordering getInitialOrdering() {
422423
}
423424

424425
public InstanceKey getInstanceKey(Collection<InstanceKey> trackedInstances,
425-
EclipseProjectAnalysisEngine<InstanceKey> engine) throws InvalidClassFileException, IOException,
426-
CoreException, InstanceKeyNotFoundException, UnhandledCaseException {
427-
if (instanceKey == null) {
428-
instanceKey = this.getInstructionForCreation(engine)
429-
.flatMap(instruction -> trackedInstances.stream()
430-
.filter(ik -> instanceKeyCorrespondsWithInstantiationInstruction(ik, instruction,
431-
this.getEnclosingMethodReference(), engine.getCallGraph()))
432-
.findFirst())
433-
.orElseThrow(() -> new InstanceKeyNotFoundException("Can't find instance key for: "
434-
+ this.getCreation() + " using tracked instances: " + trackedInstances));
426+
EclipseProjectAnalysisEngine<InstanceKey> engine)
427+
throws InvalidClassFileException, IOException, CoreException, InstanceKeyNotFoundException,
428+
UnhandledCaseException, NoApplicationCodeExistsInCallStringsException {
429+
// if not present.
430+
if (this.instanceKey == null)
431+
// compute it.
432+
this.instanceKey = computeInstanceKey(trackedInstances, engine);
433+
return this.instanceKey;
434+
}
435+
436+
protected InstanceKey computeInstanceKey(Collection<InstanceKey> trackedInstances,
437+
EclipseProjectAnalysisEngine<InstanceKey> engine)
438+
throws InvalidClassFileException, IOException, CoreException, UnhandledCaseException,
439+
NoApplicationCodeExistsInCallStringsException, InstanceKeyNotFoundException {
440+
Optional<SSAInvokeInstruction> instructionForCreation = this.getInstructionForCreation(engine);
441+
442+
if (instructionForCreation.isPresent()) {
443+
SSAInvokeInstruction instruction = instructionForCreation.get();
444+
445+
for (InstanceKey ik : trackedInstances)
446+
if (instanceKeyCorrespondsWithInstantiationInstruction(ik, instruction,
447+
this.getEnclosingMethodReference(), engine))
448+
return ik;
435449
}
436-
return instanceKey;
450+
451+
throw new InstanceKeyNotFoundException(
452+
"Can't find instance key for: " + this.getCreation() + " using tracked instances: " + trackedInstances);
437453
}
438454

439455
Optional<SSAInvokeInstruction> getInstructionForCreation(EclipseProjectAnalysisEngine<InstanceKey> engine)

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.util.Set;
1616
import java.util.logging.Level;
1717
import java.util.logging.Logger;
18+
import java.util.stream.BaseStream;
1819
import java.util.stream.Collectors;
1920

2021
import org.eclipse.core.runtime.CoreException;
@@ -48,6 +49,8 @@ public class StreamAnalyzer extends ASTVisitor {
4849

4950
private static final Logger LOGGER = Logger.getLogger(LoggerNames.LOGGER_NAME);
5051

52+
private static final int N_FOR_STREAMS_DEFAULT = 2;
53+
5154
private static final String ENTRY_POINT_FILE = "entry_points.txt";
5255

5356
private static void addImplicitEntryPoints(Collection<Entrypoint> target, Iterable<Entrypoint> source) {
@@ -71,6 +74,11 @@ private static void addImplicitEntryPoints(Collection<Entrypoint> target, Iterab
7174

7275
private Set<Stream> streamSet = new HashSet<>();
7376

77+
/**
78+
* The N to use for instances of {@link BaseStream} in the nCFA.
79+
*/
80+
private int nForStreams = N_FOR_STREAMS_DEFAULT;
81+
7482
public StreamAnalyzer() {
7583
this(false);
7684
}
@@ -79,24 +87,42 @@ public StreamAnalyzer(boolean visitDocTags) {
7987
super(visitDocTags);
8088
}
8189

90+
public StreamAnalyzer(boolean visitDocTags, int nForStreams) {
91+
super(visitDocTags);
92+
this.nForStreams = nForStreams;
93+
}
94+
8295
public StreamAnalyzer(boolean visitDocTags, boolean findImplicitEntryPoints) {
8396
this(visitDocTags);
8497
this.findImplicitEntryPoints = findImplicitEntryPoints;
8598
}
8699

100+
public StreamAnalyzer(boolean visitDocTags, int nForStreams, boolean findImplicitEntryPoints) {
101+
this(visitDocTags, findImplicitEntryPoints);
102+
this.nForStreams = nForStreams;
103+
}
104+
87105
public StreamAnalyzer(boolean visitDocTags, boolean findImplicitEntryPoints, boolean findImplicitTestEntryPoints,
88106
boolean findImplicitBenchmarkEntryPoints) {
89107
this(visitDocTags, findImplicitEntryPoints);
90108
this.findImplicitTestEntryPoints = findImplicitTestEntryPoints;
91109
this.findImplicitBenchmarkEntryPoints = findImplicitBenchmarkEntryPoints;
92110
}
93111

112+
public StreamAnalyzer(boolean visitDocTags, int nForStreams, boolean findImplicitEntryPoints,
113+
boolean findImplicitTestEntryPoints, boolean findImplicitBenchmarkEntryPoints) {
114+
this(visitDocTags, findImplicitEntryPoints, findImplicitTestEntryPoints, findImplicitBenchmarkEntryPoints);
115+
this.nForStreams = nForStreams;
116+
}
117+
94118
/**
95119
* Analyzes this {@link StreamAnalyzer}'s streams.
96120
*
97121
* @return {@link Map} of project's analyzed along with the entry points used.
98122
*/
99123
public Map<IJavaProject, Collection<Entrypoint>> analyze() throws CoreException {
124+
LOGGER.info(() -> "Using N = " + this.getNForStreams());
125+
100126
Map<IJavaProject, Collection<Entrypoint>> ret = new HashMap<>();
101127

102128
// collect the projects to be analyzed.
@@ -108,7 +134,7 @@ public Map<IJavaProject, Collection<Entrypoint>> analyze() throws CoreException
108134
// create the analysis engine for the project.
109135
EclipseProjectAnalysisEngine<InstanceKey> engine = null;
110136
try {
111-
engine = new EclipseProjectAnalysisEngine<>(project);
137+
engine = new EclipseProjectAnalysisEngine<>(project, this.getNForStreams());
112138
engine.buildAnalysisScope();
113139
} catch (IOException e) {
114140
LOGGER.log(Level.SEVERE, "Could not create analysis engine for: " + project.getElementName(), e);
@@ -189,7 +215,8 @@ public Map<IJavaProject, Collection<Entrypoint>> analyze() throws CoreException
189215
* graph.
190216
* @return The {@link Entrypoint}s used in building the {@link CallGraph}.
191217
*/
192-
protected Collection<Entrypoint> buildCallGraph(EclipseProjectAnalysisEngine<InstanceKey> engine) throws IOException, CoreException, CallGraphBuilderCancelException, CancelException {
218+
protected Collection<Entrypoint> buildCallGraph(EclipseProjectAnalysisEngine<InstanceKey> engine)
219+
throws IOException, CoreException, CallGraphBuilderCancelException, CancelException {
193220
// if we haven't built the call graph yet.
194221
if (!this.enginesWithBuiltCallGraphsToEntrypointsUsed.keySet().contains(engine)) {
195222

@@ -353,4 +380,12 @@ public boolean visit(MethodInvocation node) {
353380

354381
return super.visit(node);
355382
}
383+
384+
public int getNForStreams() {
385+
return nForStreams;
386+
}
387+
388+
protected void setNForStreams(int nForStreams) {
389+
this.nForStreams = nForStreams;
390+
}
356391
}

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

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
import com.ibm.wala.util.strings.Atom;
8383

8484
import edu.cuny.hunter.streamrefactoring.core.safe.ModifiedBenignOracle;
85+
import edu.cuny.hunter.streamrefactoring.core.safe.NoApplicationCodeExistsInCallStringsException;
8586
import edu.cuny.hunter.streamrefactoring.core.safe.TypestateSolverFactory;
8687
import edu.cuny.hunter.streamrefactoring.core.utils.LoggerNames;
8788
import edu.cuny.hunter.streamrefactoring.core.wala.CallStringWithReceivers;
@@ -745,46 +746,26 @@ private void discoverPossibleSideEffects(EclipseProjectAnalysisEngine<InstanceKe
745746
continue;
746747

747748
CallStringWithReceivers callString = Util.getCallString(instance);
748-
CallSiteReference[] callSiteRefs = callString.getCallSiteRefs();
749-
assert callSiteRefs.length == 2 : "Expecting call sites two-deep.";
749+
assert callString.getMethods().length >= 1 : "Expecting call sites at least one-deep.";
750750

751-
// get the target of the caller.
752-
MethodReference callerDeclaredTarget = callSiteRefs[1].getDeclaredTarget();
753-
754-
// get it's IR.
755-
IMethod callerTargetMethod = engine.getClassHierarchy().resolveMethod(callerDeclaredTarget);
756-
boolean fallback = false;
757-
758-
if (callerTargetMethod == null) {
759-
LOGGER.warning("Cannot resolve caller declared target method: " + callerDeclaredTarget);
760-
761-
// fall back.
762-
callerTargetMethod = callString.getMethods()[1];
763-
LOGGER.warning("Falling back to method: " + callerTargetMethod);
764-
fallback = true;
765-
}
766-
767-
IR ir = engine.getCache().getIR(callerTargetMethod);
751+
IR ir = engine.getCache().getIR(callString.getMethods()[0]);
768752

769753
if (ir == null) {
770-
LOGGER.warning("Can't find IR for target: " + callerTargetMethod);
754+
LOGGER.warning("Can't find IR for target: " + callString.getMethods()[0]);
771755
continue; // next instance.
772756
}
773757

774758
// get calls to the caller target.
775-
// if we are falling back, use index 1, otherwise stick with index
776-
// 0.
777-
int callSiteRefsInx = fallback ? 1 : 0;
778-
SSAAbstractInvokeInstruction[] calls = ir.getCalls(callSiteRefs[callSiteRefsInx]);
759+
SSAAbstractInvokeInstruction[] calls = ir.getCalls(callString.getCallSiteRefs()[0]);
779760
assert calls.length == 1 : "Are we only expecting one call here?";
780761

781762
// I guess we're only interested in ones with a single behavioral
782763
// parameter (the first parameter is implicit).
783764
if (calls[0].getNumberOfUses() == 2) {
784765
// get the use of the first parameter.
785766
int use = calls[0].getUse(1);
786-
this.discoverLambdaSideEffects(engine, mod, Collections.singleton(instance), callerDeclaredTarget, ir,
787-
use);
767+
this.discoverLambdaSideEffects(engine, mod, Collections.singleton(instance),
768+
callString.getMethods()[0].getReference(), ir, use);
788769
}
789770
}
790771
}
@@ -866,18 +847,28 @@ private void fillInstanceToStreamMap(Set<Stream> streamSet, EclipseProjectAnalys
866847
try {
867848
instanceKey = stream.getInstanceKey(this.trackedInstances, engine);
868849
} catch (InstanceKeyNotFoundException e) {
869-
LOGGER.log(Level.WARNING, "Encountered unreachable code while processing: " + stream.getCreation(), e);
850+
LOGGER.log(Level.WARNING,
851+
"Encountered unreachable code while processing: " + stream.getCreation() + ".", e);
870852
stream.addStatusEntry(PreconditionFailure.STREAM_CODE_NOT_REACHABLE,
871853
"Either pivital code isn't reachable for stream: " + stream.getCreation()
872854
+ " or entry points are misconfigured.");
873855
++skippedStreams;
874856
continue; // next stream.
875857
} catch (UnhandledCaseException e) {
876-
String msg = "Encountered possible unhandled case (AIC #155) while processing: " + stream.getCreation();
858+
String msg = "Encountered possible unhandled case (AIC #155) while processing: " + stream.getCreation()
859+
+ ".";
877860
LOGGER.log(Level.WARNING, msg, e);
878861
stream.addStatusEntry(PreconditionFailure.CURRENTLY_NOT_HANDLED, msg);
879862
++skippedStreams;
880863
continue; // next stream.
864+
} catch (NoApplicationCodeExistsInCallStringsException e) {
865+
LOGGER.log(Level.WARNING, "Did not encounter application code in call strings while processing: "
866+
+ stream.getCreation() + ".", e);
867+
stream.addStatusEntry(PreconditionFailure.NO_APPLICATION_CODE_IN_CALL_STRINGS,
868+
"No application code in the call strings generated for stream: " + stream.getCreation()
869+
+ " was found. The maximum call string length may need to be increased.");
870+
++skippedStreams;
871+
continue; // next stream.
881872
}
882873

883874
// add the mapping.

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
import com.ibm.wala.ssa.SSAInvokeInstruction;
6161
import com.ibm.wala.ssa.SSAPhiInstruction;
6262
import com.ibm.wala.ssa.Value;
63+
import com.ibm.wala.types.ClassLoaderReference;
6364
import com.ibm.wala.types.MethodReference;
6465
import com.ibm.wala.types.TypeName;
6566
import com.ibm.wala.types.TypeReference;
@@ -568,6 +569,7 @@ else if (Modifier.isAbstract(clazz.getModifiers()))
568569
else
569570
return false;
570571
}
572+
571573

572574
public static boolean isBaseStream(IClass clazz) {
573575
return Util.isType(clazz, "java/util/stream", "BaseStream");
@@ -706,4 +708,24 @@ public static TypeReference getEvaluationType(IMethod method) {
706708
// use the return type.
707709
return method.getReturnType();
708710
}
709-
}
711+
712+
/**
713+
* Returns the index of the first {@link IMethod} in methods that is client
714+
* code.
715+
*
716+
* @param methods
717+
* The {@link IMethod}s in question.
718+
* @return The index of the first {@link IMethod} that is client code and -1 if
719+
* none found.
720+
*/
721+
public static int findIndexOfFirstClientMethod(IMethod[] methods) {
722+
for (int i = 0; i < methods.length; i++) {
723+
IMethod meth = methods[i];
724+
725+
if (meth.getDeclaringClass().getClassLoader().getReference().equals(ClassLoaderReference.Application))
726+
return i;
727+
}
728+
729+
return -1; // not found.
730+
}
731+
}

0 commit comments

Comments
 (0)