diff --git a/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/Stream.java b/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/Stream.java index 5ac3f6d9..33da8001 100644 --- a/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/Stream.java +++ b/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/Stream.java @@ -593,7 +593,7 @@ private void inferInitialOrdering() throws IOException, CoreException, ClassHier try { possibleTypes = getPossibleTypesInterprocedurally(node, valueNumber, this.getAnalysisEngine().getHeapGraph().getHeapModel(), - this.getAnalysisEngine().getPointerAnalysis(), this, LOGGER); + this.getAnalysisEngine().getPointerAnalysis(), this); // Possible types: check each one. calledMethod = (IMethod) calledMethodBinding.getJavaElement(); diff --git a/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/StreamStateMachine.java b/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/StreamStateMachine.java index 6d626c99..a4262197 100644 --- a/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/StreamStateMachine.java +++ b/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/StreamStateMachine.java @@ -56,8 +56,6 @@ import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; import com.ibm.wala.ipa.callgraph.propagation.NormalAllocationInNode; import com.ibm.wala.ipa.callgraph.propagation.PointerKey; -import com.ibm.wala.ipa.callgraph.propagation.cfa.CallStringContext; -import com.ibm.wala.ipa.callgraph.propagation.cfa.CallStringContextSelector; import com.ibm.wala.ipa.cfg.BasicBlockInContext; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.ipa.modref.ModRef; @@ -239,6 +237,7 @@ public void start() throws IOException, CoreException, CallGraphBuilderCancelExc new PropertiesManager.IPropertyDescriptor[] { WholeProgramProperties.Props.LIVE_ANALYSIS }); TypeStateOptions typeStateOptions = new TypeStateOptions(manager); typeStateOptions.setBooleanValue(WholeProgramProperties.Props.LIVE_ANALYSIS.getName(), false); + // TODO: #127 should also set entry points. TypeReference typeReference = this.getStream().getTypeReference(); IClass streamClass = engine.getClassHierarchy().lookupClass(typeReference); @@ -415,7 +414,7 @@ public void start() throws IOException, CoreException, CallGraphBuilderCancelExc // fill the instance to predecessors map. for (Iterator it = result.iterateInstances(); it.hasNext();) { InstanceKey instance = it.next(); - CallStringWithReceivers callString = getCallString(instance); + CallStringWithReceivers callString = Util.getCallString(instance); Set possibleReceivers = new HashSet<>(callString.getPossibleReceivers()); // get any additional receivers if necessary #36. @@ -526,7 +525,7 @@ private static Collection getAdditionalNecessaryReceivers Collection ret = new HashSet<>(); LOGGER.fine(() -> "Instance is: " + instance); - CallStringWithReceivers callString = getCallString(instance); + CallStringWithReceivers callString = Util.getCallString(instance); // for each method in the call string. for (IMethod calledMethod : callString.getMethods()) { @@ -550,7 +549,7 @@ private static Collection getAdditionalNecessaryReceivers LOGGER.fine(() -> "Found node: " + node); // try to get its CallStringWithReceivers. - CallStringWithReceivers calledMethodCallString = getCallString(node); + CallStringWithReceivers calledMethodCallString = Util.getCallString(node); // what are its receivers? Set possibleReceivers = calledMethodCallString.getPossibleReceivers(); @@ -602,7 +601,7 @@ private static void discoverPossibleStatefulIntermediateOperations(AggregateSolv if (!isStreamCreatedFromIntermediateOperation(instance, hierarchy, callGraph)) continue; - CallStringWithReceivers callString = getCallString(instance); + CallStringWithReceivers callString = Util.getCallString(instance); boolean found = false; for (CallSiteReference callSiteReference : callString.getCallSiteRefs()) { @@ -645,7 +644,7 @@ private void discoverIfReduceOrderingPossiblyMatters( possibleReturnTypes = Util.getPossibleTypesInterprocedurally(block.getNode(), returnValue, this.getStream().getAnalysisEngine().getHeapGraph().getHeapModel(), - this.getStream().getAnalysisEngine().getPointerAnalysis(), this.getStream(), LOGGER); + this.getStream().getAnalysisEngine().getPointerAnalysis(), this.getStream()); LOGGER.info("Possible reduce types are: " + possibleReturnTypes); } else { @@ -864,7 +863,7 @@ private void discoverPossibleSideEffects(AggregateSolverResult result, engine.getCallGraph())) continue; - CallStringWithReceivers callString = getCallString(instance); + CallStringWithReceivers callString = Util.getCallString(instance); CallSiteReference[] callSiteRefs = callString.getCallSiteRefs(); assert callSiteRefs.length == 2 : "Expecting call sites two-deep."; @@ -1026,7 +1025,7 @@ private static boolean filterPointerKey(PointerKey pointerKey, EclipseProjectAna private static boolean isStreamCreatedFromIntermediateOperation(InstanceKey instance, IClassHierarchy hierarchy, CallGraph callGraph) throws IOException, CoreException { // Get the immediate possible receivers of the stream instance. - Set receivers = getCallString(instance).getPossibleReceivers(); + Set receivers = Util.getCallString(instance).getPossibleReceivers(); // Get any additional receivers we need to consider. Collection additionalReceivers = getAdditionalNecessaryReceiversFromPredecessors( @@ -1045,23 +1044,6 @@ private static boolean isStreamCreatedFromIntermediateOperation(InstanceKey inst }); } - private static CallStringWithReceivers getCallString(InstanceKey instance) { - NormalAllocationInNode allocationInNode = (NormalAllocationInNode) instance; - return getCallString(allocationInNode); - } - - private static CallStringWithReceivers getCallString(NormalAllocationInNode allocationInNode) { - CGNode node = allocationInNode.getNode(); - return getCallString(node); - } - - private static CallStringWithReceivers getCallString(CGNode node) { - CallStringContext context = (CallStringContext) node.getContext(); - CallStringWithReceivers callString = (CallStringWithReceivers) context - .get(CallStringContextSelector.CALL_STRING); - return callString; - } - /** * The typestate rules to use. */ diff --git a/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/Util.java b/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/Util.java index 2f4c7c67..a3358ad5 100644 --- a/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/Util.java +++ b/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/analysis/Util.java @@ -36,8 +36,11 @@ import com.ibm.wala.ipa.callgraph.impl.DefaultEntrypoint; import com.ibm.wala.ipa.callgraph.propagation.HeapModel; import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; +import com.ibm.wala.ipa.callgraph.propagation.NormalAllocationInNode; import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis; import com.ibm.wala.ipa.callgraph.propagation.PointerKey; +import com.ibm.wala.ipa.callgraph.propagation.cfa.CallStringContext; +import com.ibm.wala.ipa.callgraph.propagation.cfa.CallStringContextSelector; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.shrikeCT.InvalidClassFileException; import com.ibm.wala.ssa.IR; @@ -56,6 +59,7 @@ import edu.cuny.hunter.streamrefactoring.core.utils.LoggerNames; import edu.cuny.hunter.streamrefactoring.core.wala.AnalysisUtils; +import edu.cuny.hunter.streamrefactoring.core.wala.CallStringWithReceivers; public final class Util { @@ -197,22 +201,22 @@ static String getBinaryName(TypeReference typeReference) { } public static Collection getPossibleTypesInterprocedurally(CGNode node, int valueNumber, - HeapModel heapModel, PointerAnalysis pointerAnalysis, Stream stream, Logger logger) + HeapModel heapModel, PointerAnalysis pointerAnalysis, Stream stream) throws NoniterableException, NoninstantiableException, CannotExtractSpliteratorException { Collection ret = new HashSet<>(); PointerKey valueKey = heapModel.getPointerKeyForLocal(node, valueNumber); - logger.fine(() -> "Value pointer key is: " + valueKey); + LOGGER.fine(() -> "Value pointer key is: " + valueKey); OrdinalSet pointsToSet = pointerAnalysis.getPointsToSet(valueKey); assert pointsToSet != null; - logger.fine(() -> "PointsTo set is: " + pointsToSet); + LOGGER.fine(() -> "PointsTo set is: " + pointsToSet); for (InstanceKey instanceKey : pointsToSet) { IClass concreteClass = instanceKey.getConcreteType(); if (!(concreteClass instanceof SyntheticClass)) { - logger.fine(() -> "Found non-synthetic concrete type: " + concreteClass); + LOGGER.fine(() -> "Found non-synthetic concrete type: " + concreteClass); // Workaround #38, problem seemingly with generics. // Due to type erasure, we may have the problem if the return @@ -409,7 +413,7 @@ private static boolean matches(TypeReference methodDeclaringType, MethodReferenc public static JDTIdentityMapper getJDTIdentifyMapper(ASTNode node) { return new JDTIdentityMapper(JavaSourceAnalysisScope.SOURCE, node.getAST()); } - + /** * check whether the annotation is "EntryPoint" */ @@ -421,9 +425,10 @@ private static boolean isEntryPointClass(TypeName typeName) { * Find all annotations in test cases and check whether they are "entry point". * If yes, call DefaultEntrypoint to get entry point, then, add it into the * result set. - * @throws InvalidClassFileException + * + * @throws InvalidClassFileException */ - public static Set findEntryPoints(IClassHierarchy classHierarchy) throws InvalidClassFileException { + public static Set findEntryPoints(IClassHierarchy classHierarchy) throws InvalidClassFileException { final Set result = new HashSet<>(); Iterator classIterator = classHierarchy.iterator(); while (classIterator.hasNext()) { @@ -433,7 +438,7 @@ public static Set findEntryPoints(IClassHierarchy classHierarchy) th // iterate over all declared methods for (com.ibm.wala.classLoader.IMethod method : klass.getDeclaredMethods()) { - // if method has an annotation + // if method has an annotation if (!(method instanceof ShrikeCTMethod)) { throw new IllegalArgumentException("@EntryPoint only works for byte code."); } @@ -448,7 +453,24 @@ public static Set findEntryPoints(IClassHierarchy classHierarchy) th } } } - + return result; } + + public static CallStringWithReceivers getCallString(InstanceKey instance) { + NormalAllocationInNode allocationInNode = (NormalAllocationInNode) instance; + return getCallString(allocationInNode); + } + + public static CallStringWithReceivers getCallString(NormalAllocationInNode allocationInNode) { + CGNode node = allocationInNode.getNode(); + return getCallString(node); + } + + public static CallStringWithReceivers getCallString(CGNode node) { + CallStringContext context = (CallStringContext) node.getContext(); + CallStringWithReceivers callString = (CallStringWithReceivers) context + .get(CallStringContextSelector.CALL_STRING); + return callString; + } } diff --git a/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/safe/ClientSlicingUniqueSolver.java b/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/safe/ClientSlicingUniqueSolver.java new file mode 100644 index 00000000..4584c17a --- /dev/null +++ b/edu.cuny.hunter.streamrefactoring.core/src/edu/cuny/hunter/streamrefactoring/core/safe/ClientSlicingUniqueSolver.java @@ -0,0 +1,73 @@ +package edu.cuny.hunter.streamrefactoring.core.safe; + +import java.util.Collection; +import java.util.Iterator; +import java.util.logging.Logger; + +import com.ibm.safe.internal.exceptions.PropertiesException; +import com.ibm.safe.reporting.IReporter; +import com.ibm.safe.typestate.core.BenignOracle; +import com.ibm.safe.typestate.merge.IMergeFunctionFactory; +import com.ibm.safe.typestate.metrics.TypeStateMetrics; +import com.ibm.safe.typestate.mine.TraceReporter; +import com.ibm.safe.typestate.options.TypeStateOptions; +import com.ibm.safe.typestate.rules.ITypeStateDFA; +import com.ibm.safe.typestate.unique.UniqueSolver; +import com.ibm.wala.classLoader.CallSiteReference; +import com.ibm.wala.classLoader.IClass; +import com.ibm.wala.classLoader.IMethod; +import com.ibm.wala.escape.ILiveObjectAnalysis; +import com.ibm.wala.ipa.callgraph.CallGraph; +import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; +import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis; +import com.ibm.wala.types.MethodReference; +import com.ibm.wala.types.TypeName; +import com.ibm.wala.types.TypeReference; +import com.ibm.wala.util.strings.Atom; + +import edu.cuny.hunter.streamrefactoring.core.analysis.Util; +import edu.cuny.hunter.streamrefactoring.core.utils.LoggerNames; +import edu.cuny.hunter.streamrefactoring.core.wala.CallStringWithReceivers; + +/** + * A solver that only tracks instances created from client (non-JDK) calls. + * + * @author Raffi Khatchadourian + */ +public class ClientSlicingUniqueSolver extends UniqueSolver { + + private static final Logger LOGGER = Logger.getLogger(LoggerNames.LOGGER_NAME); + + public ClientSlicingUniqueSolver(CallGraph cg, PointerAnalysis pointerAnalysis, ITypeStateDFA dfa, + TypeStateOptions options, ILiveObjectAnalysis live, BenignOracle ora, TypeStateMetrics metrics, + IReporter reporter, TraceReporter traceReporter, IMergeFunctionFactory mergeFactory) { + super(cg, pointerAnalysis, dfa, options, live, ora, metrics, reporter, traceReporter, mergeFactory); + } + + @Override + protected Collection computeTrackedInstances() throws PropertiesException { + Collection instances = super.computeTrackedInstances(); + + for (Iterator iterator = instances.iterator(); iterator.hasNext();) { + InstanceKey instanceKey = iterator.next(); + CallStringWithReceivers callString = Util.getCallString(instanceKey); + IMethod[] callingMethods = callString.getMethods(); + IMethod outerMostCallingMethod = callingMethods[1]; + MethodReference reference = outerMostCallingMethod.getReference(); + TypeReference declaringClass = reference.getDeclaringClass(); + TypeName name = declaringClass.getName(); + Atom classPackage = name.getPackage(); + boolean isFromAPI = classPackage.startsWith(Atom.findOrCreateAsciiAtom("java")); + + // if it's being called from the API. + if (isFromAPI) { + // remove it. + LOGGER.info(() -> "Removing instance: " + instanceKey); + iterator.remove(); + } + } + + return instances; + } + +} diff --git a/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI7/in/A.java b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI7/in/A.java new file mode 100644 index 00000000..d30ce3c2 --- /dev/null +++ b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI7/in/A.java @@ -0,0 +1,14 @@ +package p; + +import java.util.HashSet; +import java.util.stream.*; + +import edu.cuny.hunter.streamrefactoring.annotations.*; + +class A { + + @EntryPoint + void n() { + new HashSet<>().stream().sorted().distinct().count(); + } +} \ No newline at end of file diff --git a/edu.cuny.hunter.streamrefactoring.tests/test cases/edu/cuny/hunter/streamrefactoring/ui/tests/ConvertStreamToParallelRefactoringTest.java b/edu.cuny.hunter.streamrefactoring.tests/test cases/edu/cuny/hunter/streamrefactoring/ui/tests/ConvertStreamToParallelRefactoringTest.java index 29a9b912..ecc2426f 100644 --- a/edu.cuny.hunter.streamrefactoring.tests/test cases/edu/cuny/hunter/streamrefactoring/ui/tests/ConvertStreamToParallelRefactoringTest.java +++ b/edu.cuny.hunter.streamrefactoring.tests/test cases/edu/cuny/hunter/streamrefactoring/ui/tests/ConvertStreamToParallelRefactoringTest.java @@ -59,7 +59,8 @@ public class ConvertStreamToParallelRefactoringTest extends RefactoringTest { /** - * The name of the directory containing resources under the project directory. + * The name of the directory containing resources under the project + * directory. */ private static final String RESOURCE_PATH = "resources"; @@ -223,9 +224,10 @@ private Path getAbsolutionPath(String fileName) { * * @see * org.eclipse.jdt.ui.tests.refactoring.RefactoringTest#getFileContents(java - * .lang.String) Had to override this method because, since this plug-in is a - * fragment (at least I think that this is the reason), it doesn't have an - * activator and the bundle is resolving to the eclipse refactoring test bundle. + * .lang.String) Had to override this method because, since this plug-in is + * a fragment (at least I think that this is the reason), it doesn't have an + * activator and the bundle is resolving to the eclipse refactoring test + * bundle. */ @Override public String getFileContents(String fileName) throws IOException { @@ -452,8 +454,7 @@ public void testHashSetParallelStream2() throws Exception { public void testNonInternalAPI() throws Exception { helper(new StreamAnalysisExpectedResult("new HashSet<>().parallelStream()", Collections.singleton(ExecutionMode.PARALLEL), Collections.singleton(Ordering.UNORDERED), false, false, - false, null, null, null, RefactoringStatus.ERROR, - EnumSet.of(PreconditionFailure.UNORDERED))); + false, null, null, null, RefactoringStatus.ERROR, EnumSet.of(PreconditionFailure.UNORDERED))); } public void testNonInternalAPI2() throws Exception { @@ -462,14 +463,14 @@ public void testNonInternalAPI2() throws Exception { false, Collections.singleton(TransformationAction.CONVERT_TO_PARALLEL), PreconditionSuccess.P1, Refactoring.CONVERT_SEQUENTIAL_STREAM_TO_PARALLEL, RefactoringStatus.OK, Collections.emptySet())); } - + public void testNonInternalAPI3() throws Exception { helper(new StreamAnalysisExpectedResult("new HashSet<>().stream()", - Collections.singleton(ExecutionMode.SEQUENTIAL), Collections.singleton(Ordering.UNORDERED), false, true, + Collections.singleton(ExecutionMode.SEQUENTIAL), Collections.singleton(Ordering.UNORDERED), false, true, false, Collections.singleton(TransformationAction.CONVERT_TO_PARALLEL), PreconditionSuccess.P1, Refactoring.CONVERT_SEQUENTIAL_STREAM_TO_PARALLEL, RefactoringStatus.OK, Collections.emptySet())); } - + /** * related to #126 */ @@ -482,19 +483,19 @@ public void testNonInternalAPI4() throws Exception { Collections.singleton(ExecutionMode.SEQUENTIAL), orderings, false, true, false, null, null, null, RefactoringStatus.ERROR, EnumSet.of(PreconditionFailure.INCONSISTENT_POSSIBLE_ORDERINGS))); } - + /** * related to #126 */ public void testNonInternalAPI5() throws Exception { HashSet executionModes = new HashSet<>(); - executionModes .add(ExecutionMode.PARALLEL); - executionModes .add(ExecutionMode.SEQUENTIAL); + executionModes.add(ExecutionMode.PARALLEL); + executionModes.add(ExecutionMode.SEQUENTIAL); helper(new StreamAnalysisExpectedResult("new HashSet<>().stream()", executionModes, Collections.singleton(Ordering.UNORDERED), false, true, false, null, null, null, RefactoringStatus.ERROR, EnumSet.of(PreconditionFailure.INCONSISTENT_POSSIBLE_EXECUTION_MODES))); } - + /** * related to #126 */ @@ -506,7 +507,18 @@ public void testNonInternalAPI6() throws Exception { Collections.singleton(Ordering.UNORDERED), false, true, false, null, null, null, RefactoringStatus.ERROR, EnumSet.of(PreconditionFailure.INCONSISTENT_POSSIBLE_EXECUTION_MODES))); } - + + /** + * This is a control to testNonInternalAPI4. + */ + public void testNonInternalAPI7() throws Exception { + helper(new StreamAnalysisExpectedResult("new HashSet<>().stream()", + Collections.singleton(ExecutionMode.SEQUENTIAL), EnumSet.of(Ordering.ORDERED), false, true, false, + EnumSet.of(TransformationAction.UNORDER, TransformationAction.CONVERT_TO_PARALLEL), + PreconditionSuccess.P3, Refactoring.CONVERT_SEQUENTIAL_STREAM_TO_PARALLEL, RefactoringStatus.OK, + Collections.emptySet())); + } + public void testCollectionFromParameter() throws Exception { helper(new StreamAnalysisExpectedResult("h.parallelStream()", Collections.singleton(ExecutionMode.PARALLEL), Collections.singleton(Ordering.UNORDERED), false, true, false, null, null, null,