diff --git a/docs/bodyinterceptors.md b/docs/bodyinterceptors.md
index 4039ee4508e..bd1de886a9a 100644
--- a/docs/bodyinterceptors.md
+++ b/docs/bodyinterceptors.md
@@ -101,7 +101,9 @@ Obviously, the code segmentl2 = 2; l3 = 3;
is unreachable. It will b
### CopyPropagator
-CopyPropagator is aBodyInterceptor
that supports the global copy propagation and constant propagation.
+CopyPropagator is aBodyInterceptor
that supports the global copy propagation and constant propagation.
+
+Refer [Sreekala, S. K. and Vineeth Kumar Paleri. “Copy Propagation subsumes Constant Propagation.” ArXiv abs/2207.03894 (2022): n. pag.](https://arxiv.org/pdf/2207.03894)
Example for global copy propagation:
diff --git a/shared-test-resources/interceptors/CopyPropagatorTest.class b/shared-test-resources/interceptors/CopyPropagatorTest.class
new file mode 100644
index 00000000000..5f0ebb5f6b7
Binary files /dev/null and b/shared-test-resources/interceptors/CopyPropagatorTest.class differ
diff --git a/shared-test-resources/interceptors/CopyPropagatorTest.java b/shared-test-resources/interceptors/CopyPropagatorTest.java
new file mode 100644
index 00000000000..b0a9ebbe41c
--- /dev/null
+++ b/shared-test-resources/interceptors/CopyPropagatorTest.java
@@ -0,0 +1,11 @@
+public class CopyPropagatorTest {
+
+ void tc1(int w) {
+ int y, z = 0;
+ y = w;
+ w = 10;
+ z = 20;
+ int x = y + z;
+ }
+
+}
\ No newline at end of file
diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/CopyPropagatorTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/CopyPropagatorTest.java
index 1415c00a12d..6503c11d978 100644
--- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/CopyPropagatorTest.java
+++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/CopyPropagatorTest.java
@@ -1,12 +1,14 @@
package sootup.java.bytecode.interceptors;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.*;
import categories.TestCategories;
import java.nio.file.Paths;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import sootup.core.graph.MutableStmtGraph;
@@ -28,6 +30,9 @@
import sootup.core.signatures.MethodSignature;
import sootup.core.types.VoidType;
import sootup.core.util.ImmutableUtils;
+import sootup.core.util.Utils;
+import sootup.core.views.View;
+import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation;
import sootup.java.bytecode.inputlocation.PathBasedAnalysisInputLocation;
import sootup.java.core.JavaIdentifierFactory;
import sootup.java.core.interceptors.CopyPropagator;
@@ -128,6 +133,44 @@ public class CopyPropagatorTest {
JAssignStmt estmt13 =
JavaJimple.newAssignStmt(r5, NullConstant.getInstance(), noStmtPositionInfo);
+ public View setUp() {
+ String baseDir = "../shared-test-resources/interceptors/";
+ JavaClassPathAnalysisInputLocation inputLocation =
+ new JavaClassPathAnalysisInputLocation(
+ baseDir, SourceType.Library, Collections.emptyList());
+ final JavaView view = new JavaView(Arrays.asList(inputLocation));
+ return view;
+ }
+
+ @Test
+ public void testCopyPropagationWithRedefinition() {
+ View view = setUp();
+ final MethodSignature methodSignature =
+ view.getIdentifierFactory()
+ .getMethodSignature(
+ "CopyPropagatorTest", "tc1", "void", Collections.singletonList("int"));
+ Body bodyBefore = view.getMethod(methodSignature).get().getBody();
+ final Body.BodyBuilder builder = Body.builder(bodyBefore, Collections.emptySet());
+ new CopyPropagator().interceptBody(builder, view);
+ Body bodyAfter = builder.build();
+ assertEquals(
+ Stream.of(
+ "CopyPropagatorTest this",
+ "int l1",
+ "unknown l2, l3, l4",
+ "this := @this: CopyPropagatorTest",
+ "l1 := @parameter0: int",
+ "l3 = 0",
+ "l2 = l1",
+ "l1 = 10",
+ "l3 = 20",
+ // l2 should not be replaced with l1 as l1 gets redefined
+ "l4 = l2 + 20",
+ "return")
+ .collect(Collectors.toList()),
+ Utils.filterJimple(bodyAfter.toString()));
+ }
+
@Test
public void testEqualStmt() {
assertTrue(eestmt4.equivTo(eestmt4.withRValue(NullConstant.getInstance())));
diff --git a/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java b/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java
index 2e4d15867ff..4509c3fe262 100644
--- a/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java
+++ b/sootup.java.core/src/main/java/sootup/java/core/interceptors/CopyPropagator.java
@@ -22,6 +22,7 @@
*/
import com.google.common.collect.Lists;
+import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -91,7 +92,48 @@ else if (rhs instanceof JCastExpr && rhs.getType() instanceof ReferenceType) {
}
// if rhs is a local, then replace use, if it is possible
else if (rhs instanceof Local && !rhs.equivTo(use)) {
- newStmt = replaceUse(stmtGraph, newStmt, use, rhs);
+ Local m = (Local) rhs;
+ if (use != m) {
+ Integer defCount = m.getDefs(stmtGraph.getStmts()).size();
+ if (defCount == null || defCount == 0) {
+ throw new RuntimeException("Variable " + m + " used without definition!");
+ } else if (defCount == 1) {
+ newStmt = replaceUse(stmtGraph, newStmt, use, rhs);
+ continue;
+ }
+
+ List path = stmtGraph.getExtendedBasicBlockPathBetween(defStmt, newStmt);
+ if (path == null) {
+ // no path in the extended basic block
+ continue;
+ }
+ {
+ boolean isRedefined = false;
+ Iterator pathIt = path.iterator();
+ // Skip first node
+ pathIt.next();
+ // Make sure that m is not redefined along path
+ while (pathIt.hasNext()) {
+ Stmt s = (Stmt) pathIt.next();
+ if (newStmt == s) {
+ // Don't look at the last statement
+ // since it is evaluated after the uses.
+ break;
+ }
+ if (s instanceof AbstractDefinitionStmt) {
+ if (((AbstractDefinitionStmt) s).getLeftOp() == m) {
+ isRedefined = true;
+ break;
+ }
+ }
+ }
+
+ if (isRedefined) {
+ continue;
+ }
+ }
+ newStmt = replaceUse(stmtGraph, newStmt, use, rhs);
+ }
}
}
}