From 8a8fdb45448b202cb66cdb40d497754c8355b6f4 Mon Sep 17 00:00:00 2001 From: Yiming Date: Thu, 30 Nov 2017 18:13:12 -0500 Subject: [PATCH 1/9] test cases for #103 --- .../testNonInternalAPI2/in/A.java | 20 ++++++++++++++++++ .../testNonInternalAPI3/in/A.java | 20 ++++++++++++++++++ .../testNonInternalAPI4/in/A.java | 20 ++++++++++++++++++ ...onvertStreamToParallelRefactoringTest.java | 21 +++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI2/in/A.java create mode 100644 edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI3/in/A.java create mode 100644 edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI4/in/A.java diff --git a/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI2/in/A.java b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI2/in/A.java new file mode 100644 index 00000000..12473904 --- /dev/null +++ b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI2/in/A.java @@ -0,0 +1,20 @@ +package p; + +import java.util.HashSet; +import java.util.stream.*; + +import edu.cuny.hunter.streamrefactoring.annotations.*; + +class A { + + Stream m() { + Stream stream = new HashSet<>().stream().distinct(); + return stream; + } + + @EntryPoint + void n() { + Stream s = m(); + s.count(); + } +} \ No newline at end of file diff --git a/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI3/in/A.java b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI3/in/A.java new file mode 100644 index 00000000..0cf855b9 --- /dev/null +++ b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI3/in/A.java @@ -0,0 +1,20 @@ +package p; + +import java.util.HashSet; +import java.util.stream.*; + +import edu.cuny.hunter.streamrefactoring.annotations.*; + +class A { + + Stream m() { + Stream stream = new HashSet<>().stream(); + return stream; + } + + @EntryPoint + void n() { + Stream s = m(); + s.distinct().count(); + } +} \ No newline at end of file diff --git a/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI4/in/A.java b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI4/in/A.java new file mode 100644 index 00000000..ef289b6f --- /dev/null +++ b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI4/in/A.java @@ -0,0 +1,20 @@ +package p; + +import java.util.HashSet; +import java.util.stream.*; + +import edu.cuny.hunter.streamrefactoring.annotations.*; + +class A { + + Stream m() { + Stream stream = new HashSet<>().stream().sorted(); + return stream; + } + + @EntryPoint + void n() { + Stream s = m(); + s.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 37c5511d..732b3b76 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 @@ -457,6 +457,27 @@ public void testNonInternalAPI() throws Exception { EnumSet.of(PreconditionFailure.NO_TERMINAL_OPERATIONS))); } + public void testNonInternalAPI2() throws Exception { + helper(new StreamAnalysisExpectedResult("new HashSet<>().stream()", + Collections.singleton(ExecutionMode.SEQUENTIAL), Collections.singleton(Ordering.UNORDERED), false, false, + false, null, null, null, RefactoringStatus.ERROR, + EnumSet.of(PreconditionFailure.NO_TERMINAL_OPERATIONS))); + } + + public void testNonInternalAPI3() throws Exception { + helper(new StreamAnalysisExpectedResult("new HashSet<>().stream()", + Collections.singleton(ExecutionMode.SEQUENTIAL), Collections.singleton(Ordering.UNORDERED), false, false, + false, null, null, null, RefactoringStatus.ERROR, + EnumSet.of(PreconditionFailure.NO_TERMINAL_OPERATIONS))); + } + + public void testNonInternalAPI4() throws Exception { + helper(new StreamAnalysisExpectedResult("new HashSet<>().stream()", + Collections.singleton(ExecutionMode.SEQUENTIAL), Collections.singleton(Ordering.UNORDERED), false, false, + false, null, null, null, RefactoringStatus.ERROR, + EnumSet.of(PreconditionFailure.NO_TERMINAL_OPERATIONS))); + } + public void testCollectionFromParameter() throws Exception { helper(new StreamAnalysisExpectedResult("h.parallelStream()", Collections.singleton(ExecutionMode.PARALLEL), Collections.singleton(Ordering.UNORDERED), false, true, false, null, null, null, From 36e9ed176d17081596843dc4728e6479643d75c0 Mon Sep 17 00:00:00 2001 From: Yiming Date: Fri, 1 Dec 2017 11:26:15 -0500 Subject: [PATCH 2/9] add on more test case #103 --- .../testNonInternalAPI5/in/A.java | 20 +++++++++++++++++++ ...onvertStreamToParallelRefactoringTest.java | 9 ++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI5/in/A.java diff --git a/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI5/in/A.java b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI5/in/A.java new file mode 100644 index 00000000..a4dff85b --- /dev/null +++ b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI5/in/A.java @@ -0,0 +1,20 @@ +package p; + +import java.util.HashSet; +import java.util.stream.*; + +import edu.cuny.hunter.streamrefactoring.annotations.*; + +class A { + + Stream m() { + Stream stream = new HashSet<>().parallelStream().sorted(); + return stream; + } + + @EntryPoint + void n() { + Stream s = m(); + s.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 732b3b76..87b5c92b 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 @@ -477,7 +477,14 @@ public void testNonInternalAPI4() throws Exception { false, null, null, null, RefactoringStatus.ERROR, EnumSet.of(PreconditionFailure.NO_TERMINAL_OPERATIONS))); } - + + public void testNonInternalAPI5() 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.NO_TERMINAL_OPERATIONS))); + } + public void testCollectionFromParameter() throws Exception { helper(new StreamAnalysisExpectedResult("h.parallelStream()", Collections.singleton(ExecutionMode.PARALLEL), Collections.singleton(Ordering.UNORDERED), false, true, false, null, null, null, From fa7ddbb56566a1d357fa0110941c6004faffb7f5 Mon Sep 17 00:00:00 2001 From: Yiming Date: Mon, 4 Dec 2017 02:48:15 -0500 Subject: [PATCH 3/9] fix #103 --- .../core/analysis/Stream.java | 19 ++ .../core/analysis/StreamStateMachine.java | 215 +++++++++--------- .../testNonInternalAPI5/in/A.java | 2 +- ...onvertStreamToParallelRefactoringTest.java | 34 +-- 4 files changed, 148 insertions(+), 122 deletions(-) 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 1bbd5ba6..91788b9a 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 @@ -56,6 +56,7 @@ import com.ibm.wala.ipa.callgraph.impl.FakeRootMethod; import com.ibm.wala.ipa.callgraph.impl.FakeWorldClinitMethod; import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; +import com.ibm.wala.ipa.callgraph.propagation.cfa.CallStringContext; import com.ibm.wala.ipa.cha.ClassHierarchyException; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.shrikeCT.InvalidClassFileException; @@ -636,6 +637,11 @@ private void inferInitialOrdering() */ protected CGNode getEnclosingMethodNode() throws IOException, CoreException, NoEnclosingMethodNodeFoundException { MethodReference methodReference = this.getEnclosingMethodReference(); + return getEnclosingMethodNode(methodReference); + } + + protected CGNode getEnclosingMethodNode(MethodReference methodReference) + throws IOException, CoreException, NoEnclosingMethodNodeFoundException { Set nodes = this.getAnalysisEngine().getCallGraph().getNodes(methodReference); if (nodes.isEmpty()) @@ -644,6 +650,19 @@ protected CGNode getEnclosingMethodNode() throws IOException, CoreException, NoE return nodes.iterator().next(); // just return the first. } + protected HashSet getEnclosingMethodNodes() + throws IOException, CoreException, NoEnclosingMethodNodeFoundException { + HashSet cgNodes = new HashSet<>(); + com.ibm.wala.classLoader.IMethod[] methods = ((CallStringContext) (getEnclosingMethodNode().getContext())) + .getCallString().getMethods(); + + for (int i = 0; i < methods.length - 1; ++i) { + cgNodes.add(getEnclosingMethodNode(methods[i].getReference())); + } + cgNodes.add(this.getEnclosingMethodNode()); + return cgNodes; + } + /** * Returns true iff all of the predecessors of all of the given {@link CGNode}s * in the {@link CallGraph} are {@link FakeRootMethod}s. 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 fdf1321e..596d1311 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 @@ -22,7 +22,6 @@ import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.jdt.core.dom.MethodInvocation; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; @@ -281,114 +280,116 @@ public void start() throws IOException, CoreException, CallGraphBuilderCancelExc // the node of where the stream was declared: TODO: Can this be // somehow rewritten to get blocks corresponding to terminal // operations? - CGNode cgNode = this.getStream().getEnclosingMethodNode(); - - for (Iterator callSites = cgNode.iterateCallSites(); callSites.hasNext();) { - CallSiteReference callSiteReference = callSites.next(); - MethodReference calledMethod = callSiteReference.getDeclaredTarget(); - - // is it a terminal operation? TODO: Should this be - // cached somehow? Collection of all terminal operation - // invocations? - if (isTerminalOperation(calledMethod)) { - // get the basic block for the call. - ISSABasicBlock[] blocksForCall = cgNode.getIR().getBasicBlocksForCall(callSiteReference); - - assert blocksForCall.length == 1 : "Expecting only a single basic block for the call: " - + callSiteReference; - - for (int i = 0; i < blocksForCall.length; i++) { - ISSABasicBlock block = blocksForCall[i]; - - BasicBlockInContext blockInContext = getBasicBlockInContextForBlock( - block, cgNode, supergraph) - .orElseThrow(() -> new IllegalStateException( - "No basic block in context for block: " + block)); - - if (!terminalBlockToPossibleReceivers.containsKey(blockInContext)) { - // associate possible receivers with the - // blockInContext. - // search through each instruction in the - // block. - int processedInstructions = 0; - for (Iterator it = block.iterator(); it.hasNext();) { - SSAInstruction instruction = it.next(); - - // if it's a phi instruction. - if (instruction instanceof SSAPhiInstruction) - // skip it. The pointer analysis - // below will handle it. - continue; - - // Get the possible receivers. This - // number corresponds to the value - // number of the receiver of the method. - int valueNumberForReceiver = instruction.getUse(0); - - // it should be represented by a pointer - // key. - PointerKey pointerKey = engine.getHeapGraph().getHeapModel() - .getPointerKeyForLocal(cgNode, valueNumberForReceiver); - - // get the points to set for the - // receiver. This will give us all - // object instances that the receiver - // reference points to. - OrdinalSet pointsToSet = engine.getPointerAnalysis() - .getPointsToSet(pointerKey); - assert pointsToSet != null : "The points-to set (I think) should not be null for pointer: " - + pointerKey; - - OrdinalSet previousReceivers = terminalBlockToPossibleReceivers - .put(blockInContext, pointsToSet); - assert previousReceivers == null : "Reassociating a blockInContext: " - + blockInContext + " with a new points-to set: " + pointsToSet - + " that was originally: " + previousReceivers; - - ++processedInstructions; + // CGNode cgNode = this.getStream().getEnclosingMethodNode(); + for (CGNode cgNode : this.getStream().getEnclosingMethodNodes()) { + for (Iterator callSites = cgNode.iterateCallSites(); callSites.hasNext();) { + CallSiteReference callSiteReference = callSites.next(); + MethodReference calledMethod = callSiteReference.getDeclaredTarget(); + + // is it a terminal operation? TODO: Should this be + // cached somehow? Collection of all terminal operation + // invocations? + if (isTerminalOperation(calledMethod)) { + // get the basic block for the call. + + ISSABasicBlock[] blocksForCall = cgNode.getIR().getBasicBlocksForCall(callSiteReference); + + assert blocksForCall.length == 1 : "Expecting only a single basic block for the call: " + + callSiteReference; + + for (int i = 0; i < blocksForCall.length; i++) { + ISSABasicBlock block = blocksForCall[i]; + + BasicBlockInContext blockInContext = getBasicBlockInContextForBlock( + block, cgNode, supergraph) + .orElseThrow(() -> new IllegalStateException( + "No basic block in context for block: " + block)); + + if (!terminalBlockToPossibleReceivers.containsKey(blockInContext)) { + // associate possible receivers with the + // blockInContext. + // search through each instruction in the + // block. + int processedInstructions = 0; + for (Iterator it = block.iterator(); it.hasNext();) { + SSAInstruction instruction = it.next(); + + // if it's a phi instruction. + if (instruction instanceof SSAPhiInstruction) + // skip it. The pointer analysis + // below will handle it. + continue; + + // Get the possible receivers. This + // number corresponds to the value + // number of the receiver of the method. + int valueNumberForReceiver = instruction.getUse(0); + + // it should be represented by a pointer + // key. + PointerKey pointerKey = engine.getHeapGraph().getHeapModel() + .getPointerKeyForLocal(cgNode, valueNumberForReceiver); + + // get the points to set for the + // receiver. This will give us all + // object instances that the receiver + // reference points to. + OrdinalSet pointsToSet = engine.getPointerAnalysis() + .getPointsToSet(pointerKey); + assert pointsToSet != null : "The points-to set (I think) should not be null for pointer: " + + pointerKey; + + OrdinalSet previousReceivers = terminalBlockToPossibleReceivers + .put(blockInContext, pointsToSet); + assert previousReceivers == null : "Reassociating a blockInContext: " + + blockInContext + " with a new points-to set: " + pointsToSet + + " that was originally: " + previousReceivers; + + ++processedInstructions; + } + + assert processedInstructions == 1 : "Expecting to process one and only one instruction here."; } - assert processedInstructions == 1 : "Expecting to process one and only one instruction here."; - } - - IntSet intSet = instanceResult.getResult().getResult(blockInContext); - for (IntIterator it = intSet.intIterator(); it.hasNext();) { - int nextInt = it.next(); - - // retrieve the state set for this instance - // and block. - Map> ruleToStates = instanceBlockStateTable - .get(instanceKey, blockInContext); - - // if it doesn't yet exist. - if (ruleToStates == null) { - // allocate a new rule map. - ruleToStates = new HashMap<>(); - - // place it in the table. - instanceBlockStateTable.put(instanceKey, blockInContext, ruleToStates); - } - - Set stateSet = ruleToStates.get(rule); - - // if it does not yet exist. - if (stateSet == null) { - // allocate a new set. - stateSet = new HashSet<>(); - - // place it in the map. - ruleToStates.put(rule, stateSet); - } - - // get the facts. - Factoid factoid = instanceResult.getDomain().getMappedObject(nextInt); - if (factoid != DUMMY_ZERO) { - BaseFactoid baseFactoid = (BaseFactoid) factoid; - assert baseFactoid.instance.equals( - instanceKey) : "Sanity check that the fact instance should be the same as the instance being examined."; - - // add the encountered state to the set. - stateSet.add(baseFactoid.state); + IntSet intSet = instanceResult.getResult().getResult(blockInContext); + for (IntIterator it = intSet.intIterator(); it.hasNext();) { + int nextInt = it.next(); + + // retrieve the state set for this instance + // and block. + Map> ruleToStates = instanceBlockStateTable + .get(instanceKey, blockInContext); + + // if it doesn't yet exist. + if (ruleToStates == null) { + // allocate a new rule map. + ruleToStates = new HashMap<>(); + + // place it in the table. + instanceBlockStateTable.put(instanceKey, blockInContext, ruleToStates); + } + + Set stateSet = ruleToStates.get(rule); + + // if it does not yet exist. + if (stateSet == null) { + // allocate a new set. + stateSet = new HashSet<>(); + + // place it in the map. + ruleToStates.put(rule, stateSet); + } + + // get the facts. + Factoid factoid = instanceResult.getDomain().getMappedObject(nextInt); + if (factoid != DUMMY_ZERO) { + BaseFactoid baseFactoid = (BaseFactoid) factoid; + assert baseFactoid.instance.equals( + instanceKey) : "Sanity check that the fact instance should be the same as the instance being examined."; + + // add the encountered state to the set. + stateSet.add(baseFactoid.state); + } } } } diff --git a/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI5/in/A.java b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI5/in/A.java index a4dff85b..ca5fc61b 100644 --- a/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI5/in/A.java +++ b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI5/in/A.java @@ -8,7 +8,7 @@ class A { Stream m() { - Stream stream = new HashSet<>().parallelStream().sorted(); + Stream stream = new HashSet<>().stream().parallel(); return stream; } 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 87b5c92b..3f897554 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 @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -454,35 +455,40 @@ 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.NO_TERMINAL_OPERATIONS))); + EnumSet.of(PreconditionFailure.UNORDERED))); } public void testNonInternalAPI2() throws Exception { helper(new StreamAnalysisExpectedResult("new HashSet<>().stream()", - Collections.singleton(ExecutionMode.SEQUENTIAL), Collections.singleton(Ordering.UNORDERED), false, false, - false, null, null, null, RefactoringStatus.ERROR, - EnumSet.of(PreconditionFailure.NO_TERMINAL_OPERATIONS))); + 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())); } public void testNonInternalAPI3() throws Exception { helper(new StreamAnalysisExpectedResult("new HashSet<>().stream()", - Collections.singleton(ExecutionMode.SEQUENTIAL), Collections.singleton(Ordering.UNORDERED), false, false, - false, null, null, null, RefactoringStatus.ERROR, - EnumSet.of(PreconditionFailure.NO_TERMINAL_OPERATIONS))); + 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())); } public void testNonInternalAPI4() throws Exception { + HashSet orderings = new HashSet<>(); + orderings.add(Ordering.UNORDERED); + orderings.add(Ordering.ORDERED); + helper(new StreamAnalysisExpectedResult("new HashSet<>().stream()", - Collections.singleton(ExecutionMode.SEQUENTIAL), Collections.singleton(Ordering.UNORDERED), false, false, - false, null, null, null, RefactoringStatus.ERROR, - EnumSet.of(PreconditionFailure.NO_TERMINAL_OPERATIONS))); + Collections.singleton(ExecutionMode.SEQUENTIAL), orderings, false, true, false, null, null, null, + RefactoringStatus.ERROR, EnumSet.of(PreconditionFailure.INCONSISTENT_POSSIBLE_ORDERINGS))); } public void testNonInternalAPI5() 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.NO_TERMINAL_OPERATIONS))); + HashSet executionModes = new HashSet<>(); + 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))); } public void testCollectionFromParameter() throws Exception { From 55f2f71590bc8ef9e46f7687d38068cccc732225 Mon Sep 17 00:00:00 2001 From: Yiming Date: Wed, 6 Dec 2017 03:11:15 -0500 Subject: [PATCH 4/9] #103 improve --- .../core/analysis/Stream.java | 23 ++++++++++--------- .../core/analysis/StreamStateMachine.java | 1 - 2 files changed, 12 insertions(+), 12 deletions(-) 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 91788b9a..932707d4 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 @@ -637,11 +637,6 @@ private void inferInitialOrdering() */ protected CGNode getEnclosingMethodNode() throws IOException, CoreException, NoEnclosingMethodNodeFoundException { MethodReference methodReference = this.getEnclosingMethodReference(); - return getEnclosingMethodNode(methodReference); - } - - protected CGNode getEnclosingMethodNode(MethodReference methodReference) - throws IOException, CoreException, NoEnclosingMethodNodeFoundException { Set nodes = this.getAnalysisEngine().getCallGraph().getNodes(methodReference); if (nodes.isEmpty()) @@ -649,17 +644,23 @@ protected CGNode getEnclosingMethodNode(MethodReference methodReference) else return nodes.iterator().next(); // just return the first. } - + + /** + * add all CGNodes in call graph to a hash set + * @return a hash set of CGNode + * @throws IOException + * @throws CoreException + * @throws NoEnclosingMethodNodeFoundException + */ protected HashSet getEnclosingMethodNodes() throws IOException, CoreException, NoEnclosingMethodNodeFoundException { HashSet cgNodes = new HashSet<>(); - com.ibm.wala.classLoader.IMethod[] methods = ((CallStringContext) (getEnclosingMethodNode().getContext())) - .getCallString().getMethods(); - for (int i = 0; i < methods.length - 1; ++i) { - cgNodes.add(getEnclosingMethodNode(methods[i].getReference())); + Iterator cgNodeIterator = this.getAnalysisEngine().getCallGraph().iterator(); + for (; cgNodeIterator.hasNext();) { + cgNodes.add(cgNodeIterator.next()); } - cgNodes.add(this.getEnclosingMethodNode()); + return cgNodes; } 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 596d1311..1ceb36b7 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 @@ -280,7 +280,6 @@ public void start() throws IOException, CoreException, CallGraphBuilderCancelExc // the node of where the stream was declared: TODO: Can this be // somehow rewritten to get blocks corresponding to terminal // operations? - // CGNode cgNode = this.getStream().getEnclosingMethodNode(); for (CGNode cgNode : this.getStream().getEnclosingMethodNodes()) { for (Iterator callSites = cgNode.iterateCallSites(); callSites.hasNext();) { CallSiteReference callSiteReference = callSites.next(); From 344353762c98161a574718b832b53c0b158f1bef Mon Sep 17 00:00:00 2001 From: Yiming Date: Wed, 6 Dec 2017 03:13:13 -0500 Subject: [PATCH 5/9] delete useless import --- .../edu/cuny/hunter/streamrefactoring/core/analysis/Stream.java | 1 - 1 file changed, 1 deletion(-) 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 932707d4..787886af 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 @@ -56,7 +56,6 @@ import com.ibm.wala.ipa.callgraph.impl.FakeRootMethod; import com.ibm.wala.ipa.callgraph.impl.FakeWorldClinitMethod; import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; -import com.ibm.wala.ipa.callgraph.propagation.cfa.CallStringContext; import com.ibm.wala.ipa.cha.ClassHierarchyException; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.shrikeCT.InvalidClassFileException; From ab879785938f8da640a5a20a4c8bf26cb31ab2e3 Mon Sep 17 00:00:00 2001 From: Yiming Date: Wed, 6 Dec 2017 03:28:48 -0500 Subject: [PATCH 6/9] change method name and format --- .../cuny/hunter/streamrefactoring/core/analysis/Stream.java | 3 +-- .../streamrefactoring/core/analysis/StreamStateMachine.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) 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 787886af..487129b5 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 @@ -651,8 +651,7 @@ protected CGNode getEnclosingMethodNode() throws IOException, CoreException, NoE * @throws CoreException * @throws NoEnclosingMethodNodeFoundException */ - protected HashSet getEnclosingMethodNodes() - throws IOException, CoreException, NoEnclosingMethodNodeFoundException { + protected HashSet getCGNodesInGraph() throws IOException, CoreException { HashSet cgNodes = new HashSet<>(); Iterator cgNodeIterator = this.getAnalysisEngine().getCallGraph().iterator(); 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 1ceb36b7..72953930 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 @@ -280,7 +280,7 @@ public void start() throws IOException, CoreException, CallGraphBuilderCancelExc // the node of where the stream was declared: TODO: Can this be // somehow rewritten to get blocks corresponding to terminal // operations? - for (CGNode cgNode : this.getStream().getEnclosingMethodNodes()) { + for (CGNode cgNode : this.getStream().getCGNodesInGraph()) { for (Iterator callSites = cgNode.iterateCallSites(); callSites.hasNext();) { CallSiteReference callSiteReference = callSites.next(); MethodReference calledMethod = callSiteReference.getDeclaredTarget(); From 05bf41ac8cc4faf3bf37203d0dfb5890d891e87f Mon Sep 17 00:00:00 2001 From: Yiming Date: Wed, 6 Dec 2017 15:18:43 -0500 Subject: [PATCH 7/9] #103 change iterator --- .../core/analysis/Stream.java | 18 ------------------ .../core/analysis/StreamStateMachine.java | 4 +++- 2 files changed, 3 insertions(+), 19 deletions(-) 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 487129b5..1bbd5ba6 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 @@ -643,24 +643,6 @@ protected CGNode getEnclosingMethodNode() throws IOException, CoreException, NoE else return nodes.iterator().next(); // just return the first. } - - /** - * add all CGNodes in call graph to a hash set - * @return a hash set of CGNode - * @throws IOException - * @throws CoreException - * @throws NoEnclosingMethodNodeFoundException - */ - protected HashSet getCGNodesInGraph() throws IOException, CoreException { - HashSet cgNodes = new HashSet<>(); - - Iterator cgNodeIterator = this.getAnalysisEngine().getCallGraph().iterator(); - for (; cgNodeIterator.hasNext();) { - cgNodes.add(cgNodeIterator.next()); - } - - return cgNodes; - } /** * Returns true iff all of the predecessors of all of the given {@link CGNode}s 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 72953930..f2f8ffd0 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 @@ -280,7 +280,9 @@ public void start() throws IOException, CoreException, CallGraphBuilderCancelExc // the node of where the stream was declared: TODO: Can this be // somehow rewritten to get blocks corresponding to terminal // operations? - for (CGNode cgNode : this.getStream().getCGNodesInGraph()) { + Iterator cgNodes = this.getStream().getAnalysisEngine().getCallGraph().iterator(); + for (;cgNodes.hasNext();) { + CGNode cgNode = cgNodes.next(); for (Iterator callSites = cgNode.iterateCallSites(); callSites.hasNext();) { CallSiteReference callSiteReference = callSites.next(); MethodReference calledMethod = callSiteReference.getDeclaredTarget(); From 87bb0bc2b5351c91a8ec27fe090232029176a707 Mon Sep 17 00:00:00 2001 From: Yiming Date: Thu, 7 Dec 2017 14:08:17 -0500 Subject: [PATCH 8/9] #103 add a test case --- .../testNonInternalAPI6/in/A.java | 23 +++++++++++++++++++ ...onvertStreamToParallelRefactoringTest.java | 9 ++++++++ 2 files changed, 32 insertions(+) create mode 100644 edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI6/in/A.java diff --git a/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI6/in/A.java b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI6/in/A.java new file mode 100644 index 00000000..da1fe804 --- /dev/null +++ b/edu.cuny.hunter.streamrefactoring.tests/resources/ConvertStreamToParallel/testNonInternalAPI6/in/A.java @@ -0,0 +1,23 @@ +package p; + +import java.util.HashSet; +import java.util.stream.*; + +import edu.cuny.hunter.streamrefactoring.annotations.*; + +class A { + + Stream m() { + Stream stream = new HashSet<>().stream().parallel(); + return stream; + } + + void n(Stream s) { + s.distinct().count(); + } + + @EntryPoint + public void main(String[] args) { + n(m()); + } +} \ 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 3f897554..8aa011a4 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 @@ -491,6 +491,15 @@ public void testNonInternalAPI5() throws Exception { RefactoringStatus.ERROR, EnumSet.of(PreconditionFailure.INCONSISTENT_POSSIBLE_EXECUTION_MODES))); } + public void testNonInternalAPI6() throws Exception { + HashSet executionModes = new HashSet<>(); + 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))); + } + public void testCollectionFromParameter() throws Exception { helper(new StreamAnalysisExpectedResult("h.parallelStream()", Collections.singleton(ExecutionMode.PARALLEL), Collections.singleton(Ordering.UNORDERED), false, true, false, null, null, null, From 6ea3f22d09a23ff27e947af6a0b1a608ff3fffdb Mon Sep 17 00:00:00 2001 From: Yiming Date: Sat, 9 Dec 2017 11:55:24 -0500 Subject: [PATCH 9/9] add comments --- .../ui/tests/ConvertStreamToParallelRefactoringTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) 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 8aa011a4..dfbfb320 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 @@ -472,6 +472,9 @@ public void testNonInternalAPI3() throws Exception { Refactoring.CONVERT_SEQUENTIAL_STREAM_TO_PARALLEL, RefactoringStatus.OK, Collections.emptySet())); } + /** + * related to #126 + */ public void testNonInternalAPI4() throws Exception { HashSet orderings = new HashSet<>(); orderings.add(Ordering.UNORDERED); @@ -482,6 +485,9 @@ public void testNonInternalAPI4() throws Exception { RefactoringStatus.ERROR, EnumSet.of(PreconditionFailure.INCONSISTENT_POSSIBLE_ORDERINGS))); } + /** + * related to #126 + */ public void testNonInternalAPI5() throws Exception { HashSet executionModes = new HashSet<>(); executionModes .add(ExecutionMode.PARALLEL); @@ -491,6 +497,9 @@ public void testNonInternalAPI5() throws Exception { RefactoringStatus.ERROR, EnumSet.of(PreconditionFailure.INCONSISTENT_POSSIBLE_EXECUTION_MODES))); } + /** + * related to #126 + */ public void testNonInternalAPI6() throws Exception { HashSet executionModes = new HashSet<>(); executionModes.add(ExecutionMode.PARALLEL);