From 0b3cfdb10317a59bc159ea3e01307644b3108f31 Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Sat, 6 Jan 2024 22:04:48 +0100 Subject: [PATCH 01/24] wip --- .../bytecode/interceptors/LocalSplitter.java | 410 +++--------- .../interceptors/LocalSplitterTarget.class | Bin 0 -> 287 bytes .../interceptors/LocalSplitterTest.java | 622 ++---------------- 3 files changed, 129 insertions(+), 903 deletions(-) create mode 100644 sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 99bc4fcafe4..4bbbc4b2d6c 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -24,19 +24,14 @@ import java.util.*; import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import sootup.core.graph.StmtGraph; + import sootup.core.jimple.basic.LValue; import sootup.core.jimple.basic.Local; import sootup.core.jimple.basic.Value; -import sootup.core.jimple.common.ref.JCaughtExceptionRef; -import sootup.core.jimple.common.stmt.AbstractDefinitionStmt; -import sootup.core.jimple.common.stmt.JIdentityStmt; +import sootup.core.jimple.common.stmt.JAssignStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; -import sootup.core.model.Body.BodyBuilder; import sootup.core.transform.BodyInterceptor; -import sootup.core.types.ClassType; import sootup.core.views.View; /** @@ -53,7 +48,7 @@ * l2 = l2 + 1 * return * - * + *

* to: * *

@@ -65,342 +60,103 @@
  *    return
  * 
* - * @author Zun Wang */ public class LocalSplitter implements BodyInterceptor { - // FIXME: [ms] assumes that names of Locals do not contain a '#' already -> could lead to problems - // TODO: [ms] check equivTo()'s - I guess they can be equals()'s - or even: '=='s - - @Override - public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) { - // Find all Locals that must be split - // If a local as a definition appears two or more times, then this local must be split - List stmts = builder.getStmts(); - Set visitedLocals = new LinkedHashSet<>(); - Set toSplitLocals = new LinkedHashSet<>(); - for (Stmt stmt : stmts) { - final List defs = stmt.getDefs(); - if (!defs.isEmpty()) { - Value def = defs.get(0); - if (def instanceof Local) { - if (visitedLocals.contains(def)) { - toSplitLocals.add((Local) def); - } - visitedLocals.add((Local) def); + @Override + public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) { + Set localsToSplit = findLocalsToSplit(builder); + Map mostRecentDef = new HashMap<>(); + Map originalToNewStmt = new HashMap<>(); + for (Local local : localsToSplit) { + mostRecentDef.put(local, 0); } - } - } - - StmtGraph graph = builder.getStmtGraph(); - - // Create a new Local-Set for the modified new body. - Set newLocals = new LinkedHashSet<>(builder.getLocals()); - int localIndex = 1; - - // iterate stmts - while (!stmts.isEmpty()) { - Stmt currentStmt = stmts.remove(0); - // At first Check the definition(left side) of the currentStmt is a local which must be split: - final List defs = currentStmt.getDefs(); - if (!defs.isEmpty() && defs.get(0) instanceof Local && toSplitLocals.contains(defs.get(0))) { - // then assign a new name to the oriLocal to get a new local which is called newLocal - Local oriLocal = (Local) defs.get(0); - Local newLocal = oriLocal.withName(oriLocal.getName() + "#" + localIndex); - newLocals.add(newLocal); - localIndex++; - - // create newStmt whose definition is replaced with the newLocal, - Stmt newStmt = ((AbstractDefinitionStmt) currentStmt).withNewDef(newLocal); - // replace corresponding oldStmt with newStmt in builder - replaceStmtInBuilder(builder, stmts, currentStmt, newStmt); - - // Build the forwardsQueue which is used to iterate all Stmts before the orilocal is defined - // again. - // The direction of iteration is from root of the StmtGraph to leafs. So the successors of - // the newStmt are added into the forwardsQueue. - Deque forwardsQueue = new ArrayDeque<>(graph.successors(newStmt)); - // Create the visitedStmt to store the visited Stmts for the forwardsQueue, to avoid, a - // Stmt is added twice into the forwardQueue. - Set visitedStmts = new HashSet<>(); - - while (!forwardsQueue.isEmpty()) { - Stmt head = forwardsQueue.remove(); - visitedStmts.add(head); - - // 1.case: if useList of head contains oriLocal, then replace the oriLocal with - // newLocal. - if (head.getUses().contains(oriLocal)) { - Stmt newHead = head.withNewUse(oriLocal, newLocal); - replaceStmtInBuilder(builder, stmts, head, newHead); - - // if head doesn't define the the oriLocal again, then add all successors which are - // not in forwardsQueue and visitedUsesStmt, into the forwardsQueue. - if (newHead.getDefs().isEmpty() || !newHead.getDefs().get(0).equivTo(oriLocal)) { - for (Stmt succ : graph.successors(newHead)) { - if (!visitedStmts.contains(succ) && !forwardsQueue.contains(succ)) { - forwardsQueue.addLast(succ); - } - } - } - } - - // 2.case: if uses of head contains the modified orilocal, so a conflict maybe arise, - // then trace the StmtGraph backwards to resolve the conflict. - else if (hasModifiedUse(head, oriLocal)) { - - Local modifiedLocal = getModifiedUse(head, oriLocal); - if (modifiedLocal == null) { - throw new IllegalStateException("Modified Use is not found."); - } - - // if modifed name is not same as the newLocal's name then -> conflict arises -> trace - // backwards - if (!modifiedLocal.getName().equals(newLocal.getName())) { - localIndex--; - - // Build the backwardsQueue which is used to iterate all Stmts between head and the - // Stmts which define the oriLocal in last time. - // The direction of iteration is from leave of the StmtGraph to the root. So the - // predecessors of head are added into the BackwardsQueue. - Deque backwardsQueue = new ArrayDeque<>(graph.predecessors(head)); - - while (!backwardsQueue.isEmpty()) { - // Remove the first Stmt of backwardQueue, and name it as backStmt. - Stmt backStmt = backwardsQueue.remove(); - - // 2.1 case: if backStmt's definition is the modified and has a higher - // local-name-index than the modifiedLocal of head - // then replace the definition of backStmt with the modifiedLocal of head, and - // remove the corresponding Local(definition of backStmt) from the set: newLocals - if (hasModifiedDef(backStmt, oriLocal)) { - if (hasHigherLocalName((Local) backStmt.getDefs().get(0), modifiedLocal)) { - Stmt newBackStmt = - ((AbstractDefinitionStmt) backStmt).withNewDef(modifiedLocal); - replaceStmtInBuilder(builder, stmts, backStmt, newBackStmt); - newLocals.remove(newLocal); - } - } - // 2.2 case: if backStmt's uses contains the modified oriLocal, and this - // modified oriLocal has a higher local-name-index that the modifiedLocal of head - // then replace the corresponding use of backStmt with modifiedLocal of head, and - // add all predecessors of the backStmt into the backwardsQueue. - else if (hasModifiedUse(backStmt, oriLocal)) { - Local modifiedUse = getModifiedUse(backStmt, oriLocal); - if (hasHigherLocalName(modifiedUse, modifiedLocal)) { - Stmt newBackStmt = backStmt.withNewUse(modifiedUse, modifiedLocal); - replaceStmtInBuilder(builder, stmts, backStmt, newBackStmt); - backwardsQueue.addAll(graph.predecessors(newBackStmt)); - } - } - // 2.3 case: if there's no relationship between backStmt's defs/uses and - // oriLocal, then add all predecessors of the backStmt into the backwardsQueue. - else { - backwardsQueue.addAll(graph.predecessors(backStmt)); - } - } + int i = 0; + for (Stmt stmt : builder.getStmts()) { + List defs = stmt.getDefs(); + if(defs.size()==0){ + continue; } - } - // 3.case: if uses of head contains neither orilocal nor the modified orilocal, - // then add all successors of head which are not in forwardsQueue and visitedStmts, - // into the forwardsQueue. - else { - final List headDefs = head.getDefs(); - if (headDefs.isEmpty() || !headDefs.get(0).equivTo(oriLocal)) { - for (Stmt succ : graph.successors(head)) { - if (!visitedStmts.contains(succ) && !forwardsQueue.contains(succ)) { - forwardsQueue.addLast(succ); + if (defs.size()==1) { + LValue def = defs.get(0); + if (def instanceof Local) { + Local oldLocal = (Local) def; + if (localsToSplit.contains(oldLocal)) { + handleSelfUse(builder, stmt, oldLocal, mostRecentDef, originalToNewStmt); + Local newLocal = oldLocal.withName(oldLocal.getName() + '#' + (++i)); // renaming should not be done here + mostRecentDef.put(oldLocal, i); + builder.addLocal(newLocal); + Stmt toReplace; + if (originalToNewStmt.containsKey(stmt)) { + toReplace = originalToNewStmt.get(stmt); + } else { + toReplace = stmt; + } + Stmt withNewDef = new JAssignStmt(newLocal, toReplace.getUses().get(0), stmt.getPositionInfo()); + builder.getStmtGraph().replaceNode(toReplace, withNewDef); + originalToNewStmt.put(stmt, withNewDef); + } } - } + } else{ + throw new RuntimeException("stmt with more than 1 def!"); } - } - } - // Then check the uses of currentStmt: - } else { - // For each Local(oriL) which is to be split, check whether it is used in currentStmt - // without definition before. - // We define a StmtGraph consists of a mainStmtGraph and none or more trapStmtGraphs. - // This situation could arise just in a trapStmtGraph. - for (Local oriLocal : toSplitLocals) { - // If so: - // 1.step: find out all trapStmtGraphs' root(handlerStmts) which contain currentStmt, - // namely a set of handlerStmts. - // 2.step: find out all stmts whose exceptional destination-traps are with the found - // handlerStmts. - // 3.step: iterate these stmts, find a modified oriL((Local) with a maximum name index. - // 4.step: Use this modified oriL to modify the visitedStmt - if (currentStmt.getUses().contains(oriLocal)) { - // 1.step: - Set handlerStmts = traceHandlerStmts(graph, currentStmt); - // 2.step: - Set stmtsWithDests = new HashSet<>(); - for (Stmt handlerStmt : handlerStmts) { - for (Stmt exceptionalPred : graph.predecessors(handlerStmt)) { - Map dests = graph.exceptionalSuccessors(exceptionalPred); - List destHandlerStmts = new ArrayList<>(); - dests.forEach((key, dest) -> destHandlerStmts.add(dest)); - if (destHandlerStmts.contains(handlerStmt)) { - stmtsWithDests.add(exceptionalPred); +/* + for (Value use : stmt.getUses()) { + if (!(use instanceof Local)) { + continue; } - } - } - // 3.step: - Local lastChange = null; - for (Stmt stmt : stmtsWithDests) { - if (hasModifiedDef(stmt, oriLocal)) { - Local modifiedLocal = (Local) stmt.getDefs().get(0); - if (lastChange == null || hasHigherLocalName(modifiedLocal, lastChange)) { - lastChange = modifiedLocal; + Local oldLocalUse = (Local) use; + if (localsToSplit.contains(use)) { + Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + mostRecentDef.get(oldLocalUse)); // use the most recent split name + Stmt withNewUse = stmt.withNewUse(oldLocalUse, newLocal); + Stmt toReplace; + if (originalToNewStmt.containsKey(stmt)) { + toReplace = originalToNewStmt.get(stmt); + } else { + toReplace = stmt; + } + builder.getStmtGraph().replaceNode(toReplace, withNewUse); + originalToNewStmt.put(stmt, withNewUse); } - } - } - // 4.step: - if (lastChange != null) { - Stmt newStmt = currentStmt.withNewUse(oriLocal, lastChange); - replaceStmtInBuilder(builder, stmts, currentStmt, newStmt); } - } +*/ } - } - } - builder.setLocals(newLocals); - } - - // ******************assist_functions************************* - - /** - * Replace corresponding oldStmt with newStmt in BodyBuilder and visitList - * - * @param builder - * @param stmtIterationList - * @param oldStmt - * @param newStmt - */ - private void replaceStmtInBuilder( - @Nonnull BodyBuilder builder, - @Nonnull List stmtIterationList, - @Nonnull Stmt oldStmt, - @Nonnull Stmt newStmt) { - - builder.replaceStmt(oldStmt, newStmt); - - // adapt VisitList - final int index = stmtIterationList.indexOf(oldStmt); - if (index > -1) { - stmtIterationList.set(index, newStmt); - } - } - - /** - * Check whether a Stmt's useList contains the given modified oriLocal. - * - * @param stmt: a stmt is to be checked - * @param oriLocal: a local is to be checked - * @return if so, return true, else return false - */ - private boolean hasModifiedUse(@Nonnull Stmt stmt, @Nonnull Local oriLocal) { - for (Value use : stmt.getUses()) { - return isLocalFromSameOrigin(oriLocal, use); } - return false; - } - /** - * Get the modified Local if a Stmt's useList contains the given modified oriLocal - * - * @param stmt: a stmt is to be checked - * @param oriLocal: a local is to be checked. - * @return if so, return this modified local, else return null - */ - @Nullable - private Local getModifiedUse(@Nonnull Stmt stmt, @Nonnull Local oriLocal) { - if (hasModifiedUse(stmt, oriLocal)) { - List useList = stmt.getUses(); - for (Value use : useList) { - if (isLocalFromSameOrigin(oriLocal, use)) { - return (Local) use; + private void handleSelfUse(Body.BodyBuilder builder, Stmt stmt, Local def, Map mostRecentDef, Map originalToNewStmt) { + for (Value use : stmt.getUses()) { + if(use.equals(def)){ + Local oldLocalUse = (Local) use; + Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + mostRecentDef.get(oldLocalUse)); // use the most recent split name + Stmt withNewUse = stmt.withNewUse(oldLocalUse, newLocal); + Stmt toReplace; + if (originalToNewStmt.containsKey(stmt)) { + toReplace = originalToNewStmt.get(stmt); + } else { + toReplace = stmt; + } + builder.getStmtGraph().replaceNode(toReplace, withNewUse); + originalToNewStmt.put(stmt, withNewUse); + } } - } } - return null; - } - /** - * Check whether a local is modified from the given oriLocal - * - * @param local: a local is to be checked - * @param oriLocal: the given oriLocal - * @return if so, return true, else return false. - */ - private boolean isLocalFromSameOrigin(@Nonnull Local oriLocal, Value local) { - if (local instanceof Local) { - final String name = ((Local) local).getName(); - final String origName = oriLocal.getName(); - final int origLength = origName.length(); - return name.length() > origLength - && name.startsWith(origName) - && name.charAt(origLength) == '#'; - } - return false; - } - - /** - * Check whether a Stmt's def is the modified oriLocal - * - * @param stmt: a stmt is to be checked - * @param oriLocal: a local is to be checked - * @return if so, return true, else return false - */ - private boolean hasModifiedDef(@Nonnull Stmt stmt, @Nonnull Local oriLocal) { - final List defs = stmt.getDefs(); - if (!defs.isEmpty() && defs.get(0) instanceof Local) { - return isLocalFromSameOrigin(oriLocal, defs.get(0)); + private Set findLocalsToSplit(Body.BodyBuilder builder) { + Set visitedLocals = new LinkedHashSet<>(); + Set localsToSplit = new LinkedHashSet<>(); + for (Stmt stmt : builder.getStmts()) { + for (LValue def : stmt.getDefs()) { + if (def instanceof Local) { + if (visitedLocals.contains(def)) { + localsToSplit.add((Local) def); + } else { + visitedLocals.add((Local) def); + } + } + } + } + return localsToSplit; } - return false; - } - - /** - * Check whether leftLocal's name has higher index than rightLocal's. - * - * @param leftLocal: a local in form oriLocal#num1 - * @param rightLocal: a local in form oriLocal#num2 - * @return if so return true, else return false - */ - private boolean hasHigherLocalName(@Nonnull Local leftLocal, @Nonnull Local rightLocal) { - String leftName = leftLocal.getName(); - String rightName = rightLocal.getName(); - int lIdx = leftName.lastIndexOf('#'); - int rIdx = rightName.lastIndexOf('#'); - int leftNum = Integer.parseInt(leftName.substring(lIdx + 1)); - int rightNum = Integer.parseInt(rightName.substring(rIdx + 1)); - return leftNum > rightNum; - } - /** - * A given entryStmt may be in one or several trapStmtGraphs, return these trapStmtGraphs' - * handlerStmts - * - * @param entryStmt a given entryStmt which is in one or several trapStmtGraphs - * @param graph to trace handlerStmts - * @return a set of handlerStmts - */ - @Nonnull - private Set traceHandlerStmts(@Nonnull StmtGraph graph, @Nonnull Stmt entryStmt) { - Set handlerStmts = new HashSet<>(); - - Deque queue = new ArrayDeque<>(); - queue.add(entryStmt); - while (!queue.isEmpty()) { - Stmt stmt = queue.removeFirst(); - if (stmt instanceof JIdentityStmt - && ((JIdentityStmt) stmt).getRightOp() instanceof JCaughtExceptionRef) { - handlerStmts.add(stmt); - } else { - final List predecessors = graph.predecessors(stmt); - queue.addAll(predecessors); - } - } - return handlerStmts; - } } diff --git a/sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class b/sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class new file mode 100644 index 0000000000000000000000000000000000000000..1b16df51427dc4446148c7dffd5a0597de5b1498 GIT binary patch literal 287 zcmZurO>4qH5Pg$u5*y)RA#u6I~hU1BV7NpCv>f2RLF8Udd*fU zH+Y1@KiP7?Kfq)3d3s - * l0 := @this Test - * l1 = 0 - * if l1 >= 0 goto label1 - * l1 = l1 + 1 - * goto label2 - * label1: - * l1 = l1 - 1 - * l1 = l1 + 2 - * label2: - * return l1 - * - * - * to: - * - *
-   *    l0 := @this Test
-   *    l1#1 = 0
-   *    if l1#1 >= 0 goto label1
-   *    l1#2 = l1#1 + 1
-   *    goto label2
-   * label1:
-   *    l1#3 = l1#1 - 1
-   *    l1#2 = l1#3 + 2
-   * label2:
-   *    return l1#2
-   * 
- */ - @Test - public void testLocalSplitterForBinaryBranches() { - - Body body = createBBBody(); - Body.BodyBuilder builder = Body.builder(body, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, null); - Body expectedBody = createExpectedBBBody(); - - final Body interceptedBody = builder.build(); - // check newBody's locals - AssertUtils.assertLocalsEquiv(expectedBody, interceptedBody); - - // check newBody's stmtGraph - AssertUtils.assertStmtGraphEquiv(expectedBody, interceptedBody); - } +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; - /** - * int a = 0; int b = 0; a = a + 1; b = b + 1; - * - *
-   *    l0 := @this Test
-   *    l1 = 0
-   *    l2 = 1
-   *    l1 = l1 + 1
-   *    l2 = l2 + 1
-   *    return
-   * 
- * - * to: - * - *
-   *    l0 := @this Test
-   *    l1#1 = 0
-   *    l2#2 = 1
-   *    l1#3 = l1#1 + 1
-   *    l2#4 = l2#2 + 1
-   *    return
-   * 
- */ - @Test - public void testLocalSplitterForMultilocals() { - - Body body = createMultilocalsBody(); - Body.BodyBuilder builder = Body.builder(body, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, null); - Body expectedBody = createExpectedMuiltilocalsBody(); +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; - // check newBody's locals - AssertUtils.assertLocalsEquiv(expectedBody, builder.build()); +@Category(Java8Test.class) +public class LocalSplitterTest { + JavaView view; - // check newBody's stmtGraph - AssertUtils.assertStmtGraphEquiv(expectedBody, builder.build()); + @Before + public void Setup() { + String classPath = "src/test/java/resources/interceptors"; + JavaClassPathAnalysisInputLocation inputLocation = new JavaClassPathAnalysisInputLocation(classPath); + view = new JavaView(inputLocation); } - /** - * for(int i = 0; i < 10; i++){ i = i + 1 } transform: - * - *
-   *    l0 := @this Test
-   *    l1 = 0
-   * label1:
-   *    $stack4 = l1
-   *    $stack3 = 10
-   *    if $stack4 >= $stack3 goto label2
-   *    l2 = l1 + 1
-   *    l1 = l2 + 1
-   *    l1 = l1 +1
-   *    goto label1
-   * label2:
-   *    return
-   * 
- * - * to: - * - *
-   *    l0 := @this Test
-   *    l1#1 = 0
-   * label1:
-   *    $stack4 = l1#1
-   *    $stack3 = 10
-   *    if $stack4 >= $stack3 goto label2
-   *    l2 = l1#1 + 1
-   *    l1#2 = l2 + 1
-   *    l1#1 = l1#2 +1
-   *    goto label1
-   * label2:
-   *    return
-   * 
- */ @Test - public void testLocalSplitterForLoop() { - - Body body = createLoopBody(); - Body.BodyBuilder builder = Body.builder(body, Collections.emptySet()); + public void testSelfAssignment() { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case1", Collections.EMPTY_LIST, VoidType.getInstance())); + SootMethod sootMethod = view.getMethod(sig).get(); + Body originalBody = sootMethod.getBody(); + + List expectedLocals = new ArrayList<>(); + expectedLocals.addAll(originalBody.getLocals()); + expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + + Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, null); - Body expectedBody = createExpectedLoopBody(); - - // check newBody's locals - AssertUtils.assertLocalsEquiv(expectedBody, builder.build()); - - // check newBody's stmtGraph - AssertUtils.assertStmtGraphEquiv(expectedBody, builder.build()); - } - - /** - * for(int i = 0; i < 10; i++){ i = i + 1 } transform: - * - *
-   *    l0 := @this Test
-   *    l1 = 0
-   *    l1 = 1
-   *    l2 = 2
-   *    return
-   *    $stack3 := @caughtexception
-   *    l3 = l1
-   *    goto return
-   * 
- * - * to: - * - *
-   *    l0 := @this Test
-   *    l1#1 = 0
-   *    l1#2 = 1
-   *    l2 = 2
-   *    return
-   *    $stack3 := @caughtexception
-   *    l3 = l1#2
-   *    goto return
-   * 
- */ - @Test - public void testLocalSplitterInTraps() { - - Body.BodyBuilder builder = createTrapBody(); - new LocalSplitter().interceptBody(builder, null); - Body expectedBody = createExpectedTrapBody(); - - // check newBody's locals - AssertUtils.assertLocalsEquiv(expectedBody, builder.build()); - - // check newBody's stmtGraph - AssertUtils.assertStmtGraphEquiv(expectedBody, builder.build()); - } - - /** bodycreater for BinaryBranches */ - private Body createBBBody() { - MutableStmtGraph graph = new MutableBlockStmtGraph(); - Body.BodyBuilder builder = Body.builder(graph); - builder.setMethodSignature(methodSignature); - - // build set locals - Set locals = ImmutableUtils.immutableSet(l0, l1); - - builder.setLocals(locals); - - Stmt stmt1 = JavaJimple.newAssignStmt(l1, IntConstant.getInstance(0), noStmtPositionInfo); - BranchingStmt stmt2 = - JavaJimple.newIfStmt( - JavaJimple.newGeExpr(l1, IntConstant.getInstance(0)), noStmtPositionInfo); - Stmt stmt3 = - JavaJimple.newAssignStmt( - l1, JavaJimple.newAddExpr(l1, IntConstant.getInstance(1)), noStmtPositionInfo); - BranchingStmt stmt4 = JavaJimple.newGotoStmt(noStmtPositionInfo); - Stmt stmt5 = - JavaJimple.newAssignStmt( - l1, JavaJimple.newSubExpr(l1, IntConstant.getInstance(1)), noStmtPositionInfo); - FallsThroughStmt stmt6 = - JavaJimple.newAssignStmt( - l1, JavaJimple.newAddExpr(l1, IntConstant.getInstance(2)), noStmtPositionInfo); - Stmt ret = JavaJimple.newReturnStmt(l1, noStmtPositionInfo); - - graph.addBlock(Arrays.asList(startingStmt, stmt1, stmt2), Collections.emptyMap()); - graph.setEdges(stmt2, Arrays.asList(stmt3, stmt5)); - graph.addBlock(Arrays.asList(stmt3, stmt4), Collections.emptyMap()); - graph.addBlock(Arrays.asList(stmt5, stmt6), Collections.emptyMap()); - graph.putEdge(stmt4, JGotoStmt.BRANCH_IDX, ret); - graph.putEdge(stmt6, ret); - - graph.setStartingStmt(startingStmt); - - /* set graph - stmtGraph.putEdge(startingStmt, stmt1); - stmtGraph.putEdge(stmt1, stmt2); - stmtGraph.putEdge(stmt2, stmt3); - stmtGraph.putEdge(stmt3, stmt4); - stmtGraph.putEdge(stmt4, ret); - stmtGraph.putEdge(stmt2, stmt5); - stmtGraph.putEdge(stmt5, stmt6); - stmtGraph.putEdge(stmt6, ret); - - // build startingStmt - builder.setStartingStmt(startingStmt); - */ - - // build position - Position position = NoPositionInformation.getInstance(); - builder.setPosition(position); - - return builder.build(); - } - - private Body createExpectedBBBody() { - - MutableStmtGraph graph = new MutableBlockStmtGraph(); - Body.BodyBuilder builder = Body.builder(graph); - builder.setMethodSignature(methodSignature); - - // build set locals - Set locals = ImmutableUtils.immutableSet(l0, l1, l1hash1, l1hash2, l1hash3); - - builder.setLocals(locals); - - Stmt stmt1 = JavaJimple.newAssignStmt(l1hash1, IntConstant.getInstance(0), noStmtPositionInfo); - BranchingStmt stmt2 = - JavaJimple.newIfStmt( - JavaJimple.newGeExpr(l1hash1, IntConstant.getInstance(0)), noStmtPositionInfo); - Stmt stmt3 = - JavaJimple.newAssignStmt( - l1hash2, - JavaJimple.newAddExpr(l1hash1, IntConstant.getInstance(1)), - noStmtPositionInfo); - BranchingStmt stmt4 = JavaJimple.newGotoStmt(noStmtPositionInfo); - Stmt stmt5 = - JavaJimple.newAssignStmt( - l1hash3, - JavaJimple.newSubExpr(l1hash1, IntConstant.getInstance(1)), - noStmtPositionInfo); - FallsThroughStmt stmt6 = - JavaJimple.newAssignStmt( - l1hash2, - JavaJimple.newAddExpr(l1hash3, IntConstant.getInstance(2)), - noStmtPositionInfo); - Stmt ret = JavaJimple.newReturnStmt(l1hash2, noStmtPositionInfo); - - graph.addBlock(Arrays.asList(startingStmt, stmt1, stmt2), Collections.emptyMap()); - graph.setEdges(stmt2, Arrays.asList(stmt3, stmt5)); - graph.addBlock(Arrays.asList(stmt3, stmt4), Collections.emptyMap()); - graph.putEdge(stmt4, JGotoStmt.BRANCH_IDX, ret); - graph.addBlock(Arrays.asList(stmt5, stmt6), Collections.emptyMap()); - graph.putEdge(stmt6, ret); - - graph.setStartingStmt(startingStmt); - - /* set graph - stmtGraph.putEdge(startingStmt, stmt1); - stmtGraph.putEdge(stmt1, stmt2); - stmtGraph.putEdge(stmt2, stmt3); - stmtGraph.putEdge(stmt3, stmt4); - stmtGraph.putEdge(stmt4, ret); - stmtGraph.putEdge(stmt2, stmt5); - stmtGraph.putEdge(stmt5, stmt6); - stmtGraph.putEdge(stmt6, ret); - - // build startingStmt - builder.setStartingStmt(startingStmt); - */ - // build position - Position position = NoPositionInformation.getInstance(); - builder.setPosition(position); - - return builder.build(); - } - - /** bodycreater for multilocals */ - private Body createMultilocalsBody() { + localSplitter.interceptBody(builder, view); - MutableStmtGraph graph = new MutableBlockStmtGraph(); - Body.BodyBuilder builder = Body.builder(graph); - builder.setMethodSignature(methodSignature); + Body newBody = builder.build(); + assertTrue(expectedLocals.containsAll(newBody.getLocals())); + assertTrue(newBody.getLocals().containsAll(expectedLocals)); - // build set locals - Set locals = ImmutableUtils.immutableSet(l0, l1, l2); + String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + + "$l1#1 = 0;\n" + + "$l2#2 = 1;\n" + + "$l1#3 = $l1#1 + 1;\n" + + "$l2#4 = $l2#2 + 1;\n" + + "\n" + + "return;"; - builder.setLocals(locals); - - Stmt stmt1 = JavaJimple.newAssignStmt(l1, IntConstant.getInstance(0), noStmtPositionInfo); - Stmt stmt2 = JavaJimple.newAssignStmt(l2, IntConstant.getInstance(1), noStmtPositionInfo); - Stmt stmt3 = - JavaJimple.newAssignStmt( - l1, JavaJimple.newAddExpr(l1, IntConstant.getInstance(1)), noStmtPositionInfo); - Stmt stmt4 = - JavaJimple.newAssignStmt( - l2, JavaJimple.newAddExpr(l2, IntConstant.getInstance(1)), noStmtPositionInfo); - Stmt ret = JavaJimple.newReturnVoidStmt(noStmtPositionInfo); - - graph.addBlock( - Arrays.asList(startingStmt, stmt1, stmt2, stmt3, stmt4, ret), Collections.emptyMap()); - graph.setStartingStmt(startingStmt); - - // build position - Position position = NoPositionInformation.getInstance(); - builder.setPosition(position); - - return builder.build(); - } - - private Body createExpectedMuiltilocalsBody() { - - MutableStmtGraph graph = new MutableBlockStmtGraph(); - Body.BodyBuilder builder = Body.builder(graph); - builder.setMethodSignature(methodSignature); - - // build set locals - Set locals = ImmutableUtils.immutableSet(l0, l1, l2, l1hash1, l2hash2, l1hash3, l2hash4); - - builder.setLocals(locals); - - Stmt stmt1 = JavaJimple.newAssignStmt(l1hash1, IntConstant.getInstance(0), noStmtPositionInfo); - Stmt stmt2 = JavaJimple.newAssignStmt(l2hash2, IntConstant.getInstance(1), noStmtPositionInfo); - Stmt stmt3 = - JavaJimple.newAssignStmt( - l1hash3, - JavaJimple.newAddExpr(l1hash1, IntConstant.getInstance(1)), - noStmtPositionInfo); - Stmt stmt4 = - JavaJimple.newAssignStmt( - l2hash4, - JavaJimple.newAddExpr(l2hash2, IntConstant.getInstance(1)), - noStmtPositionInfo); - Stmt ret = JavaJimple.newReturnVoidStmt(noStmtPositionInfo); - - graph.addBlock( - Arrays.asList(startingStmt, stmt1, stmt2, stmt3, stmt4, ret), Collections.emptyMap()); - graph.setStartingStmt(startingStmt); - - /* set graph - stmtGraph.putEdge(startingStmt, stmt1); - stmtGraph.putEdge(stmt1, stmt2); - stmtGraph.putEdge(stmt2, stmt3); - stmtGraph.putEdge(stmt3, stmt4); - stmtGraph.putEdge(stmt4, ret); - - // set first stmt - builder.setStartingStmt(startingStmt); - */ - - // build position - Position position = NoPositionInformation.getInstance(); - builder.setPosition(position); - - return builder.build(); + assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } - /** bodycreater for Loop */ - private Body createLoopBody() { - - MutableStmtGraph graph = new MutableBlockStmtGraph(); - Body.BodyBuilder builder = Body.builder(graph); - builder.setMethodSignature(methodSignature); - - // build set locals - Set locals = ImmutableUtils.immutableSet(l0, l1, l2, stack3, stack4); - - builder.setLocals(locals); - - Stmt stmt1 = JavaJimple.newAssignStmt(l1, IntConstant.getInstance(0), noStmtPositionInfo); - Stmt stmt2 = JavaJimple.newAssignStmt(stack4, l1, noStmtPositionInfo); - Stmt stmt3 = JavaJimple.newAssignStmt(stack3, IntConstant.getInstance(10), noStmtPositionInfo); - BranchingStmt stmt4 = - JavaJimple.newIfStmt( - JavaJimple.newGeExpr(stack4, stack3), noStmtPositionInfo); // branch to ret - Stmt stmt5 = - JavaJimple.newAssignStmt( - l2, JavaJimple.newAddExpr(l1, IntConstant.getInstance(1)), noStmtPositionInfo); - Stmt stmt6 = - JavaJimple.newAssignStmt( - l1, JavaJimple.newAddExpr(l2, IntConstant.getInstance(1)), noStmtPositionInfo); - Stmt stmt7 = - JavaJimple.newAssignStmt( - l1, JavaJimple.newAddExpr(l1, IntConstant.getInstance(1)), noStmtPositionInfo); - BranchingStmt stmt8 = JavaJimple.newGotoStmt(noStmtPositionInfo); // goto stmt2 - Stmt ret = JavaJimple.newReturnVoidStmt(noStmtPositionInfo); - - graph.addBlock(Arrays.asList(startingStmt, stmt1, stmt2, stmt3, stmt4), Collections.emptyMap()); - graph.setEdges(stmt4, Arrays.asList(stmt5, ret)); - graph.addBlock(Arrays.asList(stmt5, stmt6, stmt7, stmt8), Collections.emptyMap()); - graph.putEdge(stmt8, JGotoStmt.BRANCH_IDX, stmt2); - - graph.setStartingStmt(startingStmt); - - // build position - Position position = NoPositionInformation.getInstance(); - builder.setPosition(position); - - return builder.build(); - } - - private Body createExpectedLoopBody() { - - MutableStmtGraph graph = new MutableBlockStmtGraph(); - Body.BodyBuilder builder = Body.builder(graph); - builder.setMethodSignature(methodSignature); - // build set locals - Set locals = ImmutableUtils.immutableSet(l0, l1, l2, stack3, stack4, l1hash1, l1hash2); - - builder.setLocals(locals); - - Stmt stmt1 = JavaJimple.newAssignStmt(l1hash1, IntConstant.getInstance(0), noStmtPositionInfo); - Stmt stmt2 = JavaJimple.newAssignStmt(stack4, l1hash1, noStmtPositionInfo); - Stmt stmt3 = JavaJimple.newAssignStmt(stack3, IntConstant.getInstance(10), noStmtPositionInfo); - BranchingStmt stmt4 = - JavaJimple.newIfStmt( - JavaJimple.newGeExpr(stack4, stack3), noStmtPositionInfo); // branch to ret - Stmt stmt5 = - JavaJimple.newAssignStmt( - l2, JavaJimple.newAddExpr(l1hash1, IntConstant.getInstance(1)), noStmtPositionInfo); - Stmt stmt6 = - JavaJimple.newAssignStmt( - l1hash2, JavaJimple.newAddExpr(l2, IntConstant.getInstance(1)), noStmtPositionInfo); - Stmt stmt7 = - JavaJimple.newAssignStmt( - l1hash1, - JavaJimple.newAddExpr(l1hash2, IntConstant.getInstance(1)), - noStmtPositionInfo); - BranchingStmt stmt8 = JavaJimple.newGotoStmt(noStmtPositionInfo); // goto stmt2 - Stmt ret = JavaJimple.newReturnVoidStmt(noStmtPositionInfo); - - graph.addBlock(Arrays.asList(startingStmt, stmt1, stmt2, stmt3, stmt4), Collections.emptyMap()); - graph.setEdges(stmt4, Arrays.asList(stmt5, ret)); - graph.addBlock(Arrays.asList(stmt5, stmt6, stmt7, stmt8), Collections.emptyMap()); - graph.putEdge(stmt8, JGotoStmt.BRANCH_IDX, stmt2); - - graph.setStartingStmt(startingStmt); - - // build startingStmt - builder.setStartingStmt(startingStmt); - - // build position - Position position = NoPositionInformation.getInstance(); - builder.setPosition(position); - - return builder.build(); - } - - private Body.BodyBuilder createTrapBody() { - MutableStmtGraph graph = new MutableBlockStmtGraph(); - Body.BodyBuilder builder = Body.builder(graph); - builder.setMethodSignature(methodSignature); - - // build set locals - Set locals = ImmutableUtils.immutableSet(l0, l1, l2, l3, stack3); - - builder.setLocals(locals); - - FallsThroughStmt stmt1 = - JavaJimple.newAssignStmt(l1, IntConstant.getInstance(0), noStmtPositionInfo); - FallsThroughStmt stmt2 = - JavaJimple.newAssignStmt(l1, IntConstant.getInstance(1), noStmtPositionInfo); - FallsThroughStmt stmt3 = - JavaJimple.newAssignStmt(l2, IntConstant.getInstance(2), noStmtPositionInfo); - FallsThroughStmt stmt4 = - JavaJimple.newIdentityStmt(stack3, caughtExceptionRef, noStmtPositionInfo); - FallsThroughStmt stmt5 = JavaJimple.newAssignStmt(l3, l1, noStmtPositionInfo); - BranchingStmt stmt6 = JavaJimple.newGotoStmt(noStmtPositionInfo); - Stmt ret = JavaJimple.newReturnVoidStmt(noStmtPositionInfo); - - // build graph - graph.addBlock( - Arrays.asList(startingStmt, stmt1, stmt2), Collections.singletonMap(exception, stmt4)); - graph.addBlock(Arrays.asList(stmt4, stmt5, stmt6), Collections.emptyMap()); - graph.addNode(stmt3); - graph.putEdge(stmt2, stmt3); - graph.putEdge(stmt3, ret); - graph.putEdge(stmt6, JGotoStmt.BRANCH_IDX, ret); - - graph.setStartingStmt(startingStmt); - - return builder; - } - - private Body createExpectedTrapBody() { - - MutableStmtGraph graph = new MutableBlockStmtGraph(); - Body.BodyBuilder builder = Body.builder(graph); - builder.setMethodSignature(methodSignature); - - // build set locals - Set locals = ImmutableUtils.immutableSet(l0, l1, l2, l3, stack3, l1hash1, l1hash2); - - builder.setLocals(locals); - - Stmt l1hash1assign0Stmt = - JavaJimple.newAssignStmt(l1hash1, IntConstant.getInstance(0), noStmtPositionInfo); - FallsThroughStmt l1hash2assign1Stmt = - JavaJimple.newAssignStmt(l1hash2, IntConstant.getInstance(1), noStmtPositionInfo); - FallsThroughStmt l2assign2Stmt = - JavaJimple.newAssignStmt(l2, IntConstant.getInstance(2), noStmtPositionInfo); - FallsThroughStmt exceptionCatchStmt = - JavaJimple.newIdentityStmt(stack3, caughtExceptionRef, noStmtPositionInfo); - Stmt l3assignl1hash2Stmt = JavaJimple.newAssignStmt(l3, l1hash2, noStmtPositionInfo); - BranchingStmt gotoStmt = JavaJimple.newGotoStmt(noStmtPositionInfo); - Stmt ret = JavaJimple.newReturnVoidStmt(noStmtPositionInfo); - - graph.addBlock( - Arrays.asList(startingStmt, l1hash1assign0Stmt, l1hash2assign1Stmt), - Collections.singletonMap(exception, exceptionCatchStmt)); - graph.addBlock( - Arrays.asList(exceptionCatchStmt, l3assignl1hash2Stmt, gotoStmt), Collections.emptyMap()); - graph.addNode(l2assign2Stmt); - graph.putEdge(l1hash2assign1Stmt, l2assign2Stmt); - graph.putEdge(l2assign2Stmt, ret); - graph.putEdge(gotoStmt, JGotoStmt.BRANCH_IDX, ret); - - graph.setStartingStmt(startingStmt); - - return builder.build(); - } } From 9f978ea3ee54c3085c09a75eadc018fe28c469a8 Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Sat, 6 Jan 2024 22:51:43 +0100 Subject: [PATCH 02/24] test simpleAssign --- .../bytecode/interceptors/LocalSplitter.java | 7 ++-- .../interceptors/LocalSplitterTarget.class | Bin 287 -> 0 bytes .../interceptors/LocalSplitterTest.java | 33 ++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) delete mode 100644 sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 4bbbc4b2d6c..389e0a0ce3c 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -100,7 +100,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) } else{ throw new RuntimeException("stmt with more than 1 def!"); } -/* + for (Value use : stmt.getUses()) { if (!(use instanceof Local)) { continue; @@ -108,18 +108,19 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) Local oldLocalUse = (Local) use; if (localsToSplit.contains(use)) { Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + mostRecentDef.get(oldLocalUse)); // use the most recent split name - Stmt withNewUse = stmt.withNewUse(oldLocalUse, newLocal); Stmt toReplace; if (originalToNewStmt.containsKey(stmt)) { toReplace = originalToNewStmt.get(stmt); } else { toReplace = stmt; } + Stmt withNewUse = toReplace.withNewUse(oldLocalUse, newLocal); + //Stmt withNewUse = new JAssignStmt(toReplace.getDefs().get(0), newLocal, stmt.getPositionInfo()); builder.getStmtGraph().replaceNode(toReplace, withNewUse); originalToNewStmt.put(stmt, withNewUse); } } -*/ + } } diff --git a/sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class b/sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class deleted file mode 100644 index 1b16df51427dc4446148c7dffd5a0597de5b1498..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 287 zcmZurO>4qH5Pg$u5*y)RA#u6I~hU1BV7NpCv>f2RLF8Udd*fU zH+Y1@KiP7?Kfq)3d3s expectedLocals = new ArrayList<>(); + expectedLocals.addAll(originalBody.getLocals()); + expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + + Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); + LocalSplitter localSplitter = new LocalSplitter(); + localSplitter.interceptBody(builder, view); + + Body newBody = builder.build(); + assertTrue(expectedLocals.containsAll(newBody.getLocals())); + assertTrue(newBody.getLocals().containsAll(expectedLocals)); + + String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + + "$l1#1 = 0;\n" + + "$l2#2 = 1;\n" + + "$l1#3 = $l2#2 + 1;\n" + + "$l2#4 = $l1#3 + 1;\n" + + "\n" + + "return;"; + + assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + } + @Test public void testSelfAssignment() { ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); From 6382df381b5272d1eb9ed99cba10d3ce399a85c2 Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Wed, 10 Jan 2024 16:17:12 +0100 Subject: [PATCH 03/24] initial solution with separate counter for each local --- .../java/sootup/core/graph/StmtGraph.java | 1 - .../bytecode/interceptors/LocalSplitter.java | 144 ++++++++++++------ .../interceptors/LocalSplitterTest.java | 35 +++++ 3 files changed, 135 insertions(+), 45 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java b/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java index f631a7a1c37..b828d94fcf5 100644 --- a/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java @@ -538,7 +538,6 @@ private BasicBlock retrieveNextBlock() { return nestedBlocks.pollFirst(); } } - return null; } diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 389e0a0ce3c..15b490668af 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -25,6 +25,7 @@ import java.util.*; import javax.annotation.Nonnull; +import sootup.core.graph.*; import sootup.core.jimple.basic.LValue; import sootup.core.jimple.basic.Local; import sootup.core.jimple.basic.Value; @@ -63,85 +64,140 @@ */ public class LocalSplitter implements BodyInterceptor { + private DominanceFinder dominanceFinder; + private Map> mostRecentDefBlock; + private Map originalToNewStmt; + @Override public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) { Set localsToSplit = findLocalsToSplit(builder); - Map mostRecentDef = new HashMap<>(); - Map originalToNewStmt = new HashMap<>(); - for (Local local : localsToSplit) { - mostRecentDef.put(local, 0); + init(localsToSplit, builder); + for (Stmt stmt : builder.getStmtGraph()) { + + handleDefs(builder, localsToSplit, stmt); + + handleUses(builder, localsToSplit, stmt); + } - int i = 0; - for (Stmt stmt : builder.getStmts()) { - List defs = stmt.getDefs(); - if(defs.size()==0){ + } + + private void init(Set localsToSplit, Body.BodyBuilder builder){ + originalToNewStmt = new HashMap<>(); + dominanceFinder = new DominanceFinder(builder.getStmtGraph()); + mostRecentDefBlock = new HashMap<>(); + Map, Integer> blockToIdx = dominanceFinder.getBlockToIdx(); + for (Integer val : blockToIdx.values()) { + Map localToDefId = new HashMap<>(); + for (Local local : localsToSplit) { + localToDefId.put(local, 0); // init to zero + } + mostRecentDefBlock.put(val, localToDefId); + } + } + + private int getMostRecentDefInPredBlock(Local local, StmtGraph stmtGraph, Stmt stmt){ + BasicBlock currentBlock = stmtGraph.getBlockOf(stmt); + List predBlocks = currentBlock.getPredecessors(); + if(!predBlocks.isEmpty()){ + BasicBlock predBlock = predBlocks.get(0); + Integer idx = dominanceFinder.getBlockToIdx().get(predBlock); + return mostRecentDefBlock.get(idx).get(local); + } + Integer currentBlockId = dominanceFinder.getBlockToIdx().get(currentBlock); + return mostRecentDefBlock.get(currentBlockId).get(local); // entry block + } + + private void putMostRecentDefInBlock(int id, Local local, StmtGraph stmtGraph, Stmt stmt){ + BasicBlock block = stmtGraph.getBlockOf(stmt); + Integer blockId = dominanceFinder.getBlockToIdx().get(block); + Map LocalToDefIdInBlock = mostRecentDefBlock.get(blockId); + if(LocalToDefIdInBlock!=null){ + LocalToDefIdInBlock.put(local, id); + }else{ + LocalToDefIdInBlock = new HashMap<>(); + LocalToDefIdInBlock.put(local, id); + } + } + + private void handleUses(Body.BodyBuilder builder, Set localsToSplit, Stmt stmt) { + for (Value use : stmt.getUses()) { + if (!(use instanceof Local)) { continue; } - if (defs.size()==1) { - LValue def = defs.get(0); - if (def instanceof Local) { - Local oldLocal = (Local) def; - if (localsToSplit.contains(oldLocal)) { - handleSelfUse(builder, stmt, oldLocal, mostRecentDef, originalToNewStmt); - Local newLocal = oldLocal.withName(oldLocal.getName() + '#' + (++i)); // renaming should not be done here - mostRecentDef.put(oldLocal, i); - builder.addLocal(newLocal); - Stmt toReplace; - if (originalToNewStmt.containsKey(stmt)) { - toReplace = originalToNewStmt.get(stmt); - } else { - toReplace = stmt; - } - Stmt withNewDef = new JAssignStmt(newLocal, toReplace.getUses().get(0), stmt.getPositionInfo()); - builder.getStmtGraph().replaceNode(toReplace, withNewDef); - originalToNewStmt.put(stmt, withNewDef); - } + Local oldLocalUse = (Local) use; + if (localsToSplit.contains(use)) { + Stmt toReplace; + if (originalToNewStmt.containsKey(stmt)) { + toReplace = originalToNewStmt.get(stmt); + } else { + toReplace = stmt; } - } else{ - throw new RuntimeException("stmt with more than 1 def!"); + int mostRecentDefId = getMostRecentDefInPredBlock(oldLocalUse, builder.getStmtGraph(), toReplace); + Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + mostRecentDefId); // use the most recent split name + Stmt withNewUse = toReplace.withNewUse(oldLocalUse, newLocal); + //Stmt withNewUse = new JAssignStmt(toReplace.getDefs().get(0), newLocal, stmt.getPositionInfo()); + builder.getStmtGraph().replaceNode(toReplace, withNewUse); + originalToNewStmt.put(stmt, withNewUse); } + } + } - for (Value use : stmt.getUses()) { - if (!(use instanceof Local)) { - continue; - } - Local oldLocalUse = (Local) use; - if (localsToSplit.contains(use)) { - Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + mostRecentDef.get(oldLocalUse)); // use the most recent split name + private void handleDefs(Body.BodyBuilder builder, Set localsToSplit, Stmt stmt) { + List defs = stmt.getDefs(); + if(defs.size()==0){ + return; + } + if (defs.size()==1) { + LValue def = defs.get(0); + if (def instanceof Local) { + Local oldLocal = (Local) def; + if (localsToSplit.contains(oldLocal)) { Stmt toReplace; if (originalToNewStmt.containsKey(stmt)) { toReplace = originalToNewStmt.get(stmt); } else { toReplace = stmt; } - Stmt withNewUse = toReplace.withNewUse(oldLocalUse, newLocal); - //Stmt withNewUse = new JAssignStmt(toReplace.getDefs().get(0), newLocal, stmt.getPositionInfo()); - builder.getStmtGraph().replaceNode(toReplace, withNewUse); - originalToNewStmt.put(stmt, withNewUse); + int id = getMostRecentDefInPredBlock(oldLocal, builder.getStmtGraph(), toReplace); + handleSelfUse(builder, stmt, oldLocal); + Local newLocal = oldLocal.withName(oldLocal.getName() + '#' + (++id)); // renaming should not be done here + putMostRecentDefInBlock(id, oldLocal, builder.getStmtGraph(), stmt); + builder.addLocal(newLocal); + + Stmt withNewDef = new JAssignStmt(newLocal, toReplace.getUses().get(0), stmt.getPositionInfo()); + builder.getStmtGraph().replaceNode(toReplace, withNewDef); + originalToNewStmt.put(stmt, withNewDef); } } - + } else{ + throw new RuntimeException("stmt with more than 1 def!"); } } - private void handleSelfUse(Body.BodyBuilder builder, Stmt stmt, Local def, Map mostRecentDef, Map originalToNewStmt) { + private void handleSelfUse(Body.BodyBuilder builder, Stmt stmt, Local def) { for (Value use : stmt.getUses()) { if(use.equals(def)){ Local oldLocalUse = (Local) use; - Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + mostRecentDef.get(oldLocalUse)); // use the most recent split name - Stmt withNewUse = stmt.withNewUse(oldLocalUse, newLocal); Stmt toReplace; if (originalToNewStmt.containsKey(stmt)) { toReplace = originalToNewStmt.get(stmt); } else { toReplace = stmt; } + int id = getMostRecentDefInPredBlock(oldLocalUse, builder.getStmtGraph(), toReplace); + Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + id); // use the most recent split name + Stmt withNewUse = stmt.withNewUse(oldLocalUse, newLocal); builder.getStmtGraph().replaceNode(toReplace, withNewUse); originalToNewStmt.put(stmt, withNewUse); } } } + /** + * Multiple defs of the same local are to split. + * @param builder + * @return + */ private Set findLocalsToSplit(Body.BodyBuilder builder) { Set visitedLocals = new LinkedHashSet<>(); Set localsToSplit = new LinkedHashSet<>(); diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index 882b29fd8e0..1459ea04954 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -1,6 +1,7 @@ package sootup.java.bytecode.interceptors; import categories.Java8Test; +import com.sun.jdi.IntegerType; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -12,6 +13,7 @@ import sootup.core.signatures.MethodSubSignature; import sootup.core.signatures.PackageName; import sootup.core.types.ClassType; +import sootup.core.types.PrimitiveType; import sootup.core.types.UnknownType; import sootup.core.types.VoidType; import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; @@ -102,5 +104,38 @@ public void testSelfAssignment() { assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } + @Test + public void testBranch() { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case2", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); + SootMethod sootMethod = view.getMethod(sig).get(); + Body originalBody = sootMethod.getBody(); + + List expectedLocals = new ArrayList<>(); + expectedLocals.addAll(originalBody.getLocals()); + expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + + Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); + LocalSplitter localSplitter = new LocalSplitter(); + localSplitter.interceptBody(builder, view); + + Body newBody = builder.build(); + //assertTrue(expectedLocals.containsAll(newBody.getLocals())); + //assertTrue(newBody.getLocals().containsAll(expectedLocals)); + + String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + + "$l1#1 = 0;\n" + + "$l2#2 = 1;\n" + + "$l1#3 = $l1#1 + 1;\n" + + "$l2#4 = $l2#2 + 1;\n" + + "\n" + + "return;"; + System.out.println(newBody.getStmtGraph()); + //assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + } + } From d7dab5fa909b95d838888e2abe0b53447e19499b Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Wed, 10 Jan 2024 18:10:57 +0100 Subject: [PATCH 04/24] wip --- .../bytecode/interceptors/LocalSplitter.java | 92 +++++++++++++++---- .../interceptors/LocalSplitterTest.java | 3 +- 2 files changed, 75 insertions(+), 20 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 15b490668af..d91740c16c0 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -23,12 +23,14 @@ */ import java.util.*; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import sootup.core.graph.*; import sootup.core.jimple.basic.LValue; import sootup.core.jimple.basic.Local; import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.stmt.BranchingStmt; import sootup.core.jimple.common.stmt.JAssignStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; @@ -60,12 +62,12 @@ * l2#4 = l2#2 + 1 * return * - * */ public class LocalSplitter implements BodyInterceptor { private DominanceFinder dominanceFinder; private Map> mostRecentDefBlock; + private int globalDefCounter; private Map originalToNewStmt; @Override @@ -81,7 +83,8 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) } } - private void init(Set localsToSplit, Body.BodyBuilder builder){ + private void init(Set localsToSplit, Body.BodyBuilder builder) { + globalDefCounter = 0; originalToNewStmt = new HashMap<>(); dominanceFinder = new DominanceFinder(builder.getStmtGraph()); mostRecentDefBlock = new HashMap<>(); @@ -95,25 +98,75 @@ private void init(Set localsToSplit, Body.BodyBuilder builder){ } } - private int getMostRecentDefInPredBlock(Local local, StmtGraph stmtGraph, Stmt stmt){ + + private int getMostRecentDefInPredBlock(Local local, Body.BodyBuilder builder, StmtGraph stmtGraph, Stmt stmt) { BasicBlock currentBlock = stmtGraph.getBlockOf(stmt); List predBlocks = currentBlock.getPredecessors(); - if(!predBlocks.isEmpty()){ + if (!predBlocks.isEmpty()) { + if(predBlocks.size()>0){ + BasicBlock firstPred = predBlocks.get(0); + Integer blockId = dominanceFinder.getBlockToIdx().get(firstPred); + Integer recentDefId = mostRecentDefBlock.get(blockId).get(local); + // update other branches + for (int i = 1; i < predBlocks.size(); i++) { + BasicBlock otherBlock = predBlocks.get(i); + Integer otherBlockId = dominanceFinder.getBlockToIdx().get(otherBlock); + Integer recentDefInOtherBlock = mostRecentDefBlock.get(blockId).get(local); + List stmtsInOtherBlock = otherBlock.getStmts(); + for (Stmt s : stmtsInOtherBlock) { + if(s.getDefs().size()==1){ + Local def = (Local) s.getDefs().get(0); + if(def.toString().equals(local.toString() + "#" + recentDefInOtherBlock)){ + Local newLocal = def.withName(def.getName() + '#' + recentDefId); // renaming should not be done here + putMostRecentDefInBlock(globalDefCounter, def, stmtGraph, s); + builder.addLocal(newLocal); // can omit + + Stmt withNewDef = new JAssignStmt(newLocal, s.getUses().get(0), s.getPositionInfo()); + builder.getStmtGraph().replaceNode(s, withNewDef); + originalToNewStmt.put(stmt, withNewDef); + } + } + } + } + } BasicBlock predBlock = predBlocks.get(0); - Integer idx = dominanceFinder.getBlockToIdx().get(predBlock); - return mostRecentDefBlock.get(idx).get(local); + Integer blockId = dominanceFinder.getBlockToIdx().get(predBlock); + return mostRecentDefBlock.get(blockId).get(local); } Integer currentBlockId = dominanceFinder.getBlockToIdx().get(currentBlock); return mostRecentDefBlock.get(currentBlockId).get(local); // entry block } - private void putMostRecentDefInBlock(int id, Local local, StmtGraph stmtGraph, Stmt stmt){ + private int getMostRecentDefInOtherBranch(Local local, StmtGraph stmtGraph, Stmt stmt) { + Iterator iter = stmtGraph.iterator(); + while (iter.hasNext()) { + Stmt curr = iter.next(); + if (curr instanceof BranchingStmt) { + List branchTargets = stmtGraph.getBranchTargetsOf((BranchingStmt) curr); + Stmt otherBranchHead = null; + if (branchTargets.contains(stmt)) { + Optional otherOpt = branchTargets.stream().filter(e -> !e.equals(stmt)).findFirst(); + if (otherOpt.isPresent()) { + otherBranchHead = otherOpt.get(); + } + } + if (otherBranchHead != null) { + BasicBlock otherBlock = stmtGraph.getBlockOf(otherBranchHead); + Integer otherBlockId = dominanceFinder.getBlockToIdx().get(otherBlock); + return mostRecentDefBlock.get(otherBlockId).get(local); + } + } + } + return -1; + } + + private void putMostRecentDefInBlock(int id, Local local, StmtGraph stmtGraph, Stmt stmt) { BasicBlock block = stmtGraph.getBlockOf(stmt); Integer blockId = dominanceFinder.getBlockToIdx().get(block); Map LocalToDefIdInBlock = mostRecentDefBlock.get(blockId); - if(LocalToDefIdInBlock!=null){ + if (LocalToDefIdInBlock != null) { LocalToDefIdInBlock.put(local, id); - }else{ + } else { LocalToDefIdInBlock = new HashMap<>(); LocalToDefIdInBlock.put(local, id); } @@ -144,39 +197,39 @@ private void handleUses(Body.BodyBuilder builder, Set localsToSplit, Stmt private void handleDefs(Body.BodyBuilder builder, Set localsToSplit, Stmt stmt) { List defs = stmt.getDefs(); - if(defs.size()==0){ + if (defs.size() == 0) { return; } - if (defs.size()==1) { + if (defs.size() == 1) { LValue def = defs.get(0); if (def instanceof Local) { Local oldLocal = (Local) def; if (localsToSplit.contains(oldLocal)) { + handleSelfUse(builder, stmt, oldLocal); Stmt toReplace; if (originalToNewStmt.containsKey(stmt)) { toReplace = originalToNewStmt.get(stmt); } else { toReplace = stmt; } - int id = getMostRecentDefInPredBlock(oldLocal, builder.getStmtGraph(), toReplace); - handleSelfUse(builder, stmt, oldLocal); - Local newLocal = oldLocal.withName(oldLocal.getName() + '#' + (++id)); // renaming should not be done here - putMostRecentDefInBlock(id, oldLocal, builder.getStmtGraph(), stmt); + //int id = getMostRecentDefInPredBlock(oldLocal, builder.getStmtGraph(), toReplace); + Local newLocal = oldLocal.withName(oldLocal.getName() + '#' + (++globalDefCounter)); // renaming should not be done here + putMostRecentDefInBlock(globalDefCounter, oldLocal, builder.getStmtGraph(), toReplace); builder.addLocal(newLocal); - Stmt withNewDef = new JAssignStmt(newLocal, toReplace.getUses().get(0), stmt.getPositionInfo()); + Stmt withNewDef = new JAssignStmt(newLocal, toReplace.getUses().get(0), toReplace.getPositionInfo()); builder.getStmtGraph().replaceNode(toReplace, withNewDef); originalToNewStmt.put(stmt, withNewDef); } } - } else{ + } else { throw new RuntimeException("stmt with more than 1 def!"); } } private void handleSelfUse(Body.BodyBuilder builder, Stmt stmt, Local def) { for (Value use : stmt.getUses()) { - if(use.equals(def)){ + if (use.equals(def)) { Local oldLocalUse = (Local) use; Stmt toReplace; if (originalToNewStmt.containsKey(stmt)) { @@ -186,7 +239,7 @@ private void handleSelfUse(Body.BodyBuilder builder, Stmt stmt, Local def) { } int id = getMostRecentDefInPredBlock(oldLocalUse, builder.getStmtGraph(), toReplace); Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + id); // use the most recent split name - Stmt withNewUse = stmt.withNewUse(oldLocalUse, newLocal); + Stmt withNewUse = toReplace.withNewUse(oldLocalUse, newLocal); builder.getStmtGraph().replaceNode(toReplace, withNewUse); originalToNewStmt.put(stmt, withNewUse); } @@ -195,6 +248,7 @@ private void handleSelfUse(Body.BodyBuilder builder, Stmt stmt, Local def) { /** * Multiple defs of the same local are to split. + * * @param builder * @return */ diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index 1459ea04954..81348b1d460 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -133,7 +133,8 @@ public void testBranch() { "$l2#4 = $l2#2 + 1;\n" + "\n" + "return;"; - System.out.println(newBody.getStmtGraph()); + System.out.println(originalBody); + System.out.println(newBody); //assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } From 96e9ddf97f21a6513bd3634dd5d4e48e4298a128 Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Thu, 11 Jan 2024 16:20:41 +0100 Subject: [PATCH 05/24] branch case --- .../bytecode/interceptors/LocalSplitter.java | 36 +++++++++++++------ .../interceptors/LocalSplitterTest.java | 25 ++++++++----- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index d91740c16c0..20ad4d1aa9f 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -101,29 +101,44 @@ private void init(Set localsToSplit, Body.BodyBuilder builder) { private int getMostRecentDefInPredBlock(Local local, Body.BodyBuilder builder, StmtGraph stmtGraph, Stmt stmt) { BasicBlock currentBlock = stmtGraph.getBlockOf(stmt); + Integer currentBlockId = dominanceFinder.getBlockToIdx().get(currentBlock); + Integer defId = mostRecentDefBlock.get(currentBlockId).get(local);// entry block + if(defId!=null && defId!=0){ // if it is not the first def in the block, it should find the defId in the current block + return defId; + } List predBlocks = currentBlock.getPredecessors(); if (!predBlocks.isEmpty()) { if(predBlocks.size()>0){ - BasicBlock firstPred = predBlocks.get(0); + List sortedPreds = new ArrayList<>(); + List blocksSorted = stmtGraph.getBlocksSorted(); + for (BasicBlock basicBlock : blocksSorted) { + if(predBlocks.contains(basicBlock)){ + sortedPreds.add(basicBlock); + } + } + BasicBlock firstPred = sortedPreds.get(0); Integer blockId = dominanceFinder.getBlockToIdx().get(firstPred); Integer recentDefId = mostRecentDefBlock.get(blockId).get(local); // update other branches - for (int i = 1; i < predBlocks.size(); i++) { - BasicBlock otherBlock = predBlocks.get(i); + for (int i = 1; i < sortedPreds.size(); i++) { + BasicBlock otherBlock = sortedPreds.get(i); Integer otherBlockId = dominanceFinder.getBlockToIdx().get(otherBlock); - Integer recentDefInOtherBlock = mostRecentDefBlock.get(blockId).get(local); + Integer recentDefInOtherBlock = mostRecentDefBlock.get(otherBlockId).get(local); List stmtsInOtherBlock = otherBlock.getStmts(); for (Stmt s : stmtsInOtherBlock) { if(s.getDefs().size()==1){ Local def = (Local) s.getDefs().get(0); if(def.toString().equals(local.toString() + "#" + recentDefInOtherBlock)){ - Local newLocal = def.withName(def.getName() + '#' + recentDefId); // renaming should not be done here - putMostRecentDefInBlock(globalDefCounter, def, stmtGraph, s); + String originalLocalName = def.toString().substring(0, def.toString().indexOf("#")); + String newName = originalLocalName + "#" + recentDefId; + Local newLocal = def.withName(newName); + Local original = def.withName(originalLocalName); + putMostRecentDefInBlock(recentDefId, original, stmtGraph, s); builder.addLocal(newLocal); // can omit Stmt withNewDef = new JAssignStmt(newLocal, s.getUses().get(0), s.getPositionInfo()); builder.getStmtGraph().replaceNode(s, withNewDef); - originalToNewStmt.put(stmt, withNewDef); + originalToNewStmt.put(s, withNewDef); } } } @@ -133,8 +148,7 @@ private int getMostRecentDefInPredBlock(Local local, Body.BodyBuilder builder, S Integer blockId = dominanceFinder.getBlockToIdx().get(predBlock); return mostRecentDefBlock.get(blockId).get(local); } - Integer currentBlockId = dominanceFinder.getBlockToIdx().get(currentBlock); - return mostRecentDefBlock.get(currentBlockId).get(local); // entry block + return -1; } private int getMostRecentDefInOtherBranch(Local local, StmtGraph stmtGraph, Stmt stmt) { @@ -185,7 +199,7 @@ private void handleUses(Body.BodyBuilder builder, Set localsToSplit, Stmt } else { toReplace = stmt; } - int mostRecentDefId = getMostRecentDefInPredBlock(oldLocalUse, builder.getStmtGraph(), toReplace); + int mostRecentDefId = getMostRecentDefInPredBlock(oldLocalUse, builder, builder.getStmtGraph(), toReplace); Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + mostRecentDefId); // use the most recent split name Stmt withNewUse = toReplace.withNewUse(oldLocalUse, newLocal); //Stmt withNewUse = new JAssignStmt(toReplace.getDefs().get(0), newLocal, stmt.getPositionInfo()); @@ -237,7 +251,7 @@ private void handleSelfUse(Body.BodyBuilder builder, Stmt stmt, Local def) { } else { toReplace = stmt; } - int id = getMostRecentDefInPredBlock(oldLocalUse, builder.getStmtGraph(), toReplace); + int id = getMostRecentDefInPredBlock(oldLocalUse, builder, builder.getStmtGraph(), toReplace); Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + id); // use the most recent split name Stmt withNewUse = toReplace.withNewUse(oldLocalUse, newLocal); builder.getStmtGraph().replaceNode(toReplace, withNewUse); diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index 81348b1d460..c973788449f 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -114,28 +114,35 @@ public void testBranch() { List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); LocalSplitter localSplitter = new LocalSplitter(); localSplitter.interceptBody(builder, view); Body newBody = builder.build(); - //assertTrue(expectedLocals.containsAll(newBody.getLocals())); - //assertTrue(newBody.getLocals().containsAll(expectedLocals)); + assertTrue(expectedLocals.containsAll(newBody.getLocals())); + assertTrue(newBody.getLocals().containsAll(expectedLocals)); String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + "$l1#1 = 0;\n" + - "$l2#2 = 1;\n" + - "$l1#3 = $l1#1 + 1;\n" + - "$l2#4 = $l2#2 + 1;\n" + "\n" + - "return;"; + "if $l1#1 >= 0 goto label1;\n" + + "$l1#2 = $l1#1 + 1;\n" + + "\n" + + "goto label2;\n" + + "\n" + + "label1:\n" + + "$l1#3 = $l1#1 - 1;\n" + + "$l1#2 = $l1#3 + 2;\n" + + "\n" + + "label2:\n" + + "return $l1#2;"; System.out.println(originalBody); System.out.println(newBody); - //assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } From a6c83fbb609a8b2e1a7bd9b673591cfe09496d4b Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Thu, 11 Jan 2024 17:06:14 +0100 Subject: [PATCH 06/24] more branches --- .../src/main/java/sootup/core/model/Body.java | 6 + .../bytecode/interceptors/LocalSplitter.java | 52 ++- .../interceptors/LocalSplitterTest.java | 333 ++++++++++++------ 3 files changed, 247 insertions(+), 144 deletions(-) diff --git a/sootup.core/src/main/java/sootup/core/model/Body.java b/sootup.core/src/main/java/sootup/core/model/Body.java index 30deb635bc2..0db40b65091 100644 --- a/sootup.core/src/main/java/sootup/core/model/Body.java +++ b/sootup.core/src/main/java/sootup/core/model/Body.java @@ -370,6 +370,12 @@ public BodyBuilder addLocal(@Nonnull Local local) { return this; } + @Nonnull + public BodyBuilder removeLocal(@Nonnull Local local) { + locals.remove(local); + return this; + } + public void replaceLocal(@Nonnull Local oldLocal, @Nonnull Local newLocal) { if (!locals.contains(oldLocal)) { throw new RuntimeException("The given old local: '" + oldLocal + "' is not in the body!"); diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 20ad4d1aa9f..6ada5f259e5 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -108,7 +108,24 @@ private int getMostRecentDefInPredBlock(Local local, Body.BodyBuilder builder, S } List predBlocks = currentBlock.getPredecessors(); if (!predBlocks.isEmpty()) { - if(predBlocks.size()>0){ + if(predBlocks.size()==1){ + BasicBlock predBlock = predBlocks.get(0); + Integer blockId = dominanceFinder.getBlockToIdx().get(predBlock); + Integer recentDefId = mostRecentDefBlock.get(blockId).get(local); + if(recentDefId==0){ // so no def in the pred Block, let's check uses + List stmts = predBlock.getStmts(); + for (Stmt stmt1 : stmts) { + List uses = stmt1.getUses(); + for (Value use : uses) { + if(use.toString().contains(local.getName())){ + return Integer.parseInt(use.toString().substring(use.toString().indexOf('#')+1)); + } + } + } + } + return recentDefId; + } + if(predBlocks.size()>1){ List sortedPreds = new ArrayList<>(); List blocksSorted = stmtGraph.getBlocksSorted(); for (BasicBlock basicBlock : blocksSorted) { @@ -128,14 +145,16 @@ private int getMostRecentDefInPredBlock(Local local, Body.BodyBuilder builder, S for (Stmt s : stmtsInOtherBlock) { if(s.getDefs().size()==1){ Local def = (Local) s.getDefs().get(0); - if(def.toString().equals(local.toString() + "#" + recentDefInOtherBlock)){ + if(def.toString().equals(local.toString() + '#' + recentDefInOtherBlock)){ String originalLocalName = def.toString().substring(0, def.toString().indexOf("#")); - String newName = originalLocalName + "#" + recentDefId; + String newName = originalLocalName + '#' + recentDefId; Local newLocal = def.withName(newName); Local original = def.withName(originalLocalName); putMostRecentDefInBlock(recentDefId, original, stmtGraph, s); - builder.addLocal(newLocal); // can omit - + builder.removeLocal(def); + if(globalDefCounter==recentDefInOtherBlock){ + globalDefCounter--; // because we will use that number again + } Stmt withNewDef = new JAssignStmt(newLocal, s.getUses().get(0), s.getPositionInfo()); builder.getStmtGraph().replaceNode(s, withNewDef); originalToNewStmt.put(s, withNewDef); @@ -151,29 +170,6 @@ private int getMostRecentDefInPredBlock(Local local, Body.BodyBuilder builder, S return -1; } - private int getMostRecentDefInOtherBranch(Local local, StmtGraph stmtGraph, Stmt stmt) { - Iterator iter = stmtGraph.iterator(); - while (iter.hasNext()) { - Stmt curr = iter.next(); - if (curr instanceof BranchingStmt) { - List branchTargets = stmtGraph.getBranchTargetsOf((BranchingStmt) curr); - Stmt otherBranchHead = null; - if (branchTargets.contains(stmt)) { - Optional otherOpt = branchTargets.stream().filter(e -> !e.equals(stmt)).findFirst(); - if (otherOpt.isPresent()) { - otherBranchHead = otherOpt.get(); - } - } - if (otherBranchHead != null) { - BasicBlock otherBlock = stmtGraph.getBlockOf(otherBranchHead); - Integer otherBlockId = dominanceFinder.getBlockToIdx().get(otherBlock); - return mostRecentDefBlock.get(otherBlockId).get(local); - } - } - } - return -1; - } - private void putMostRecentDefInBlock(int id, Local local, StmtGraph stmtGraph, Stmt stmt) { BasicBlock block = stmtGraph.getBlockOf(stmt); Integer blockId = dominanceFinder.getBlockToIdx().get(block); diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index c973788449f..13483e02a1c 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -29,121 +29,222 @@ @Category(Java8Test.class) public class LocalSplitterTest { - JavaView view; - - @Before - public void Setup() { - String classPath = "src/test/java/resources/interceptors"; - JavaClassPathAnalysisInputLocation inputLocation = new JavaClassPathAnalysisInputLocation(classPath); - view = new JavaView(inputLocation); - } - - @Test - public void testSimpleAssignment() { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case0", Collections.EMPTY_LIST, VoidType.getInstance())); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); - - List expectedLocals = new ArrayList<>(); - expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - - Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, view); - - Body newBody = builder.build(); - assertTrue(expectedLocals.containsAll(newBody.getLocals())); - assertTrue(newBody.getLocals().containsAll(expectedLocals)); - - String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + - "$l2#2 = 1;\n" + - "$l1#3 = $l2#2 + 1;\n" + - "$l2#4 = $l1#3 + 1;\n" + - "\n" + - "return;"; - - assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); - } - - @Test - public void testSelfAssignment() { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case1", Collections.EMPTY_LIST, VoidType.getInstance())); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); - - List expectedLocals = new ArrayList<>(); - expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - - Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, view); - - Body newBody = builder.build(); - assertTrue(expectedLocals.containsAll(newBody.getLocals())); - assertTrue(newBody.getLocals().containsAll(expectedLocals)); - - String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + - "$l2#2 = 1;\n" + - "$l1#3 = $l1#1 + 1;\n" + - "$l2#4 = $l2#2 + 1;\n" + - "\n" + - "return;"; - - assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); - } - - @Test - public void testBranch() { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case2", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); - - List expectedLocals = new ArrayList<>(); - expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - - Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, view); - - Body newBody = builder.build(); - assertTrue(expectedLocals.containsAll(newBody.getLocals())); - assertTrue(newBody.getLocals().containsAll(expectedLocals)); - - String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + - "\n" + - "if $l1#1 >= 0 goto label1;\n" + - "$l1#2 = $l1#1 + 1;\n" + - "\n" + - "goto label2;\n" + - "\n" + - "label1:\n" + - "$l1#3 = $l1#1 - 1;\n" + - "$l1#2 = $l1#3 + 2;\n" + - "\n" + - "label2:\n" + - "return $l1#2;"; - System.out.println(originalBody); - System.out.println(newBody); - assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); - } - + JavaView view; + + @Before + public void Setup() { + String classPath = "src/test/java/resources/interceptors"; + JavaClassPathAnalysisInputLocation inputLocation = new JavaClassPathAnalysisInputLocation(classPath); + view = new JavaView(inputLocation); + } + + @Test + public void testSimpleAssignment() { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case0", Collections.EMPTY_LIST, VoidType.getInstance())); + SootMethod sootMethod = view.getMethod(sig).get(); + Body originalBody = sootMethod.getBody(); + + List expectedLocals = new ArrayList<>(); + expectedLocals.addAll(originalBody.getLocals()); + expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + + Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); + LocalSplitter localSplitter = new LocalSplitter(); + localSplitter.interceptBody(builder, view); + + Body newBody = builder.build(); + assertTrue(expectedLocals.containsAll(newBody.getLocals())); + assertTrue(newBody.getLocals().containsAll(expectedLocals)); + + String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + + "$l1#1 = 0;\n" + + "$l2#2 = 1;\n" + + "$l1#3 = $l2#2 + 1;\n" + + "$l2#4 = $l1#3 + 1;\n" + + "\n" + + "return;"; + + assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + } + + @Test + public void testSelfAssignment() { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case1", Collections.EMPTY_LIST, VoidType.getInstance())); + SootMethod sootMethod = view.getMethod(sig).get(); + Body originalBody = sootMethod.getBody(); + + List expectedLocals = new ArrayList<>(); + expectedLocals.addAll(originalBody.getLocals()); + expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + + Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); + LocalSplitter localSplitter = new LocalSplitter(); + localSplitter.interceptBody(builder, view); + + Body newBody = builder.build(); + assertTrue(expectedLocals.containsAll(newBody.getLocals())); + assertTrue(newBody.getLocals().containsAll(expectedLocals)); + + String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + + "$l1#1 = 0;\n" + + "$l2#2 = 1;\n" + + "$l1#3 = $l1#1 + 1;\n" + + "$l2#4 = $l2#2 + 1;\n" + + "\n" + + "return;"; + + assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + } + + @Test + public void testBranch() { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case2", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); + SootMethod sootMethod = view.getMethod(sig).get(); + Body originalBody = sootMethod.getBody(); + + List expectedLocals = new ArrayList<>(); + expectedLocals.addAll(originalBody.getLocals()); + expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + + Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); + LocalSplitter localSplitter = new LocalSplitter(); + localSplitter.interceptBody(builder, view); + + Body newBody = builder.build(); + assertTrue(expectedLocals.containsAll(newBody.getLocals())); + assertTrue(newBody.getLocals().containsAll(expectedLocals)); + + String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + + "$l1#1 = 0;\n" + + "\n" + + "if $l1#1 >= 0 goto label1;\n" + + "$l1#2 = $l1#1 + 1;\n" + + "\n" + + "goto label2;\n" + + "\n" + + "label1:\n" + + "$l1#3 = $l1#1 - 1;\n" + + "$l1#2 = $l1#3 + 2;\n" + + "\n" + + "label2:\n" + + "return $l1#2;"; + assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + } + + + @Test + public void testBranchMoreLocals() { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case3", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); + SootMethod sootMethod = view.getMethod(sig).get(); + Body originalBody = sootMethod.getBody(); + + List expectedLocals = new ArrayList<>(); + expectedLocals.addAll(originalBody.getLocals()); + expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#6", UnknownType.getInstance(), NoPositionInformation.getInstance())); + + Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); + LocalSplitter localSplitter = new LocalSplitter(); + localSplitter.interceptBody(builder, view); + + Body newBody = builder.build(); + assertTrue(expectedLocals.containsAll(newBody.getLocals())); + assertTrue(newBody.getLocals().containsAll(expectedLocals)); + + String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + + "$l1#1 = 0;\n" + + "\n" + + "if $l1#1 >= 0 goto label1;\n" + + "$l1#2 = $l1#1 + 1;\n" + + "$l1#3 = $l1#2 + 2;\n" + + "$l1#4 = $l1#3 + 3;\n" + + "\n" + + "goto label2;\n" + + "\n" + + "label1:\n" + + "$l1#5 = $l1#1 - 1;\n" + + "$l1#6 = $l1#5 - 2;\n" + + "$l1#4 = $l1#6 - 3;\n" + + "\n" + + "label2:\n" + + "return $l1#4;"; + System.out.println(originalBody); + System.out.println(newBody); + assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + } + + + @Test + public void testBranchMoreBranches() { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case4", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); + SootMethod sootMethod = view.getMethod(sig).get(); + Body originalBody = sootMethod.getBody(); + + List expectedLocals = new ArrayList<>(); + expectedLocals.addAll(originalBody.getLocals()); + expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#6", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#7", UnknownType.getInstance(), NoPositionInformation.getInstance())); + + Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); + LocalSplitter localSplitter = new LocalSplitter(); + localSplitter.interceptBody(builder, view); + + Body newBody = builder.build(); + assertTrue(expectedLocals.containsAll(newBody.getLocals())); + assertTrue(newBody.getLocals().containsAll(expectedLocals)); + + String expectedStmts = + "$l0 := @this: LocalSplitterTarget;\n" + + "$l1#1 = 0;\n" + + "\n" + + "if $l1#1 >= 0 goto label1;\n" + + "$l1#2 = $l1#1 + 1;\n" + + "$l1#3 = $l1#2 + 2;\n" + + "\n" + + "goto label2;\n" + + "\n" + + "label1:\n" + + "$l1#4 = $l1#1 - 1;\n" + + "$l1#3 = $l1#4 - 2;\n" + + "\n" + + "label2:\n" + + "if $l1#3 <= 1 goto label3;\n" + + "$l1#5 = $l1#3 + 3;\n" + + "$l1#6 = $l1#5 + 5;\n" + + "\n" + + "goto label4;\n" + + "\n" + + "label3:\n" + + "$l1#7 = $l1#3 - 3;\n" + + "$l1#6 = $l1#7 - 5;\n" + + "\n" + + "label4:\n" + + "return $l1#6;"; + System.out.println(originalBody); + System.out.println(newBody); + assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + } } From d4afb074c41c652383bb1f4f0c0b7a71cece8b2d Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Sat, 13 Jan 2024 01:11:32 +0100 Subject: [PATCH 07/24] more consistent wip --- .../bytecode/interceptors/LocalSplitter.java | 325 ++++++------------ .../interceptors/LocalSplitterTest.java | 60 +++- 2 files changed, 171 insertions(+), 214 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 6ada5f259e5..99e39b8ab87 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -22,262 +22,165 @@ * #L% */ -import java.util.*; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; - -import sootup.core.graph.*; +import org.apache.commons.lang3.tuple.Pair; +import sootup.core.graph.BasicBlock; +import sootup.core.graph.DominanceFinder; +import sootup.core.graph.StmtGraph; import sootup.core.jimple.basic.LValue; import sootup.core.jimple.basic.Local; import sootup.core.jimple.basic.Value; -import sootup.core.jimple.common.stmt.BranchingStmt; import sootup.core.jimple.common.stmt.JAssignStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; import sootup.core.transform.BodyInterceptor; import sootup.core.views.View; -/** - * A BodyInterceptor that attempts to identify and separate uses of a local variable (definition) - * that are independent of each other. - * - *

For example the code: - * - *

- *    l0 := @this Test
- *    l1 = 0
- *    l2 = 1
- *    l1 = l1 + 1
- *    l2 = l2 + 1
- *    return
- * 
- *

- * to: - * - *

- *    l0 := @this Test
- *    l1#1 = 0
- *    l2#2 = 1
- *    l1#3 = l1#1 + 1
- *    l2#4 = l2#2 + 1
- *    return
- * 
- */ -public class LocalSplitter implements BodyInterceptor { - - private DominanceFinder dominanceFinder; - private Map> mostRecentDefBlock; - private int globalDefCounter; - private Map originalToNewStmt; - - @Override - public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) { - Set localsToSplit = findLocalsToSplit(builder); - init(localsToSplit, builder); - for (Stmt stmt : builder.getStmtGraph()) { - - handleDefs(builder, localsToSplit, stmt); +import javax.annotation.Nonnull; +import java.util.*; - handleUses(builder, localsToSplit, stmt); +public class LocalSplitter implements BodyInterceptor { - } - } + private Map>> stmtToUses; - private void init(Set localsToSplit, Body.BodyBuilder builder) { - globalDefCounter = 0; - originalToNewStmt = new HashMap<>(); - dominanceFinder = new DominanceFinder(builder.getStmtGraph()); - mostRecentDefBlock = new HashMap<>(); - Map, Integer> blockToIdx = dominanceFinder.getBlockToIdx(); - for (Integer val : blockToIdx.values()) { - Map localToDefId = new HashMap<>(); - for (Local local : localsToSplit) { - localToDefId.put(local, 0); // init to zero + /** + * Multiple defs of the same local are to split. + * + * @param builder + * @return + */ + private Set findLocalsToSplit(Body.BodyBuilder builder) { + Set visitedLocals = new LinkedHashSet<>(); + Set localsToSplit = new LinkedHashSet<>(); + for (Stmt stmt : builder.getStmts()) { + for (LValue def : stmt.getDefs()) { + if (def instanceof Local) { + if (visitedLocals.contains(def)) { + localsToSplit.add((Local) def); + } else { + visitedLocals.add((Local) def); + } + } } - mostRecentDefBlock.put(val, localToDefId); } + return localsToSplit; } - private int getMostRecentDefInPredBlock(Local local, Body.BodyBuilder builder, StmtGraph stmtGraph, Stmt stmt) { - BasicBlock currentBlock = stmtGraph.getBlockOf(stmt); - Integer currentBlockId = dominanceFinder.getBlockToIdx().get(currentBlock); - Integer defId = mostRecentDefBlock.get(currentBlockId).get(local);// entry block - if(defId!=null && defId!=0){ // if it is not the first def in the block, it should find the defId in the current block - return defId; - } - List predBlocks = currentBlock.getPredecessors(); - if (!predBlocks.isEmpty()) { - if(predBlocks.size()==1){ - BasicBlock predBlock = predBlocks.get(0); - Integer blockId = dominanceFinder.getBlockToIdx().get(predBlock); - Integer recentDefId = mostRecentDefBlock.get(blockId).get(local); - if(recentDefId==0){ // so no def in the pred Block, let's check uses - List stmts = predBlock.getStmts(); - for (Stmt stmt1 : stmts) { - List uses = stmt1.getUses(); - for (Value use : uses) { - if(use.toString().contains(local.getName())){ - return Integer.parseInt(use.toString().substring(use.toString().indexOf('#')+1)); - } - } - } + @Override + public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) { + Set localsToSplit = findLocalsToSplit(builder); + int w = 0; + + Set visited = new HashSet<>(); + for (int i = 0; i < builder.getStmts().size(); i++) { + Stmt stmt = builder.getStmts().get(i); + if (stmt.getDefs().size() == 1) { + LValue singleDef = stmt.getDefs().get(0); + if (!(singleDef instanceof Local) || visited.remove(stmt)) { + continue; } - return recentDefId; - } - if(predBlocks.size()>1){ - List sortedPreds = new ArrayList<>(); - List blocksSorted = stmtGraph.getBlocksSorted(); - for (BasicBlock basicBlock : blocksSorted) { - if(predBlocks.contains(basicBlock)){ - sortedPreds.add(basicBlock); - } + + Local oldLocal = (Local) singleDef; + if (!localsToSplit.contains(oldLocal)) { + continue; } - BasicBlock firstPred = sortedPreds.get(0); - Integer blockId = dominanceFinder.getBlockToIdx().get(firstPred); - Integer recentDefId = mostRecentDefBlock.get(blockId).get(local); - // update other branches - for (int i = 1; i < sortedPreds.size(); i++) { - BasicBlock otherBlock = sortedPreds.get(i); - Integer otherBlockId = dominanceFinder.getBlockToIdx().get(otherBlock); - Integer recentDefInOtherBlock = mostRecentDefBlock.get(otherBlockId).get(local); - List stmtsInOtherBlock = otherBlock.getStmts(); - for (Stmt s : stmtsInOtherBlock) { - if(s.getDefs().size()==1){ - Local def = (Local) s.getDefs().get(0); - if(def.toString().equals(local.toString() + '#' + recentDefInOtherBlock)){ - String originalLocalName = def.toString().substring(0, def.toString().indexOf("#")); - String newName = originalLocalName + '#' + recentDefId; - Local newLocal = def.withName(newName); - Local original = def.withName(originalLocalName); - putMostRecentDefInBlock(recentDefId, original, stmtGraph, s); - builder.removeLocal(def); - if(globalDefCounter==recentDefInOtherBlock){ - globalDefCounter--; // because we will use that number again + + Local newLocal = oldLocal.withName(oldLocal.getName() + "#" + (++w)); + builder.addLocal(newLocal); + + Deque queue = new ArrayDeque<>(); + queue.addFirst(stmt); + do { + Stmt head = queue.removeFirst(); + if (visited.add(head)) { + Set useStmts = usesUntilNewDef(builder, head); + for (Stmt useStmt : useStmts) { + for (Value use : useStmt.getUses()) { + if (use == newLocal) { + continue; + } + if (use instanceof Local) { + queue.addAll(getDefsBefore(oldLocal, builder, useStmt)); + addNewUse(builder, useStmt, newLocal, oldLocal); } - Stmt withNewDef = new JAssignStmt(newLocal, s.getUses().get(0), s.getPositionInfo()); - builder.getStmtGraph().replaceNode(s, withNewDef); - originalToNewStmt.put(s, withNewDef); + } + } + for (LValue def : head.getDefs()) { + if (def instanceof Local) { + int idx = builder.getStmts().indexOf(head); + addNewDef(builder, head, newLocal, oldLocal); + if (queue.contains(head)) { + + } + head = builder.getStmts().get(idx); } } } - } + System.out.println(builder.getStmtGraph()); + } while (!queue.isEmpty()); + + visited.remove(stmt); } - BasicBlock predBlock = predBlocks.get(0); - Integer blockId = dominanceFinder.getBlockToIdx().get(predBlock); - return mostRecentDefBlock.get(blockId).get(local); + } - return -1; + } - private void putMostRecentDefInBlock(int id, Local local, StmtGraph stmtGraph, Stmt stmt) { - BasicBlock block = stmtGraph.getBlockOf(stmt); - Integer blockId = dominanceFinder.getBlockToIdx().get(block); - Map LocalToDefIdInBlock = mostRecentDefBlock.get(blockId); - if (LocalToDefIdInBlock != null) { - LocalToDefIdInBlock.put(local, id); - } else { - LocalToDefIdInBlock = new HashMap<>(); - LocalToDefIdInBlock.put(local, id); + private void addNewDef(Body.BodyBuilder builder, Stmt stmt, Local newDef, Local oldDef) { + if (stmt.getDefs().contains(oldDef)) { + Stmt witNewDef = new JAssignStmt(newDef, stmt.getUses().get(0), stmt.getPositionInfo()); + builder.getStmtGraph().replaceNode(stmt, witNewDef); } } - private void handleUses(Body.BodyBuilder builder, Set localsToSplit, Stmt stmt) { - for (Value use : stmt.getUses()) { - if (!(use instanceof Local)) { - continue; - } - Local oldLocalUse = (Local) use; - if (localsToSplit.contains(use)) { - Stmt toReplace; - if (originalToNewStmt.containsKey(stmt)) { - toReplace = originalToNewStmt.get(stmt); - } else { - toReplace = stmt; - } - int mostRecentDefId = getMostRecentDefInPredBlock(oldLocalUse, builder, builder.getStmtGraph(), toReplace); - Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + mostRecentDefId); // use the most recent split name - Stmt withNewUse = toReplace.withNewUse(oldLocalUse, newLocal); - //Stmt withNewUse = new JAssignStmt(toReplace.getDefs().get(0), newLocal, stmt.getPositionInfo()); - builder.getStmtGraph().replaceNode(toReplace, withNewUse); - originalToNewStmt.put(stmt, withNewUse); - } + private void addNewUse(Body.BodyBuilder builder, Stmt stmt, Local newLocal, Local old) { + if (stmt.getUses().contains(old)) { + Stmt withNewUse = stmt.withNewUse(old, newLocal); + builder.getStmtGraph().replaceNode(stmt, withNewUse); } } - private void handleDefs(Body.BodyBuilder builder, Set localsToSplit, Stmt stmt) { + private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { + Set usesUntilNewDef = new HashSet<>(); List defs = stmt.getDefs(); - if (defs.size() == 0) { - return; - } if (defs.size() == 1) { - LValue def = defs.get(0); - if (def instanceof Local) { - Local oldLocal = (Local) def; - if (localsToSplit.contains(oldLocal)) { - handleSelfUse(builder, stmt, oldLocal); - Stmt toReplace; - if (originalToNewStmt.containsKey(stmt)) { - toReplace = originalToNewStmt.get(stmt); - } else { - toReplace = stmt; + Local def = (Local) defs.get(0); + Deque queue = new ArrayDeque<>(); + queue.add(builder.getStmts().get(0)); + while (!queue.isEmpty()) { + Stmt s = queue.removeFirst(); + if (s.getUses().contains(def)) { + usesUntilNewDef.add(s); + } + if (!stmt.equals(s)) { + if (s.getDefs().contains(def)) { + continue; } - //int id = getMostRecentDefInPredBlock(oldLocal, builder.getStmtGraph(), toReplace); - Local newLocal = oldLocal.withName(oldLocal.getName() + '#' + (++globalDefCounter)); // renaming should not be done here - putMostRecentDefInBlock(globalDefCounter, oldLocal, builder.getStmtGraph(), toReplace); - builder.addLocal(newLocal); - - Stmt withNewDef = new JAssignStmt(newLocal, toReplace.getUses().get(0), toReplace.getPositionInfo()); - builder.getStmtGraph().replaceNode(toReplace, withNewDef); - originalToNewStmt.put(stmt, withNewDef); } + queue.addAll(builder.getStmtGraph().successors(s)); } - } else { - throw new RuntimeException("stmt with more than 1 def!"); } + return usesUntilNewDef; } - private void handleSelfUse(Body.BodyBuilder builder, Stmt stmt, Local def) { - for (Value use : stmt.getUses()) { - if (use.equals(def)) { - Local oldLocalUse = (Local) use; - Stmt toReplace; - if (originalToNewStmt.containsKey(stmt)) { - toReplace = originalToNewStmt.get(stmt); - } else { - toReplace = stmt; + private List getDefsBefore(Local local, Body.BodyBuilder builder, Stmt until) { + List defsBefore = new ArrayList<>(); + Deque queue = new ArrayDeque<>(); + queue.addAll(builder.getStmtGraph().predecessors(until)); + while (!queue.isEmpty()) { + Stmt s = queue.removeFirst(); + + if (s.getDefs().contains(local)) { + defsBefore.add(s); + if (queue.size() - defsBefore.size() >= 0) { + continue; } - int id = getMostRecentDefInPredBlock(oldLocalUse, builder, builder.getStmtGraph(), toReplace); - Local newLocal = oldLocalUse.withName(oldLocalUse.getName() + '#' + id); // use the most recent split name - Stmt withNewUse = toReplace.withNewUse(oldLocalUse, newLocal); - builder.getStmtGraph().replaceNode(toReplace, withNewUse); - originalToNewStmt.put(stmt, withNewUse); } - } - } - /** - * Multiple defs of the same local are to split. - * - * @param builder - * @return - */ - private Set findLocalsToSplit(Body.BodyBuilder builder) { - Set visitedLocals = new LinkedHashSet<>(); - Set localsToSplit = new LinkedHashSet<>(); - for (Stmt stmt : builder.getStmts()) { - for (LValue def : stmt.getDefs()) { - if (def instanceof Local) { - if (visitedLocals.contains(def)) { - localsToSplit.add((Local) def); - } else { - visitedLocals.add((Local) def); - } - } - } + queue.addAll(builder.getStmtGraph().predecessors(s)); } - return localsToSplit; + return defsBefore; } -} +} \ No newline at end of file diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index 13483e02a1c..486febf32e4 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -184,8 +184,6 @@ public void testBranchMoreLocals() { "\n" + "label2:\n" + "return $l1#4;"; - System.out.println(originalBody); - System.out.println(newBody); assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } @@ -242,9 +240,65 @@ public void testBranchMoreBranches() { "\n" + "label4:\n" + "return $l1#6;"; + assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + } + + + @Test + public void testBranchElseIf() { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case5", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); + SootMethod sootMethod = view.getMethod(sig).get(); + Body originalBody = sootMethod.getBody(); + + List expectedLocals = new ArrayList<>(); + expectedLocals.addAll(originalBody.getLocals()); + expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#6", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#7", UnknownType.getInstance(), NoPositionInformation.getInstance())); + + Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); + LocalSplitter localSplitter = new LocalSplitter(); + localSplitter.interceptBody(builder, view); + + Body newBody = builder.build(); +// assertTrue(expectedLocals.containsAll(newBody.getLocals())); +// assertTrue(newBody.getLocals().containsAll(expectedLocals)); + + String expectedStmts = + "$l0 := @this: LocalSplitterTarget;\n" + + "$l1#1 = 0;\n" + + "\n" + + "if $l1#1 >= 0 goto label1;\n" + + "$l1#2 = $l1#1 + 1;\n" + + "$l1#3 = $l1#2 + 2;\n" + + "\n" + + "goto label2;\n" + + "\n" + + "label1:\n" + + "$l1#4 = $l1#1 - 1;\n" + + "$l1#3 = $l1#4 - 2;\n" + + "\n" + + "label2:\n" + + "if $l1#3 <= 1 goto label3;\n" + + "$l1#5 = $l1#3 + 3;\n" + + "$l1#6 = $l1#5 + 5;\n" + + "\n" + + "goto label4;\n" + + "\n" + + "label3:\n" + + "$l1#7 = $l1#3 - 3;\n" + + "$l1#6 = $l1#7 - 5;\n" + + "\n" + + "label4:\n" + + "return $l1#6;"; System.out.println(originalBody); System.out.println(newBody); - assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); +// assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } } From 34b3e8720089ea9db3d9405458174b6fd8cdbd6a Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Mon, 15 Jan 2024 17:25:49 +0100 Subject: [PATCH 08/24] else-if --- .../bytecode/interceptors/LocalSplitter.java | 24 +++--- .../interceptors/LocalSplitterTest.java | 78 +++++++++---------- 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 99e39b8ab87..32737914b3a 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -116,7 +116,6 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View vi } } } - System.out.println(builder.getStmtGraph()); } while (!queue.isEmpty()); visited.remove(stmt); @@ -165,19 +164,20 @@ private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { private List getDefsBefore(Local local, Body.BodyBuilder builder, Stmt until) { List defsBefore = new ArrayList<>(); - Deque queue = new ArrayDeque<>(); - queue.addAll(builder.getStmtGraph().predecessors(until)); - while (!queue.isEmpty()) { - Stmt s = queue.removeFirst(); - - if (s.getDefs().contains(local)) { - defsBefore.add(s); - if (queue.size() - defsBefore.size() >= 0) { - continue; + BasicBlock block = builder.getStmtGraph().getBlockOf(until); + List predBlocks = (List) block.getPredecessors(); + for (BasicBlock predBlock : predBlocks) { + Deque queue = new ArrayDeque<>(); + queue.add(predBlock.getTail()); + while (!queue.isEmpty()) { + Stmt s = queue.removeFirst(); + + if (s.getDefs().contains(local)) { + defsBefore.add(s); + break; } + queue.addAll(builder.getStmtGraph().predecessors(s)); } - - queue.addAll(builder.getStmtGraph().predecessors(s)); } return defsBefore; } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index 486febf32e4..9d0f36eb3c3 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -215,31 +215,31 @@ public void testBranchMoreBranches() { String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + - "\n" + - "if $l1#1 >= 0 goto label1;\n" + - "$l1#2 = $l1#1 + 1;\n" + - "$l1#3 = $l1#2 + 2;\n" + - "\n" + - "goto label2;\n" + - "\n" + - "label1:\n" + - "$l1#4 = $l1#1 - 1;\n" + - "$l1#3 = $l1#4 - 2;\n" + - "\n" + - "label2:\n" + - "if $l1#3 <= 1 goto label3;\n" + - "$l1#5 = $l1#3 + 3;\n" + - "$l1#6 = $l1#5 + 5;\n" + - "\n" + - "goto label4;\n" + - "\n" + - "label3:\n" + - "$l1#7 = $l1#3 - 3;\n" + - "$l1#6 = $l1#7 - 5;\n" + - "\n" + - "label4:\n" + - "return $l1#6;"; + "$l1#1 = 0;\n" + + "\n" + + "if $l1#1 >= 0 goto label1;\n" + + "$l1#2 = $l1#1 + 1;\n" + + "$l1#3 = $l1#2 + 2;\n" + + "\n" + + "goto label2;\n" + + "\n" + + "label1:\n" + + "$l1#4 = $l1#1 - 1;\n" + + "$l1#3 = $l1#4 - 2;\n" + + "\n" + + "label2:\n" + + "if $l1#3 <= 1 goto label3;\n" + + "$l1#5 = $l1#3 + 3;\n" + + "$l1#6 = $l1#5 + 5;\n" + + "\n" + + "goto label4;\n" + + "\n" + + "label3:\n" + + "$l1#7 = $l1#3 - 3;\n" + + "$l1#6 = $l1#7 - 5;\n" + + "\n" + + "label4:\n" + + "return $l1#6;"; assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } @@ -258,16 +258,14 @@ public void testBranchElseIf() { expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); expectedLocals.add(new Local("$l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#6", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#7", UnknownType.getInstance(), NoPositionInformation.getInstance())); Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); LocalSplitter localSplitter = new LocalSplitter(); localSplitter.interceptBody(builder, view); Body newBody = builder.build(); -// assertTrue(expectedLocals.containsAll(newBody.getLocals())); -// assertTrue(newBody.getLocals().containsAll(expectedLocals)); + assertTrue(expectedLocals.containsAll(newBody.getLocals())); + assertTrue(newBody.getLocals().containsAll(expectedLocals)); String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + @@ -277,28 +275,22 @@ public void testBranchElseIf() { "$l1#2 = $l1#1 + 1;\n" + "$l1#3 = $l1#2 + 2;\n" + "\n" + - "goto label2;\n" + + "goto label3;\n" + "\n" + "label1:\n" + + "if $l1#1 >= 5 goto label2;\n" + "$l1#4 = $l1#1 - 1;\n" + "$l1#3 = $l1#4 - 2;\n" + "\n" + - "label2:\n" + - "if $l1#3 <= 1 goto label3;\n" + - "$l1#5 = $l1#3 + 3;\n" + - "$l1#6 = $l1#5 + 5;\n" + + "goto label3;\n" + "\n" + - "goto label4;\n" + + "label2:\n" + + "$l1#5 = $l1#1 * 1;\n" + + "$l1#3 = $l1#5 * 2;\n" + "\n" + "label3:\n" + - "$l1#7 = $l1#3 - 3;\n" + - "$l1#6 = $l1#7 - 5;\n" + - "\n" + - "label4:\n" + - "return $l1#6;"; - System.out.println(originalBody); - System.out.println(newBody); -// assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + "return $l1#3;"; + assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } } From af48e3207d0f8a21e111d7f07e61b4c48bffb50d Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Mon, 15 Jan 2024 22:22:11 +0100 Subject: [PATCH 09/24] wip loop --- .../bytecode/interceptors/LocalSplitter.java | 81 ++++++++++++++----- .../interceptors/LocalSplitterTest.java | 55 +++++++++++++ 2 files changed, 116 insertions(+), 20 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 32737914b3a..07f0352dce8 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -23,9 +23,7 @@ */ import org.apache.commons.lang3.tuple.Pair; -import sootup.core.graph.BasicBlock; -import sootup.core.graph.DominanceFinder; -import sootup.core.graph.StmtGraph; +import sootup.core.graph.*; import sootup.core.jimple.basic.LValue; import sootup.core.jimple.basic.Local; import sootup.core.jimple.basic.Value; @@ -42,6 +40,8 @@ public class LocalSplitter implements BodyInterceptor { private Map>> stmtToUses; + private Map oldToNewStmt; + /** * Multiple defs of the same local are to split. * @@ -68,6 +68,8 @@ private Set findLocalsToSplit(Body.BodyBuilder builder) { @Override public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) { + oldToNewStmt = new HashMap<>(); + Set localsToSplit = findLocalsToSplit(builder); int w = 0; @@ -108,14 +110,16 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View vi for (LValue def : head.getDefs()) { if (def instanceof Local) { int idx = builder.getStmts().indexOf(head); - addNewDef(builder, head, newLocal, oldLocal); - if (queue.contains(head)) { - + if(idx==-1){ + Stmt newStmt = oldToNewStmt.get(head); + idx = builder.getStmts().indexOf(newStmt); } + addNewDef(builder, head, newLocal, oldLocal); head = builder.getStmts().get(idx); } } } + System.out.println(builder.getStmtGraph()); } while (!queue.isEmpty()); visited.remove(stmt); @@ -127,8 +131,14 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View vi private void addNewDef(Body.BodyBuilder builder, Stmt stmt, Local newDef, Local oldDef) { if (stmt.getDefs().contains(oldDef)) { - Stmt witNewDef = new JAssignStmt(newDef, stmt.getUses().get(0), stmt.getPositionInfo()); - builder.getStmtGraph().replaceNode(stmt, witNewDef); + Stmt withNewDef = new JAssignStmt(newDef, stmt.getUses().get(0), stmt.getPositionInfo()); + if(builder.getStmtGraph().containsNode(stmt)){ + builder.getStmtGraph().replaceNode(stmt, withNewDef); + }else if(builder.getStmtGraph().containsNode(oldToNewStmt.get(stmt))){ + // we cannot update the references from oldStmt to newStmt unlike soot. + builder.getStmtGraph().replaceNode(oldToNewStmt.get(stmt), withNewDef); + } + oldToNewStmt.put(stmt, withNewDef); } } @@ -136,6 +146,7 @@ private void addNewUse(Body.BodyBuilder builder, Stmt stmt, Local newLocal, Loca if (stmt.getUses().contains(old)) { Stmt withNewUse = stmt.withNewUse(old, newLocal); builder.getStmtGraph().replaceNode(stmt, withNewUse); + oldToNewStmt.put(stmt, withNewUse); } } @@ -149,7 +160,9 @@ private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { while (!queue.isEmpty()) { Stmt s = queue.removeFirst(); if (s.getUses().contains(def)) { - usesUntilNewDef.add(s); + if(!usesUntilNewDef.add(s)){ + break; // if it is already there maybe we started looping? + } } if (!stmt.equals(s)) { if (s.getDefs().contains(def)) { @@ -162,25 +175,53 @@ private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { return usesUntilNewDef; } - private List getDefsBefore(Local local, Body.BodyBuilder builder, Stmt until) { + private List getDefsBefore(Local local, Body.BodyBuilder builder, Stmt mStmt) { List defsBefore = new ArrayList<>(); - BasicBlock block = builder.getStmtGraph().getBlockOf(until); - List predBlocks = (List) block.getPredecessors(); - for (BasicBlock predBlock : predBlocks) { - Deque queue = new ArrayDeque<>(); - queue.add(predBlock.getTail()); - while (!queue.isEmpty()) { - Stmt s = queue.removeFirst(); - + Set visited = new HashSet<>(); + BasicBlock block = builder.getStmtGraph().getBlockOf(mStmt); + Deque blockQueue = new ArrayDeque<>(); + blockQueue.addFirst(block); + boolean firstPass = true; + do { + block = blockQueue.removeFirst(); + + if (firstPass) { + ;; // to visit the same block once more when looping + } else if (!visited.add(block)) { + continue; + } + Deque stmtQueue = new ArrayDeque<>(); + if (firstPass) { + stmtQueue.addAll(predsInSameBlock(builder, mStmt)); + firstPass = false; + } else { + stmtQueue.add(block.getTail()); + } + while (!stmtQueue.isEmpty()) { + Stmt s = stmtQueue.removeFirst(); if (s.getDefs().contains(local)) { defsBefore.add(s); break; } - queue.addAll(builder.getStmtGraph().predecessors(s)); + // iterate only in the same block here. + stmtQueue.addAll(predsInSameBlock(builder, s)); } - } + blockQueue.addAll(block.getPredecessors()); + } while (!blockQueue.isEmpty()); return defsBefore; } + private List predsInSameBlock(Body.BodyBuilder builder, Stmt stmt) { + List predsInSameBlock = new ArrayList<>(); + MutableStmtGraph stmtGraph = builder.getStmtGraph(); + List preds = stmtGraph.predecessors(stmt); + for (Stmt pred : preds) { + if (stmtGraph.getBlockOf(pred).equals(stmtGraph.getBlockOf(stmt))) { + predsInSameBlock.add(pred); + } + } + return predsInSameBlock; + } + } \ No newline at end of file diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index 9d0f36eb3c3..74e833f0080 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -3,6 +3,7 @@ import categories.Java8Test; import com.sun.jdi.IntegerType; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import sootup.core.jimple.basic.Local; @@ -293,4 +294,58 @@ public void testBranchElseIf() { assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } + + @Test + public void testForLoop() { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case6", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); + SootMethod sootMethod = view.getMethod(sig).get(); + Body originalBody = sootMethod.getBody(); + + List expectedLocals = new ArrayList<>(); + expectedLocals.addAll(originalBody.getLocals()); + expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("$l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); + + Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); + LocalSplitter localSplitter = new LocalSplitter(); + localSplitter.interceptBody(builder, view); + + + Body newBody = builder.build(); + + System.out.println(newBody); + +// assertTrue(expectedLocals.containsAll(newBody.getLocals())); +// assertTrue(newBody.getLocals().containsAll(expectedLocals)); + + String expectedStmts = + "$l0 := @this: LocalSplitterTarget;\n" + + "$l1#1 = 0;\n" + + "\n" + + "if $l1#1 >= 0 goto label1;\n" + + "$l1#2 = $l1#1 + 1;\n" + + "$l1#3 = $l1#2 + 2;\n" + + "\n" + + "goto label3;\n" + + "\n" + + "label1:\n" + + "if $l1#1 >= 5 goto label2;\n" + + "$l1#4 = $l1#1 - 1;\n" + + "$l1#3 = $l1#4 - 2;\n" + + "\n" + + "goto label3;\n" + + "\n" + + "label2:\n" + + "$l1#5 = $l1#1 * 1;\n" + + "$l1#3 = $l1#5 * 2;\n" + + "\n" + + "label3:\n" + + "return $l1#3;"; +// assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); + } + } From b4693dc416da3c492a6fb1e7bd8259e06a18df4e Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Tue, 16 Jan 2024 23:28:15 +0100 Subject: [PATCH 10/24] wip refbox --- .../bytecode/interceptors/LocalSplitter.java | 93 ++++++++++--------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 07f0352dce8..b571fa2f322 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -35,12 +35,28 @@ import javax.annotation.Nonnull; import java.util.*; +import java.util.stream.Collectors; public class LocalSplitter implements BodyInterceptor { private Map>> stmtToUses; - private Map oldToNewStmt; + private Map newToOriginalStmt; + + + static class RefBox { + Stmt stmt; + + public RefBox(Stmt stmt) { + this.stmt = stmt; + } + + @Override + public String toString() { + return stmt.toString(); + } + } + /** * Multiple defs of the same local are to split. @@ -68,17 +84,18 @@ private Set findLocalsToSplit(Body.BodyBuilder builder) { @Override public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) { - oldToNewStmt = new HashMap<>(); + newToOriginalStmt = new HashMap<>(); Set localsToSplit = findLocalsToSplit(builder); int w = 0; - Set visited = new HashSet<>(); + Set visited = new HashSet<>(); for (int i = 0; i < builder.getStmts().size(); i++) { Stmt stmt = builder.getStmts().get(i); - if (stmt.getDefs().size() == 1) { - LValue singleDef = stmt.getDefs().get(0); - if (!(singleDef instanceof Local) || visited.remove(stmt)) { + RefBox ref = new RefBox(stmt); + if (ref.stmt.getDefs().size() == 1) { + LValue singleDef = ref.stmt.getDefs().get(0); + if (!(singleDef instanceof Local) || visited.remove(ref.stmt)) { continue; } @@ -90,67 +107,56 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View vi Local newLocal = oldLocal.withName(oldLocal.getName() + "#" + (++w)); builder.addLocal(newLocal); - Deque queue = new ArrayDeque<>(); - queue.addFirst(stmt); + Deque queue = new ArrayDeque<>(); + queue.addFirst(ref); do { - Stmt head = queue.removeFirst(); + RefBox head = queue.removeFirst(); if (visited.add(head)) { - Set useStmts = usesUntilNewDef(builder, head); - for (Stmt useStmt : useStmts) { - for (Value use : useStmt.getUses()) { + Set useStmts = usesUntilNewDef(builder, head.stmt); + for (RefBox useRef : useStmts) { + for (Value use : useRef.stmt.getUses()) { if (use == newLocal) { continue; } if (use instanceof Local) { - queue.addAll(getDefsBefore(oldLocal, builder, useStmt)); - addNewUse(builder, useStmt, newLocal, oldLocal); + queue.addAll(getDefsBefore(oldLocal, builder, useRef.stmt)); + addNewUse(builder, useRef, newLocal, oldLocal); } } } - for (LValue def : head.getDefs()) { + for (LValue def : head.stmt.getDefs()) { if (def instanceof Local) { - int idx = builder.getStmts().indexOf(head); - if(idx==-1){ - Stmt newStmt = oldToNewStmt.get(head); - idx = builder.getStmts().indexOf(newStmt); - } addNewDef(builder, head, newLocal, oldLocal); - head = builder.getStmts().get(idx); } } } System.out.println(builder.getStmtGraph()); } while (!queue.isEmpty()); - visited.remove(stmt); + visited.remove(ref); } } } - private void addNewDef(Body.BodyBuilder builder, Stmt stmt, Local newDef, Local oldDef) { - if (stmt.getDefs().contains(oldDef)) { - Stmt withNewDef = new JAssignStmt(newDef, stmt.getUses().get(0), stmt.getPositionInfo()); - if(builder.getStmtGraph().containsNode(stmt)){ - builder.getStmtGraph().replaceNode(stmt, withNewDef); - }else if(builder.getStmtGraph().containsNode(oldToNewStmt.get(stmt))){ - // we cannot update the references from oldStmt to newStmt unlike soot. - builder.getStmtGraph().replaceNode(oldToNewStmt.get(stmt), withNewDef); - } - oldToNewStmt.put(stmt, withNewDef); + private void addNewDef(Body.BodyBuilder builder, RefBox ref, Local newDef, Local oldDef) { + if (ref.stmt.getDefs().contains(oldDef)) { + Stmt withNewDef = new JAssignStmt(newDef, ref.stmt.getUses().get(0), ref.stmt.getPositionInfo()); + builder.getStmtGraph().replaceNode(ref.stmt, withNewDef); + ref.stmt = withNewDef; } } - private void addNewUse(Body.BodyBuilder builder, Stmt stmt, Local newLocal, Local old) { - if (stmt.getUses().contains(old)) { - Stmt withNewUse = stmt.withNewUse(old, newLocal); - builder.getStmtGraph().replaceNode(stmt, withNewUse); - oldToNewStmt.put(stmt, withNewUse); + private void addNewUse(Body.BodyBuilder builder, RefBox ref, Local newLocal, Local old) { + if (ref.stmt.getUses().contains(old)) { + Stmt withNewUse = ref.stmt.withNewUse(old, newLocal); + builder.getStmtGraph().replaceNode(ref.stmt, withNewUse); + ref.stmt = withNewUse; } } - private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { + private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { Set usesUntilNewDef = new HashSet<>(); List defs = stmt.getDefs(); if (defs.size() == 1) { @@ -160,7 +166,7 @@ private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { while (!queue.isEmpty()) { Stmt s = queue.removeFirst(); if (s.getUses().contains(def)) { - if(!usesUntilNewDef.add(s)){ + if (!usesUntilNewDef.add(s)) { break; // if it is already there maybe we started looping? } } @@ -172,10 +178,10 @@ private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { queue.addAll(builder.getStmtGraph().successors(s)); } } - return usesUntilNewDef; + return usesUntilNewDef.stream().map(e -> new RefBox(e)).collect(Collectors.toSet()); } - private List getDefsBefore(Local local, Body.BodyBuilder builder, Stmt mStmt) { + private List getDefsBefore(Local local, Body.BodyBuilder builder, Stmt mStmt) { List defsBefore = new ArrayList<>(); Set visited = new HashSet<>(); BasicBlock block = builder.getStmtGraph().getBlockOf(mStmt); @@ -186,7 +192,8 @@ private List getDefsBefore(Local local, Body.BodyBuilder builder, Stmt mSt block = blockQueue.removeFirst(); if (firstPass) { - ;; // to visit the same block once more when looping + ; + ; // to visit the same block once more when looping } else if (!visited.add(block)) { continue; } @@ -208,7 +215,7 @@ private List getDefsBefore(Local local, Body.BodyBuilder builder, Stmt mSt } blockQueue.addAll(block.getPredecessors()); } while (!blockQueue.isEmpty()); - return defsBefore; + return defsBefore.stream().map(e -> new RefBox(e)).collect(Collectors.toList()); } private List predsInSameBlock(Body.BodyBuilder builder, Stmt stmt) { From 1f2eb3eec3eb4b24ef2bac9a59e2e2f82482468c Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Wed, 17 Jan 2024 20:46:45 +0100 Subject: [PATCH 11/24] stmtToUses & refbox fixes --- .../bytecode/interceptors/LocalSplitter.java | 101 ++++++++++++++-- .../interceptors/LocalSplitterTest.java | 110 ++++++++++++------ 2 files changed, 170 insertions(+), 41 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index b571fa2f322..b8d00239521 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -22,7 +22,9 @@ * #L% */ +import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; +import org.checkerframework.checker.units.qual.A; import sootup.core.graph.*; import sootup.core.jimple.basic.LValue; import sootup.core.jimple.basic.Local; @@ -55,6 +57,7 @@ public RefBox(Stmt stmt) { public String toString() { return stmt.toString(); } + } @@ -85,6 +88,7 @@ private Set findLocalsToSplit(Body.BodyBuilder builder) { @Override public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) { newToOriginalStmt = new HashMap<>(); + getStmtToUses(builder); Set localsToSplit = findLocalsToSplit(builder); int w = 0; @@ -95,7 +99,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View vi RefBox ref = new RefBox(stmt); if (ref.stmt.getDefs().size() == 1) { LValue singleDef = ref.stmt.getDefs().get(0); - if (!(singleDef instanceof Local) || visited.remove(ref.stmt)) { + if (!(singleDef instanceof Local) || visited.remove(ref)) { continue; } @@ -111,7 +115,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View vi queue.addFirst(ref); do { RefBox head = queue.removeFirst(); - if (visited.add(head)) { + if (addVisited(visited, head)) { Set useStmts = usesUntilNewDef(builder, head.stmt); for (RefBox useRef : useStmts) { for (Value use : useRef.stmt.getUses()) { @@ -120,42 +124,74 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View vi } if (use instanceof Local) { queue.addAll(getDefsBefore(oldLocal, builder, useRef.stmt)); - addNewUse(builder, useRef, newLocal, oldLocal); + addNewUse(builder, useRef, newLocal, oldLocal, queue); } } } for (LValue def : head.stmt.getDefs()) { if (def instanceof Local) { - addNewDef(builder, head, newLocal, oldLocal); + addNewDef(builder, head, newLocal, oldLocal, queue); } } } System.out.println(builder.getStmtGraph()); } while (!queue.isEmpty()); - - visited.remove(ref); + removeVisited(visited, ref); } } } - private void addNewDef(Body.BodyBuilder builder, RefBox ref, Local newDef, Local oldDef) { + private boolean addVisited(Set visited, RefBox ref){ + Optional first = visited.stream().filter(e -> e.stmt == ref.stmt).findFirst(); + if(first.isPresent()){ + RefBox refBox = first.get(); + return visited.add(refBox); + } + return visited.add(ref); + } + + + private boolean removeVisited(Set visited, RefBox ref){ + Optional first = visited.stream().filter(e -> e.stmt == ref.stmt).findFirst(); + if(first.isPresent()){ + RefBox refBox = first.get(); + return visited.remove(refBox); + } + return visited.remove(ref); + } + + + private void addNewDef(Body.BodyBuilder builder, RefBox ref, Local newDef, Local oldDef, Deque queue) { if (ref.stmt.getDefs().contains(oldDef)) { Stmt withNewDef = new JAssignStmt(newDef, ref.stmt.getUses().get(0), ref.stmt.getPositionInfo()); builder.getStmtGraph().replaceNode(ref.stmt, withNewDef); + //update the reference to the same statement in the queue + Set allRefs = queue.stream().filter(e -> e.stmt == ref.stmt).collect(Collectors.toSet()); + for (RefBox r : allRefs) { + r.stmt = withNewDef; + } ref.stmt = withNewDef; } } - private void addNewUse(Body.BodyBuilder builder, RefBox ref, Local newLocal, Local old) { + private void addNewUse(Body.BodyBuilder builder, RefBox ref, Local newLocal, Local old, Deque queue) { if (ref.stmt.getUses().contains(old)) { Stmt withNewUse = ref.stmt.withNewUse(old, newLocal); builder.getStmtGraph().replaceNode(ref.stmt, withNewUse); + // update refs on queue + Set allRefs = queue.stream().filter(e -> e.stmt == ref.stmt).collect(Collectors.toSet()); + for (RefBox r : allRefs) { + r.stmt = withNewUse; + } ref.stmt = withNewUse; } } + + + private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { Set usesUntilNewDef = new HashSet<>(); List defs = stmt.getDefs(); @@ -163,8 +199,13 @@ private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { Local def = (Local) defs.get(0); Deque queue = new ArrayDeque<>(); queue.add(builder.getStmts().get(0)); + Set visited = new HashSet<>(); while (!queue.isEmpty()) { Stmt s = queue.removeFirst(); + if(visited.contains(s)){ + break; + } + System.out.println("loop " + s); if (s.getUses().contains(def)) { if (!usesUntilNewDef.add(s)) { break; // if it is already there maybe we started looping? @@ -176,11 +217,55 @@ private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { } } queue.addAll(builder.getStmtGraph().successors(s)); + visited.add(s); } } return usesUntilNewDef.stream().map(e -> new RefBox(e)).collect(Collectors.toSet()); } + + public Map>> getStmtToUses(Body.BodyBuilder builder){ + Map>> stmtToUses = new HashMap<>(); + DominanceFinder df = new DominanceFinder(builder.getStmtGraph()); + + Deque queue = new ArrayDeque<>(); + queue.addAll(builder.getStmtGraph().getTails()); + while (!queue.isEmpty()){ + Stmt stmt = queue.removeFirst(); + BasicBlock dominator = df.getImmediateDominator(builder.getStmtGraph().getBlockOf(stmt)); + Deque domQueue = new ArrayDeque<>(); + domQueue.add(dominator); + while (!domQueue.isEmpty()){ + BasicBlock block = domQueue.removeFirst(); + Stmt tail = block.getTail(); + Deque stmtsInBlock = new ArrayDeque<>(); + stmtsInBlock.addAll(builder.getStmtGraph().predecessors(tail)); + while(!stmtsInBlock.isEmpty()){ + Stmt stmtInBlock = stmtsInBlock.removeFirst(); + if(!stmtInBlock.getUses().isEmpty()){ + for (Value use : stmtInBlock.getUses()) { + if(use instanceof Local){ // local use + List defs = getDefsBefore((Local) use, builder, stmtInBlock); + List> list = new ArrayList<>(); + list.add(new MutablePair<>(stmtInBlock, use)); + for (RefBox def : defs) { + stmtToUses.put(def.stmt, list); + } + } + } + } + stmtsInBlock.addAll(builder.getStmtGraph().predecessors(stmtInBlock)); + } + BasicBlock nextDom = df.getImmediateDominator(dominator); + if(!nextDom.equals(dominator)){ + domQueue.add(nextDom); + } + } + } + + return stmtToUses; + } + private List getDefsBefore(Local local, Body.BodyBuilder builder, Stmt mStmt) { List defsBefore = new ArrayList<>(); Set visited = new HashSet<>(); diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index 74e833f0080..75f417c0fc8 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -1,29 +1,28 @@ package sootup.java.bytecode.interceptors; import categories.Java8Test; -import com.sun.jdi.IntegerType; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import sootup.core.jimple.basic.Local; import sootup.core.jimple.basic.NoPositionInformation; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; import sootup.core.model.SootMethod; import sootup.core.signatures.MethodSignature; import sootup.core.signatures.MethodSubSignature; import sootup.core.signatures.PackageName; import sootup.core.types.ClassType; -import sootup.core.types.PrimitiveType; import sootup.core.types.UnknownType; import sootup.core.types.VoidType; import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; import sootup.java.core.types.JavaClassType; import sootup.java.core.views.JavaView; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -39,12 +38,67 @@ public void Setup() { view = new JavaView(inputLocation); } + @Test + public void testStmtToUsesSimpleAssignment(){ + Body originalBody = getBody("case0"); + LocalSplitter localSplitter = new LocalSplitter(); + Map>> actual = localSplitter.getStmtToUses(Body.builder(originalBody, Collections.emptySet())); + Map>> expected = new HashMap<>(); + + Stmt s1 = getStmt(originalBody, "$l2 = 1"); + List> s1Uses = new ArrayList<>(); + Stmt l1_gets_l2_plus_1 = getStmt(originalBody, "$l1 = $l2 + 1"); + s1Uses.add(new MutablePair<>(l1_gets_l2_plus_1, l1_gets_l2_plus_1.getUses().get(1))); + expected.put(s1, s1Uses); + + Stmt s2 = l1_gets_l2_plus_1; + Stmt l2_gets_l1_plus_1 = getStmt(originalBody, "$l2 = $l1 + 1"); + List> s2Uses = new ArrayList<>(); + s2Uses.add(new MutablePair<>(l2_gets_l1_plus_1, l2_gets_l1_plus_1.getUses().get(1))); + expected.put(s2, s2Uses); + + assertTrue(expected.keySet().containsAll(actual.keySet())); + assertTrue(expected.values().containsAll(actual.values())); + assertTrue(actual.keySet().containsAll(expected.keySet())); + assertTrue(actual.values().containsAll(expected.values())); + } + + @Test + public void testStmtToUsesSelfAssignment(){ + Body originalBody = getBody("case1"); + LocalSplitter localSplitter = new LocalSplitter(); + Map>> actual = localSplitter.getStmtToUses(Body.builder(originalBody, Collections.emptySet())); + Map>> expected = new HashMap<>(); + + Stmt s1 = getStmt(originalBody, "$l2 = 1"); + List> s1Uses = new ArrayList<>(); + Stmt l1_gets_l2_plus_1 = getStmt(originalBody, "$l1 = $l2 + 1"); + s1Uses.add(new MutablePair<>(l1_gets_l2_plus_1, l1_gets_l2_plus_1.getUses().get(1))); + expected.put(s1, s1Uses); + + Stmt s2 = l1_gets_l2_plus_1; + Stmt l2_gets_l1_plus_1 = getStmt(originalBody, "$l2 = $l1 + 1"); + List> s2Uses = new ArrayList<>(); + s2Uses.add(new MutablePair<>(l2_gets_l1_plus_1, l2_gets_l1_plus_1.getUses().get(1))); + expected.put(s2, s2Uses); + + assertTrue(expected.keySet().containsAll(actual.keySet())); + assertTrue(expected.values().containsAll(actual.values())); + assertTrue(actual.keySet().containsAll(expected.keySet())); + assertTrue(actual.values().containsAll(expected.values())); + } + + + + + private Stmt getStmt(Body body, String s){ + return body.getStmts().stream().filter(e -> e.toString().equals(s)).findFirst().get(); + } + + @Test public void testSimpleAssignment() { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case0", Collections.EMPTY_LIST, VoidType.getInstance())); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); + Body originalBody = getBody("case0"); List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); @@ -72,12 +126,17 @@ public void testSimpleAssignment() { assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } - @Test - public void testSelfAssignment() { + private Body getBody(String methodName) { ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case1", Collections.EMPTY_LIST, VoidType.getInstance())); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature(methodName, Collections.EMPTY_LIST, VoidType.getInstance())); SootMethod sootMethod = view.getMethod(sig).get(); Body originalBody = sootMethod.getBody(); + return originalBody; + } + + @Test + public void testSelfAssignment() { + Body originalBody = getBody("case1"); List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); @@ -107,10 +166,7 @@ public void testSelfAssignment() { @Test public void testBranch() { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case2", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); + Body originalBody = getBody("case2"); List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); @@ -146,10 +202,7 @@ public void testBranch() { @Test public void testBranchMoreLocals() { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case3", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); + Body originalBody = getBody("case3"); List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); @@ -191,10 +244,7 @@ public void testBranchMoreLocals() { @Test public void testBranchMoreBranches() { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case4", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); + Body originalBody = getBody("case4"); List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); @@ -247,10 +297,7 @@ public void testBranchMoreBranches() { @Test public void testBranchElseIf() { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case5", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); + Body originalBody = getBody("case5"); List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); @@ -297,10 +344,7 @@ public void testBranchElseIf() { @Test public void testForLoop() { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature("case6", Collections.EMPTY_LIST, PrimitiveType.IntType.getInstance())); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); + Body originalBody = getBody("case6"); List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); From fd1b957a70fff282429ee14262c602a2009fe245 Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Wed, 17 Jan 2024 20:52:10 +0100 Subject: [PATCH 12/24] stmtToUses selfAssign test --- .../bytecode/interceptors/LocalSplitterTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index 75f417c0fc8..ca9309d7fd2 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -70,16 +70,16 @@ public void testStmtToUsesSelfAssignment(){ Map>> actual = localSplitter.getStmtToUses(Body.builder(originalBody, Collections.emptySet())); Map>> expected = new HashMap<>(); - Stmt s1 = getStmt(originalBody, "$l2 = 1"); + Stmt s1 = getStmt(originalBody, "$l1 = 0"); List> s1Uses = new ArrayList<>(); - Stmt l1_gets_l2_plus_1 = getStmt(originalBody, "$l1 = $l2 + 1"); - s1Uses.add(new MutablePair<>(l1_gets_l2_plus_1, l1_gets_l2_plus_1.getUses().get(1))); + Stmt l1_gets_l1_plus_1 = getStmt(originalBody, "$l1 = $l1 + 1"); + s1Uses.add(new MutablePair<>(l1_gets_l1_plus_1, l1_gets_l1_plus_1.getUses().get(1))); expected.put(s1, s1Uses); - Stmt s2 = l1_gets_l2_plus_1; - Stmt l2_gets_l1_plus_1 = getStmt(originalBody, "$l2 = $l1 + 1"); + Stmt s2 = getStmt(originalBody, "$l2 = 1"); + Stmt l2_gets_l2_plus_1 = getStmt(originalBody, "$l2 = $l2 + 1"); List> s2Uses = new ArrayList<>(); - s2Uses.add(new MutablePair<>(l2_gets_l1_plus_1, l2_gets_l1_plus_1.getUses().get(1))); + s2Uses.add(new MutablePair<>(l2_gets_l2_plus_1, l2_gets_l2_plus_1.getUses().get(1))); expected.put(s2, s2Uses); assertTrue(expected.keySet().containsAll(actual.keySet())); From 9b896a4000b513aa489bf0c8052d641bfb3152a5 Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Tue, 23 Jan 2024 13:57:23 +0100 Subject: [PATCH 13/24] localsplitter wip --- .../interceptors/defuse/DefUseHelper.java | 48 +++++++ .../interceptors/defuse/DefUseHelperTest.java | 127 ++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/defuse/DefUseHelper.java create mode 100644 sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/defuse/DefUseHelperTest.java diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/defuse/DefUseHelper.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/defuse/DefUseHelper.java new file mode 100644 index 00000000000..8393c9fb620 --- /dev/null +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/defuse/DefUseHelper.java @@ -0,0 +1,48 @@ +package sootup.java.bytecode.interceptors.defuse; + +import sootup.core.graph.BasicBlock; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.Value; +import sootup.core.jimple.common.stmt.Stmt; + +import java.util.*; + +public class DefUseHelper { + + /** + * Returns a map of locals to Stmts, such that the Stmt defines the local in the given block + * if a local is defined multiple times, we keep the most recent definition. + * + * @param block + * @return + */ + public static Map getDefsInBlock(BasicBlock block) { + Map localToDefStmt = new HashMap<>(); + for (Stmt stmt : block.getStmts()) { + if (stmt.getDefs().size() == 1) { + Local def = (Local) stmt.getDefs().get(0); + localToDefStmt.put(def, stmt); // subsequent defs will overwrite + } + } + return localToDefStmt; + } + + /** + * Returns a map of locals to Stmts, such that the Stmt uses the local in the given block. + * We only keep the first use the local + * @param block + * @return + */ + public static Map getFirstUsesInBlock(BasicBlock block) { + Map localToUseStmt = new HashMap<>(); + for (Stmt stmt : block.getStmts()) { + for (Value use : stmt.getUses()) { + if (use instanceof Local) { + localToUseStmt.putIfAbsent((Local) use, stmt); + } + } + } + return localToUseStmt; + } + +} diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/defuse/DefUseHelperTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/defuse/DefUseHelperTest.java new file mode 100644 index 00000000000..76db7adc589 --- /dev/null +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/defuse/DefUseHelperTest.java @@ -0,0 +1,127 @@ +package sootup.java.bytecode.interceptors.defuse; + +import categories.Java8Test; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.model.Body; +import sootup.core.model.SootMethod; +import sootup.core.signatures.MethodSignature; +import sootup.core.signatures.MethodSubSignature; +import sootup.core.signatures.PackageName; +import sootup.core.types.ClassType; +import sootup.core.types.PrimitiveType; +import sootup.core.types.Type; +import sootup.core.types.VoidType; +import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; +import sootup.java.core.types.JavaClassType; +import sootup.java.core.views.JavaView; + +import java.util.*; + +import static org.junit.Assert.assertTrue; + +@Category(Java8Test.class) +public class DefUseHelperTest { + + JavaView view; + + @Before + public void Setup() { + String classPath = "src/test/java/resources/interceptors"; + JavaClassPathAnalysisInputLocation inputLocation = new JavaClassPathAnalysisInputLocation(classPath); + view = new JavaView(inputLocation); + } + + private Body getBody(String methodName, Type returnType) { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + MethodSignature sig = new MethodSignature(type, new MethodSubSignature(methodName, Collections.EMPTY_LIST, returnType)); + SootMethod sootMethod = view.getMethod(sig).get(); + Body originalBody = sootMethod.getBody(); + return originalBody; + } + + private Stmt getStmt(Body body, String s){ + return body.getStmts().stream().filter(e -> e.toString().equals(s)).findFirst().get(); + } + + @Test + public void testSimpleAssign(){ + Body originalBody = getBody("case0", VoidType.getInstance()); + Map actual = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(0)); + + Stmt s1 = getStmt(originalBody, "$l1 = $l2 + 1"); + Local def1 = (Local) s1.getDefs().get(0); + + Stmt s2 = getStmt(originalBody, "$l2 = $l1 + 1"); + Local def2 = (Local) s2.getDefs().get(0); + + assertTrue(actual.get(def1).equals(s1)); + assertTrue(actual.get(def2).equals(s2)); + } + + @Test + public void testSelfAssign(){ + Body originalBody = getBody("case1", VoidType.getInstance()); + Map actual = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(0)); + + Stmt s1 = getStmt(originalBody, "$l1 = $l1 + 1"); + Local def1 = (Local) s1.getDefs().get(0); + + Stmt s2 = getStmt(originalBody, "$l2 = $l2 + 1"); + Local def2 = (Local) s2.getDefs().get(0); + + assertTrue(actual.get(def1).equals(s1)); + assertTrue(actual.get(def2).equals(s2)); + } + + @Test + public void testBranch(){ + Body originalBody = getBody("case2", PrimitiveType.IntType.getInstance()); + Map defsInBlock0 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(0)); + Stmt s0 = getStmt(originalBody, "$l1 = 0"); + Local def0 = (Local) s0.getDefs().get(0); + assertTrue(defsInBlock0.get(def0).equals(s0)); + + Map defsInBlock1 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(1)); + Stmt s1 = getStmt(originalBody, "$l1 = $l1 + 1"); + Local def1 = (Local) s1.getDefs().get(0); + assertTrue(defsInBlock1.get(def1).equals(s1)); + + Map defsInBlock2 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(2)); + Stmt s2 = getStmt(originalBody, "$l1 = $l1 + 2"); + Local def2 = (Local) s1.getDefs().get(0); + assertTrue(defsInBlock2.get(def2).equals(s2)); + + + System.out.println(defsInBlock2); + System.out.println(originalBody); + + } + + @Test + public void testMultiBranch(){ + Body originalBody = getBody("case3", PrimitiveType.IntType.getInstance()); + Map defsInBlock0 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(0)); + Stmt s0 = getStmt(originalBody, "$l1 = 0"); + Local def0 = (Local) s0.getDefs().get(0); + assertTrue(defsInBlock0.get(def0).equals(s0)); + + Map defsInBlock1 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(1)); + Stmt s1 = getStmt(originalBody, "$l1 = $l1 + 3"); + Local def1 = (Local) s1.getDefs().get(0); + assertTrue(defsInBlock1.get(def1).equals(s1)); + + Map defsInBlock2 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(2)); + Stmt s2 = getStmt(originalBody, "$l1 = $l1 - 3"); + Local def2 = (Local) s1.getDefs().get(0); + assertTrue(defsInBlock2.get(def2).equals(s2)); + + + System.out.println(defsInBlock2); + System.out.println(originalBody); + + } +} From ec0e8a12568a5338fc885ef60133692c00744330 Mon Sep 17 00:00:00 2001 From: Kadiray Karakaya Date: Wed, 24 Jan 2024 15:34:10 +0100 Subject: [PATCH 14/24] localsplitter target --- .../interceptors/LocalSplitterTarget.class | Bin 0 -> 1014 bytes .../interceptors/LocalSplitterTarget.java | 88 ++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class create mode 100644 sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.java diff --git a/sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class b/sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class new file mode 100644 index 0000000000000000000000000000000000000000..f02f377853db86e4492b8502a0958a5564b3ed25 GIT binary patch literal 1014 zcmZva+fvg|6o&tuq-{v)nHC{<06YQ8v1N27bySp*nWAFFYtl9Y#o978^$8fC!YjrZ zZoKiz883VQpT=tk5C7hcDJq#sR(AGU|NpI({q^VPF93=tSkRG+VI+pUK-6iroBkaE z{rb!c0b{Mx@B~toX4`w*dsXwg&)r&!Um|t)wYM0=r64Y=vJHVm)pzSJH{FBN*(*U- zF^Hugt_oPyPOn?{)|*oLT%}WYTh)VB)Ayxcch~dh_uV({FbqTla%b!dq-DlJ%Wdy2 zJgMz_bsv+^*qOXcuoFc1jB<6JQ7}ekKS6xti$ILesQM^3%rLbSNF;Q}C{GwWj&1Ce z2PFk2g321BK>?FO!+ZimXG^f$q0E00aZ1SU5-d*gEky|naS}=3r`**TM|H=32TO_^ zBW@cF$Bq!`1GhBQ!njJ$a+TxT2;~aMU=(?bQ54M4PLL3?+eE0o7pPAw^{iA^vQg=h z>buY>%6s5+l^x1SYPdkBi-f&I@JTvN)9W&Ku5fj2;F=e479>O0w}I=5a!vkm4f~>- z@g7N=6t>y#R}z?iQ7kL{IZE9i#Z6Mov)@HhERmu}wi2eWiW%JE?rnZ`RK*aC6J$gz zB`1*znR)@!+`mk=Z61+D4ZP8x{T_$JdoYn%x&wD9agQwbxmqL31O9=B7{@xN@rX0o zAWKCtn9wDILP&=Q+pL#oc1(Bl@_|as2DsDZ~EHmSz WySJ5r1?>k6A-*HabCjW|hU{1){ + a = a + 3; + a = a + 5; + } else { + a = a - 3; + a = a - 5; + } + + return a; + } + + int case5(){ + int a = 0; + if(a<0){ + a = a + 1; + a = a + 2; + } else if(a<5) { + a = a - 1; + a = a - 2; + } else{ + a = a * 1; + a = a * 2; + } + + return a; + } + + int case6(){ + int a = 0; + for(int i = 0; i < 10; i++){ + i = i + 1; + a++; + } + return a; + } + +} \ No newline at end of file From 1d70be47a1028af6b84046e0a9014ce28940642c Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Mon, 5 Feb 2024 14:37:08 +0100 Subject: [PATCH 15/24] fix `LocalSplitterTest` --- .../interceptors/LocalSplitterTest.java | 224 +++++++++--------- 1 file changed, 109 insertions(+), 115 deletions(-) diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index ca9309d7fd2..9b66e1775b8 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -1,6 +1,10 @@ package sootup.java.bytecode.interceptors; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import categories.Java8Test; +import java.util.*; import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; import org.junit.Before; @@ -12,21 +16,13 @@ import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; import sootup.core.model.SootMethod; -import sootup.core.signatures.MethodSignature; -import sootup.core.signatures.MethodSubSignature; import sootup.core.signatures.PackageName; import sootup.core.types.ClassType; import sootup.core.types.UnknownType; -import sootup.core.types.VoidType; import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; import sootup.java.core.types.JavaClassType; import sootup.java.core.views.JavaView; -import java.util.*; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - @Category(Java8Test.class) public class LocalSplitterTest { JavaView view; @@ -45,14 +41,14 @@ public void testStmtToUsesSimpleAssignment(){ Map>> actual = localSplitter.getStmtToUses(Body.builder(originalBody, Collections.emptySet())); Map>> expected = new HashMap<>(); - Stmt s1 = getStmt(originalBody, "$l2 = 1"); + Stmt s1 = getStmt(originalBody, "l2 = 1"); List> s1Uses = new ArrayList<>(); - Stmt l1_gets_l2_plus_1 = getStmt(originalBody, "$l1 = $l2 + 1"); + Stmt l1_gets_l2_plus_1 = getStmt(originalBody, "l1 = l2 + 1"); s1Uses.add(new MutablePair<>(l1_gets_l2_plus_1, l1_gets_l2_plus_1.getUses().get(1))); expected.put(s1, s1Uses); Stmt s2 = l1_gets_l2_plus_1; - Stmt l2_gets_l1_plus_1 = getStmt(originalBody, "$l2 = $l1 + 1"); + Stmt l2_gets_l1_plus_1 = getStmt(originalBody, "l2 = l1 + 1"); List> s2Uses = new ArrayList<>(); s2Uses.add(new MutablePair<>(l2_gets_l1_plus_1, l2_gets_l1_plus_1.getUses().get(1))); expected.put(s2, s2Uses); @@ -70,14 +66,14 @@ public void testStmtToUsesSelfAssignment(){ Map>> actual = localSplitter.getStmtToUses(Body.builder(originalBody, Collections.emptySet())); Map>> expected = new HashMap<>(); - Stmt s1 = getStmt(originalBody, "$l1 = 0"); + Stmt s1 = getStmt(originalBody, "l1 = 0"); List> s1Uses = new ArrayList<>(); - Stmt l1_gets_l1_plus_1 = getStmt(originalBody, "$l1 = $l1 + 1"); + Stmt l1_gets_l1_plus_1 = getStmt(originalBody, "l1 = l1 + 1"); s1Uses.add(new MutablePair<>(l1_gets_l1_plus_1, l1_gets_l1_plus_1.getUses().get(1))); expected.put(s1, s1Uses); - Stmt s2 = getStmt(originalBody, "$l2 = 1"); - Stmt l2_gets_l2_plus_1 = getStmt(originalBody, "$l2 = $l2 + 1"); + Stmt s2 = getStmt(originalBody, "l2 = 1"); + Stmt l2_gets_l2_plus_1 = getStmt(originalBody, "l2 = l2 + 1"); List> s2Uses = new ArrayList<>(); s2Uses.add(new MutablePair<>(l2_gets_l2_plus_1, l2_gets_l2_plus_1.getUses().get(1))); expected.put(s2, s2Uses); @@ -102,10 +98,10 @@ public void testSimpleAssignment() { List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); LocalSplitter localSplitter = new LocalSplitter(); @@ -115,11 +111,11 @@ public void testSimpleAssignment() { assertTrue(expectedLocals.containsAll(newBody.getLocals())); assertTrue(newBody.getLocals().containsAll(expectedLocals)); - String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + - "$l2#2 = 1;\n" + - "$l1#3 = $l2#2 + 1;\n" + - "$l2#4 = $l1#3 + 1;\n" + + String expectedStmts = "l0 := @this: LocalSplitterTarget;\n" + + "l1#1 = 0;\n" + + "l2#2 = 1;\n" + + "l1#3 = l2#2 + 1;\n" + + "l2#4 = l1#3 + 1;\n" + "\n" + "return;"; @@ -128,10 +124,8 @@ public void testSimpleAssignment() { private Body getBody(String methodName) { ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature(methodName, Collections.EMPTY_LIST, VoidType.getInstance())); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); - return originalBody; + SootMethod sootMethod = view.getClass(type).get().getMethods().stream().filter(method -> method.getName().equals(methodName)).findFirst().get(); + return sootMethod.getBody(); } @Test @@ -140,10 +134,10 @@ public void testSelfAssignment() { List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); LocalSplitter localSplitter = new LocalSplitter(); @@ -153,11 +147,11 @@ public void testSelfAssignment() { assertTrue(expectedLocals.containsAll(newBody.getLocals())); assertTrue(newBody.getLocals().containsAll(expectedLocals)); - String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + - "$l2#2 = 1;\n" + - "$l1#3 = $l1#1 + 1;\n" + - "$l2#4 = $l2#2 + 1;\n" + + String expectedStmts = "l0 := @this: LocalSplitterTarget;\n" + + "l1#1 = 0;\n" + + "l2#2 = 1;\n" + + "l1#3 = l1#1 + 1;\n" + + "l2#4 = l2#2 + 1;\n" + "\n" + "return;"; @@ -170,9 +164,9 @@ public void testBranch() { List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); LocalSplitter localSplitter = new LocalSplitter(); @@ -182,20 +176,20 @@ public void testBranch() { assertTrue(expectedLocals.containsAll(newBody.getLocals())); assertTrue(newBody.getLocals().containsAll(expectedLocals)); - String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + + String expectedStmts = "l0 := @this: LocalSplitterTarget;\n" + + "l1#1 = 0;\n" + "\n" + - "if $l1#1 >= 0 goto label1;\n" + - "$l1#2 = $l1#1 + 1;\n" + + "if l1#1 >= 0 goto label1;\n" + + "l1#2 = l1#1 + 1;\n" + "\n" + "goto label2;\n" + "\n" + "label1:\n" + - "$l1#3 = $l1#1 - 1;\n" + - "$l1#2 = $l1#3 + 2;\n" + + "l1#3 = l1#1 - 1;\n" + + "l1#2 = l1#3 + 2;\n" + "\n" + "label2:\n" + - "return $l1#2;"; + "return l1#2;"; assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } @@ -206,12 +200,12 @@ public void testBranchMoreLocals() { List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#6", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#6", UnknownType.getInstance(), NoPositionInformation.getInstance())); Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); LocalSplitter localSplitter = new LocalSplitter(); @@ -221,23 +215,23 @@ public void testBranchMoreLocals() { assertTrue(expectedLocals.containsAll(newBody.getLocals())); assertTrue(newBody.getLocals().containsAll(expectedLocals)); - String expectedStmts = "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + + String expectedStmts = "l0 := @this: LocalSplitterTarget;\n" + + "l1#1 = 0;\n" + "\n" + - "if $l1#1 >= 0 goto label1;\n" + - "$l1#2 = $l1#1 + 1;\n" + - "$l1#3 = $l1#2 + 2;\n" + - "$l1#4 = $l1#3 + 3;\n" + + "if l1#1 >= 0 goto label1;\n" + + "l1#2 = l1#1 + 1;\n" + + "l1#3 = l1#2 + 2;\n" + + "l1#4 = l1#3 + 3;\n" + "\n" + "goto label2;\n" + "\n" + "label1:\n" + - "$l1#5 = $l1#1 - 1;\n" + - "$l1#6 = $l1#5 - 2;\n" + - "$l1#4 = $l1#6 - 3;\n" + + "l1#5 = l1#1 - 1;\n" + + "l1#6 = l1#5 - 2;\n" + + "l1#4 = l1#6 - 3;\n" + "\n" + "label2:\n" + - "return $l1#4;"; + "return l1#4;"; assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } @@ -248,13 +242,13 @@ public void testBranchMoreBranches() { List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#6", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#7", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#6", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#7", UnknownType.getInstance(), NoPositionInformation.getInstance())); Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); LocalSplitter localSplitter = new LocalSplitter(); @@ -265,32 +259,32 @@ public void testBranchMoreBranches() { assertTrue(newBody.getLocals().containsAll(expectedLocals)); String expectedStmts = - "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + + "l0 := @this: LocalSplitterTarget;\n" + + "l1#1 = 0;\n" + "\n" + - "if $l1#1 >= 0 goto label1;\n" + - "$l1#2 = $l1#1 + 1;\n" + - "$l1#3 = $l1#2 + 2;\n" + + "if l1#1 >= 0 goto label1;\n" + + "l1#2 = l1#1 + 1;\n" + + "l1#3 = l1#2 + 2;\n" + "\n" + "goto label2;\n" + "\n" + "label1:\n" + - "$l1#4 = $l1#1 - 1;\n" + - "$l1#3 = $l1#4 - 2;\n" + + "l1#4 = l1#1 - 1;\n" + + "l1#3 = l1#4 - 2;\n" + "\n" + "label2:\n" + - "if $l1#3 <= 1 goto label3;\n" + - "$l1#5 = $l1#3 + 3;\n" + - "$l1#6 = $l1#5 + 5;\n" + + "if l1#3 <= 1 goto label3;\n" + + "l1#5 = l1#3 + 3;\n" + + "l1#6 = l1#5 + 5;\n" + "\n" + "goto label4;\n" + "\n" + "label3:\n" + - "$l1#7 = $l1#3 - 3;\n" + - "$l1#6 = $l1#7 - 5;\n" + + "l1#7 = l1#3 - 3;\n" + + "l1#6 = l1#7 - 5;\n" + "\n" + "label4:\n" + - "return $l1#6;"; + "return l1#6;"; assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } @@ -301,11 +295,11 @@ public void testBranchElseIf() { List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); LocalSplitter localSplitter = new LocalSplitter(); @@ -316,28 +310,28 @@ public void testBranchElseIf() { assertTrue(newBody.getLocals().containsAll(expectedLocals)); String expectedStmts = - "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + + "l0 := @this: LocalSplitterTarget;\n" + + "l1#1 = 0;\n" + "\n" + - "if $l1#1 >= 0 goto label1;\n" + - "$l1#2 = $l1#1 + 1;\n" + - "$l1#3 = $l1#2 + 2;\n" + + "if l1#1 >= 0 goto label1;\n" + + "l1#2 = l1#1 + 1;\n" + + "l1#3 = l1#2 + 2;\n" + "\n" + "goto label3;\n" + "\n" + "label1:\n" + - "if $l1#1 >= 5 goto label2;\n" + - "$l1#4 = $l1#1 - 1;\n" + - "$l1#3 = $l1#4 - 2;\n" + + "if l1#1 >= 5 goto label2;\n" + + "l1#4 = l1#1 - 1;\n" + + "l1#3 = l1#4 - 2;\n" + "\n" + "goto label3;\n" + "\n" + "label2:\n" + - "$l1#5 = $l1#1 * 1;\n" + - "$l1#3 = $l1#5 * 2;\n" + + "l1#5 = l1#1 * 1;\n" + + "l1#3 = l1#5 * 2;\n" + "\n" + "label3:\n" + - "return $l1#3;"; + "return l1#3;"; assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } @@ -348,11 +342,11 @@ public void testForLoop() { List expectedLocals = new ArrayList<>(); expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("$l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("$l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); + expectedLocals.add(new Local("l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); LocalSplitter localSplitter = new LocalSplitter(); @@ -367,28 +361,28 @@ public void testForLoop() { // assertTrue(newBody.getLocals().containsAll(expectedLocals)); String expectedStmts = - "$l0 := @this: LocalSplitterTarget;\n" + - "$l1#1 = 0;\n" + + "l0 := @this: LocalSplitterTarget;\n" + + "l1#1 = 0;\n" + "\n" + - "if $l1#1 >= 0 goto label1;\n" + - "$l1#2 = $l1#1 + 1;\n" + - "$l1#3 = $l1#2 + 2;\n" + + "if l1#1 >= 0 goto label1;\n" + + "l1#2 = l1#1 + 1;\n" + + "l1#3 = l1#2 + 2;\n" + "\n" + "goto label3;\n" + "\n" + "label1:\n" + - "if $l1#1 >= 5 goto label2;\n" + - "$l1#4 = $l1#1 - 1;\n" + - "$l1#3 = $l1#4 - 2;\n" + + "if l1#1 >= 5 goto label2;\n" + + "l1#4 = l1#1 - 1;\n" + + "l1#3 = l1#4 - 2;\n" + "\n" + "goto label3;\n" + "\n" + "label2:\n" + - "$l1#5 = $l1#1 * 1;\n" + - "$l1#3 = $l1#5 * 2;\n" + + "l1#5 = l1#1 * 1;\n" + + "l1#3 = l1#5 * 2;\n" + "\n" + "label3:\n" + - "return $l1#3;"; + "return l1#3;"; // assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); } From 2f4f9a1b332a4265ce174504a998db847395eba9 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Wed, 7 Feb 2024 16:56:59 +0100 Subject: [PATCH 16/24] rewrite `LocalSplitter` --- .../bytecode/interceptors/LocalSplitter.java | 413 +++++------ .../interceptors/LocalSplitterTarget.class | Bin 1014 -> 1462 bytes .../interceptors/LocalSplitterTarget.java | 168 +++-- .../interceptors/LocalSplitterTest.java | 677 ++++++++---------- 4 files changed, 567 insertions(+), 691 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index b8d00239521..6714d28c2bd 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -22,298 +22,211 @@ * #L% */ -import org.apache.commons.lang3.tuple.MutablePair; -import org.apache.commons.lang3.tuple.Pair; -import org.checkerframework.checker.units.qual.A; +import java.util.*; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; import sootup.core.graph.*; -import sootup.core.jimple.basic.LValue; import sootup.core.jimple.basic.Local; -import sootup.core.jimple.basic.Value; -import sootup.core.jimple.common.stmt.JAssignStmt; +import sootup.core.jimple.common.stmt.AbstractDefinitionStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; import sootup.core.transform.BodyInterceptor; import sootup.core.views.View; -import javax.annotation.Nonnull; -import java.util.*; -import java.util.stream.Collectors; - public class LocalSplitter implements BodyInterceptor { - private Map>> stmtToUses; + /** + * Contains disjoint sets of nodes which are implemented as trees. Every set is represented by a + * tree in the forest. Each set is identified by the root node of its tree, also known as its + * representative. + * + *

Disjoint-set data + * structure + */ + static class DisjointSetForest { + /** Every node points to its parent in its tree. Roots of trees point to themselves. */ + private final Map parent = new HashMap<>(); - private Map newToOriginalStmt; + /** Stores the size of a tree under the key. Only updated for roots of trees. */ + private final Map sizes = new HashMap<>(); + private int setCount = 0; + + /** + * Creates a new set that only contains the {@code node}. Does nothing when the forest already + * contains the {@code node}. + */ + void add(T node) { + if (parent.containsKey(node)) return; - static class RefBox { - Stmt stmt; + parent.put(node, node); + sizes.put(node, 1); + setCount++; + } - public RefBox(Stmt stmt) { - this.stmt = stmt; - } + /** Finds the representative of the set that contains the {@code node}. */ + T find(T node) { + if (!parent.containsKey(node)) + throw new IllegalArgumentException("The DisjointSetForest does not contain the node."); - @Override - public String toString() { - return stmt.toString(); - } + while (parent.get(node) != node) { + // Path Halving to get amortized constant operations + T grandparent = parent.get(parent.get(node)); + parent.put(node, grandparent); + node = grandparent; + } + return node; } - /** - * Multiple defs of the same local are to split. - * - * @param builder - * @return + * Combines the sets of {@code first} and {@code second}. Returns the representative of the + * combined set. */ - private Set findLocalsToSplit(Body.BodyBuilder builder) { - Set visitedLocals = new LinkedHashSet<>(); - Set localsToSplit = new LinkedHashSet<>(); - for (Stmt stmt : builder.getStmts()) { - for (LValue def : stmt.getDefs()) { - if (def instanceof Local) { - if (visitedLocals.contains(def)) { - localsToSplit.add((Local) def); - } else { - visitedLocals.add((Local) def); - } - } - } - } - return localsToSplit; - } + T union(T first, T second) { + first = find(first); + second = find(second); + if (first == second) return first; - @Override - public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) { - newToOriginalStmt = new HashMap<>(); - getStmtToUses(builder); - - Set localsToSplit = findLocalsToSplit(builder); - int w = 0; - - Set visited = new HashSet<>(); - for (int i = 0; i < builder.getStmts().size(); i++) { - Stmt stmt = builder.getStmts().get(i); - RefBox ref = new RefBox(stmt); - if (ref.stmt.getDefs().size() == 1) { - LValue singleDef = ref.stmt.getDefs().get(0); - if (!(singleDef instanceof Local) || visited.remove(ref)) { - continue; - } - - Local oldLocal = (Local) singleDef; - if (!localsToSplit.contains(oldLocal)) { - continue; - } - - Local newLocal = oldLocal.withName(oldLocal.getName() + "#" + (++w)); - builder.addLocal(newLocal); - - Deque queue = new ArrayDeque<>(); - queue.addFirst(ref); - do { - RefBox head = queue.removeFirst(); - if (addVisited(visited, head)) { - Set useStmts = usesUntilNewDef(builder, head.stmt); - for (RefBox useRef : useStmts) { - for (Value use : useRef.stmt.getUses()) { - if (use == newLocal) { - continue; - } - if (use instanceof Local) { - queue.addAll(getDefsBefore(oldLocal, builder, useRef.stmt)); - addNewUse(builder, useRef, newLocal, oldLocal, queue); - } - } - } - for (LValue def : head.stmt.getDefs()) { - if (def instanceof Local) { - addNewDef(builder, head, newLocal, oldLocal, queue); - } - } - } - System.out.println(builder.getStmtGraph()); - } while (!queue.isEmpty()); - removeVisited(visited, ref); - } + T smaller = (sizes.get(first) > sizes.get(second)) ? second : first; + T larger = (smaller == first) ? second : first; - } + // adding the smaller subtree to the larger tree keeps the tree flatter + parent.put(smaller, larger); + sizes.put(larger, sizes.get(smaller) + sizes.get(larger)); + sizes.remove(smaller); + setCount--; + + return larger; } - private boolean addVisited(Set visited, RefBox ref){ - Optional first = visited.stream().filter(e -> e.stmt == ref.stmt).findFirst(); - if(first.isPresent()){ - RefBox refBox = first.get(); - return visited.add(refBox); - } - return visited.add(ref); + int getSetCount() { + return setCount; } + } + static class WrappedStmt { + @Nonnull final Stmt inner; + final boolean isDef; - private boolean removeVisited(Set visited, RefBox ref){ - Optional first = visited.stream().filter(e -> e.stmt == ref.stmt).findFirst(); - if(first.isPresent()){ - RefBox refBox = first.get(); - return visited.remove(refBox); - } - return visited.remove(ref); + WrappedStmt(@Nonnull Stmt inner, boolean isDef) { + this.inner = inner; + this.isDef = isDef; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; - private void addNewDef(Body.BodyBuilder builder, RefBox ref, Local newDef, Local oldDef, Deque queue) { - if (ref.stmt.getDefs().contains(oldDef)) { - Stmt withNewDef = new JAssignStmt(newDef, ref.stmt.getUses().get(0), ref.stmt.getPositionInfo()); - builder.getStmtGraph().replaceNode(ref.stmt, withNewDef); - //update the reference to the same statement in the queue - Set allRefs = queue.stream().filter(e -> e.stmt == ref.stmt).collect(Collectors.toSet()); - for (RefBox r : allRefs) { - r.stmt = withNewDef; - } - ref.stmt = withNewDef; - } - } + WrappedStmt that = (WrappedStmt) o; - private void addNewUse(Body.BodyBuilder builder, RefBox ref, Local newLocal, Local old, Deque queue) { - if (ref.stmt.getUses().contains(old)) { - Stmt withNewUse = ref.stmt.withNewUse(old, newLocal); - builder.getStmtGraph().replaceNode(ref.stmt, withNewUse); - // update refs on queue - Set allRefs = queue.stream().filter(e -> e.stmt == ref.stmt).collect(Collectors.toSet()); - for (RefBox r : allRefs) { - r.stmt = withNewUse; - } - ref.stmt = withNewUse; - } + if (isDef != that.isDef) return false; + return inner.equals(that.inner); } + @Override + public int hashCode() { + int result = inner.hashCode(); + result = 31 * result + (isDef ? 1 : 0); + return result; + } + } + + @Override + public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) { + MutableStmtGraph graph = builder.getStmtGraph(); + + // `#` is used as a special character for splitting the locals, + // so it can't be in any of the original local names since it might cause name collisions + assert builder.getLocals().stream().noneMatch(local -> local.getName().contains("#")); + + Set newLocals = new HashSet<>(); + + for (Local local : builder.getLocals()) { + // TODO explain why a disjoint set is used here + DisjointSetForest disjointSet = new DisjointSetForest<>(); + + List assignments = findAllAssignmentsToLocal(builder, local); + for (AbstractDefinitionStmt assignment : assignments) { + WrappedStmt defStmt = new WrappedStmt(assignment, true); + disjointSet.add(defStmt); + + Queue queue = new ArrayDeque<>(graph.successors(assignment)); + Set visited = new HashSet<>(); + + while (!queue.isEmpty()) { + Stmt stmt = queue.remove(); + if (!visited.add(stmt)) { + continue; + } + + if (stmt.getUses().contains(local)) { + // TODO might be able to stop short when running into a non-trivial set + // since from that point onward the previous walk that crated + // that set already walked everything following the current statement + WrappedStmt useStmt = new WrappedStmt(stmt, false); + disjointSet.add(useStmt); + disjointSet.union(defStmt, useStmt); + } + + // a new assignment to the local -> end walk here + // otherwise continue by adding all successors to the queue + if (!stmt.getDefs().contains(local)) { + queue.addAll(graph.successors(stmt)); + } + } + } + if (disjointSet.getSetCount() <= 1) { + // There is only a single that local that can't be split + newLocals.add(local); + continue; + } + Map representativeToNewLocal = new HashMap<>(); + final int[] nextId = {0}; - private Set usesUntilNewDef(Body.BodyBuilder builder, Stmt stmt) { - Set usesUntilNewDef = new HashSet<>(); - List defs = stmt.getDefs(); - if (defs.size() == 1) { - Local def = (Local) defs.get(0); - Deque queue = new ArrayDeque<>(); - queue.add(builder.getStmts().get(0)); - Set visited = new HashSet<>(); - while (!queue.isEmpty()) { - Stmt s = queue.removeFirst(); - if(visited.contains(s)){ - break; - } - System.out.println("loop " + s); - if (s.getUses().contains(def)) { - if (!usesUntilNewDef.add(s)) { - break; // if it is already there maybe we started looping? - } - } - if (!stmt.equals(s)) { - if (s.getDefs().contains(def)) { - continue; - } - } - queue.addAll(builder.getStmtGraph().successors(s)); - visited.add(s); - } + for (Stmt stmt : builder.getStmts()) { + if (!stmt.getUsesAndDefs().contains(local)) { + continue; } - return usesUntilNewDef.stream().map(e -> new RefBox(e)).collect(Collectors.toSet()); - } + Stmt oldStmt = stmt; - public Map>> getStmtToUses(Body.BodyBuilder builder){ - Map>> stmtToUses = new HashMap<>(); - DominanceFinder df = new DominanceFinder(builder.getStmtGraph()); - - Deque queue = new ArrayDeque<>(); - queue.addAll(builder.getStmtGraph().getTails()); - while (!queue.isEmpty()){ - Stmt stmt = queue.removeFirst(); - BasicBlock dominator = df.getImmediateDominator(builder.getStmtGraph().getBlockOf(stmt)); - Deque domQueue = new ArrayDeque<>(); - domQueue.add(dominator); - while (!domQueue.isEmpty()){ - BasicBlock block = domQueue.removeFirst(); - Stmt tail = block.getTail(); - Deque stmtsInBlock = new ArrayDeque<>(); - stmtsInBlock.addAll(builder.getStmtGraph().predecessors(tail)); - while(!stmtsInBlock.isEmpty()){ - Stmt stmtInBlock = stmtsInBlock.removeFirst(); - if(!stmtInBlock.getUses().isEmpty()){ - for (Value use : stmtInBlock.getUses()) { - if(use instanceof Local){ // local use - List defs = getDefsBefore((Local) use, builder, stmtInBlock); - List> list = new ArrayList<>(); - list.add(new MutablePair<>(stmtInBlock, use)); - for (RefBox def : defs) { - stmtToUses.put(def.stmt, list); - } - } - } - } - stmtsInBlock.addAll(builder.getStmtGraph().predecessors(stmtInBlock)); - } - BasicBlock nextDom = df.getImmediateDominator(dominator); - if(!nextDom.equals(dominator)){ - domQueue.add(nextDom); - } - } + if (stmt.getDefs().contains(local)) { + Local newDefLocal = + representativeToNewLocal.computeIfAbsent( + disjointSet.find(new WrappedStmt(oldStmt, true)), + s -> local.withName(local.getName() + "#" + (nextId[0]++))); + newLocals.add(newDefLocal); + stmt = ((AbstractDefinitionStmt) stmt).withNewDef(newDefLocal); } - return stmtToUses; - } - - private List getDefsBefore(Local local, Body.BodyBuilder builder, Stmt mStmt) { - List defsBefore = new ArrayList<>(); - Set visited = new HashSet<>(); - BasicBlock block = builder.getStmtGraph().getBlockOf(mStmt); - Deque blockQueue = new ArrayDeque<>(); - blockQueue.addFirst(block); - boolean firstPass = true; - do { - block = blockQueue.removeFirst(); - - if (firstPass) { - ; - ; // to visit the same block once more when looping - } else if (!visited.add(block)) { - continue; - } - Deque stmtQueue = new ArrayDeque<>(); - if (firstPass) { - stmtQueue.addAll(predsInSameBlock(builder, mStmt)); - firstPass = false; - } else { - stmtQueue.add(block.getTail()); - } - while (!stmtQueue.isEmpty()) { - Stmt s = stmtQueue.removeFirst(); - if (s.getDefs().contains(local)) { - defsBefore.add(s); - break; - } - // iterate only in the same block here. - stmtQueue.addAll(predsInSameBlock(builder, s)); - } - blockQueue.addAll(block.getPredecessors()); - } while (!blockQueue.isEmpty()); - return defsBefore.stream().map(e -> new RefBox(e)).collect(Collectors.toList()); - } - - private List predsInSameBlock(Body.BodyBuilder builder, Stmt stmt) { - List predsInSameBlock = new ArrayList<>(); - MutableStmtGraph stmtGraph = builder.getStmtGraph(); - List preds = stmtGraph.predecessors(stmt); - for (Stmt pred : preds) { - if (stmtGraph.getBlockOf(pred).equals(stmtGraph.getBlockOf(stmt))) { - predsInSameBlock.add(pred); - } + if (stmt.getUses().contains(local)) { + Local newUseLocal = + representativeToNewLocal.computeIfAbsent( + disjointSet.find(new WrappedStmt(oldStmt, false)), + s -> local.withName(local.getName() + "#" + (nextId[0]++))); + newLocals.add(newUseLocal); + stmt = stmt.withNewUse(local, newUseLocal); } - return predsInSameBlock; - } + graph.replaceNode(oldStmt, stmt); + } + } -} \ No newline at end of file + builder.setLocals(newLocals); + } + + List findAllAssignmentsToLocal( + @Nonnull Body.BodyBuilder builder, Local local) { + return builder.getStmts().stream() + .filter(stmt -> stmt instanceof AbstractDefinitionStmt) + .map(stmt -> (AbstractDefinitionStmt) stmt) + .filter(stmt -> stmt.getLeftOp() == local) + .collect(Collectors.toList()); + } +} diff --git a/sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class b/sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class index f02f377853db86e4492b8502a0958a5564b3ed25..42ccbaa08193ab27dd6a83c4d775ce5e0d5a7625 100644 GIT binary patch literal 1462 zcmZuxUvJY^96i@@~M7;@FYxAnhIT z6(&u4LP&k#g*|L&5BmUo8lF*soohEq3&`^I^}WB}Ip-dq$~xH0-5sCLlZfS8Za%4!4fD8%Qu{Om&Kvuw}MVm zeAh$);|7WrCQuTX7#6Slv24pwzs(n(in!^_Y7{xSZ49r?Mfw^NU z>%T>@RD3~?TBlM3H>hldt{>OqP&%De6Z1G};FN_gaav$9VL4tyC0*6w`2;B+(UH2a zy7bV%S+>@KrsLJSo*Tz9eBgv_Nml(%(2@eIMsBC;$y-t6w*8Lu zSzCytw{vtM+n{AlT1R`=1@iUSX+GL;x_yXrdm{*C!W{{ehD*1TUdcr(UA*T-a-D7( zJ3&|rf-W~g*^6YW-)z={Uf7i1yGq~eXO44Gxq~yjG$uq@FH|%*F7WLnpZp#eS^fv& zB_{&sII3+m2fiAVUQ@~9NtusX+EIX6vo0Jwiz@xTPgWm?J9~R1GE0 zCJ8mgw`q>ESmKTW?Erb91TGKIcMs7Q67-TnPhhi(WjtjeQu3Uytjo@-R`EqAWtGFB5sqtFj9Aq@Vo!M487y+chaA8WGh z+%sxP3~#00@6sgRLr0d$uKtZQ+@zLUe7j98cj*3I%;6s0_<_Otk^Da;8gj@e4T*#c z0|~#fUj^<&rkYusFPhI#RC#$K#1l>Ie|U3*F#Xt@Y}g{%&$Ry+5^iAz4-x`q>K$|; z-ZBipMT$z(6ftLtqWlU*;rBm~*IyyOalW>XTxpWezc97(H*C?LsJN%`N{_hBj5>@~ k1LJ5CyG6StS=+RGj}dsp1J|eT1poj5 literal 1014 zcmZva+fvg|6o&tuq-{v)nHC{<06YQ8v1N27bySp*nWAFFYtl9Y#o978^$8fC!YjrZ zZoKiz883VQpT=tk5C7hcDJq#sR(AGU|NpI({q^VPF93=tSkRG+VI+pUK-6iroBkaE z{rb!c0b{Mx@B~toX4`w*dsXwg&)r&!Um|t)wYM0=r64Y=vJHVm)pzSJH{FBN*(*U- zF^Hugt_oPyPOn?{)|*oLT%}WYTh)VB)Ayxcch~dh_uV({FbqTla%b!dq-DlJ%Wdy2 zJgMz_bsv+^*qOXcuoFc1jB<6JQ7}ekKS6xti$ILesQM^3%rLbSNF;Q}C{GwWj&1Ce z2PFk2g321BK>?FO!+ZimXG^f$q0E00aZ1SU5-d*gEky|naS}=3r`**TM|H=32TO_^ zBW@cF$Bq!`1GhBQ!njJ$a+TxT2;~aMU=(?bQ54M4PLL3?+eE0o7pPAw^{iA^vQg=h z>buY>%6s5+l^x1SYPdkBi-f&I@JTvN)9W&Ku5fj2;F=e479>O0w}I=5a!vkm4f~>- z@g7N=6t>y#R}z?iQ7kL{IZE9i#Z6Mov)@HhERmu}wi2eWiW%JE?rnZ`RK*aC6J$gz zB`1*znR)@!+`mk=Z61+D4ZP8x{T_$JdoYn%x&wD9agQwbxmqL31O9=B7{@xN@rX0o zAWKCtn9wDILP&=Q+pL#oc1(Bl@_|as2DsDZ~EHmSz WySJ5r1?>k6A-*HabCjW|hU{ 1) { + a = a + 3; + a = a + 5; + } else { + a = a - 3; + a = a - 5; + } - if(a>1){ - a = a + 3; - a = a + 5; - } else { - a = a - 3; - a = a - 5; - } + return a; + } - return a; + int branchElseIf() { + int a = 0; + if (a < 0) { + a = a + 1; + a = a + 2; + } else if (a < 5) { + a = a - 1; + a = a - 2; + } else { + a = a * 1; + a = a * 2; } - int case5(){ - int a = 0; - if(a<0){ - a = a + 1; - a = a + 2; - } else if(a<5) { - a = a - 1; - a = a - 2; - } else{ - a = a * 1; - a = a * 2; - } - - return a; - } + return a; + } - int case6(){ - int a = 0; - for(int i = 0; i < 10; i++){ - i = i + 1; - a++; - } - return a; + int forLoop() { + int a = 0; + for (int i = 0; i < 10; i++) { + i = i + 1; + a++; } + return a; + } -} \ No newline at end of file + void reusedLocals() { + // Somewhat interesting test case since the compiler will store + // both `b`s at the same local index + // and both `a`s at the same local index. + // This also works without them having the same name. + // The case for `b` is particularly interesting, + // since without splitting the local the `TypeAssigner` will have to assign a laxer type to `b`. + { + Object a; + if (Math.random() == 0.0) { + Integer b = 1; + a = b; + } else { + String b = ""; + a = b; + } + System.out.println(a); + } + { + Object a = null; + System.out.println(a); + } + } +} diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index 9b66e1775b8..7ea59ad92c4 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -1,389 +1,330 @@ package sootup.java.bytecode.interceptors; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import categories.Java8Test; import java.util.*; -import org.apache.commons.lang3.tuple.MutablePair; -import org.apache.commons.lang3.tuple.Pair; +import java.util.stream.Collectors; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import sootup.core.jimple.basic.Local; -import sootup.core.jimple.basic.NoPositionInformation; -import sootup.core.jimple.basic.Value; -import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; import sootup.core.model.SootMethod; import sootup.core.signatures.PackageName; import sootup.core.types.ClassType; -import sootup.core.types.UnknownType; import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; import sootup.java.core.types.JavaClassType; import sootup.java.core.views.JavaView; @Category(Java8Test.class) public class LocalSplitterTest { - JavaView view; - - @Before - public void Setup() { - String classPath = "src/test/java/resources/interceptors"; - JavaClassPathAnalysisInputLocation inputLocation = new JavaClassPathAnalysisInputLocation(classPath); - view = new JavaView(inputLocation); - } - - @Test - public void testStmtToUsesSimpleAssignment(){ - Body originalBody = getBody("case0"); - LocalSplitter localSplitter = new LocalSplitter(); - Map>> actual = localSplitter.getStmtToUses(Body.builder(originalBody, Collections.emptySet())); - Map>> expected = new HashMap<>(); - - Stmt s1 = getStmt(originalBody, "l2 = 1"); - List> s1Uses = new ArrayList<>(); - Stmt l1_gets_l2_plus_1 = getStmt(originalBody, "l1 = l2 + 1"); - s1Uses.add(new MutablePair<>(l1_gets_l2_plus_1, l1_gets_l2_plus_1.getUses().get(1))); - expected.put(s1, s1Uses); - - Stmt s2 = l1_gets_l2_plus_1; - Stmt l2_gets_l1_plus_1 = getStmt(originalBody, "l2 = l1 + 1"); - List> s2Uses = new ArrayList<>(); - s2Uses.add(new MutablePair<>(l2_gets_l1_plus_1, l2_gets_l1_plus_1.getUses().get(1))); - expected.put(s2, s2Uses); - - assertTrue(expected.keySet().containsAll(actual.keySet())); - assertTrue(expected.values().containsAll(actual.values())); - assertTrue(actual.keySet().containsAll(expected.keySet())); - assertTrue(actual.values().containsAll(expected.values())); - } - - @Test - public void testStmtToUsesSelfAssignment(){ - Body originalBody = getBody("case1"); - LocalSplitter localSplitter = new LocalSplitter(); - Map>> actual = localSplitter.getStmtToUses(Body.builder(originalBody, Collections.emptySet())); - Map>> expected = new HashMap<>(); - - Stmt s1 = getStmt(originalBody, "l1 = 0"); - List> s1Uses = new ArrayList<>(); - Stmt l1_gets_l1_plus_1 = getStmt(originalBody, "l1 = l1 + 1"); - s1Uses.add(new MutablePair<>(l1_gets_l1_plus_1, l1_gets_l1_plus_1.getUses().get(1))); - expected.put(s1, s1Uses); - - Stmt s2 = getStmt(originalBody, "l2 = 1"); - Stmt l2_gets_l2_plus_1 = getStmt(originalBody, "l2 = l2 + 1"); - List> s2Uses = new ArrayList<>(); - s2Uses.add(new MutablePair<>(l2_gets_l2_plus_1, l2_gets_l2_plus_1.getUses().get(1))); - expected.put(s2, s2Uses); - - assertTrue(expected.keySet().containsAll(actual.keySet())); - assertTrue(expected.values().containsAll(actual.values())); - assertTrue(actual.keySet().containsAll(expected.keySet())); - assertTrue(actual.values().containsAll(expected.values())); - } - - - - - private Stmt getStmt(Body body, String s){ - return body.getStmts().stream().filter(e -> e.toString().equals(s)).findFirst().get(); - } - - - @Test - public void testSimpleAssignment() { - Body originalBody = getBody("case0"); - - List expectedLocals = new ArrayList<>(); - expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - - Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, view); - - Body newBody = builder.build(); - assertTrue(expectedLocals.containsAll(newBody.getLocals())); - assertTrue(newBody.getLocals().containsAll(expectedLocals)); - - String expectedStmts = "l0 := @this: LocalSplitterTarget;\n" + - "l1#1 = 0;\n" + - "l2#2 = 1;\n" + - "l1#3 = l2#2 + 1;\n" + - "l2#4 = l1#3 + 1;\n" + - "\n" + - "return;"; - - assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); - } - - private Body getBody(String methodName) { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - SootMethod sootMethod = view.getClass(type).get().getMethods().stream().filter(method -> method.getName().equals(methodName)).findFirst().get(); - return sootMethod.getBody(); - } - - @Test - public void testSelfAssignment() { - Body originalBody = getBody("case1"); - - List expectedLocals = new ArrayList<>(); - expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l2#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l2#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - - Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, view); - - Body newBody = builder.build(); - assertTrue(expectedLocals.containsAll(newBody.getLocals())); - assertTrue(newBody.getLocals().containsAll(expectedLocals)); - - String expectedStmts = "l0 := @this: LocalSplitterTarget;\n" + - "l1#1 = 0;\n" + - "l2#2 = 1;\n" + - "l1#3 = l1#1 + 1;\n" + - "l2#4 = l2#2 + 1;\n" + - "\n" + - "return;"; - - assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); - } - - @Test - public void testBranch() { - Body originalBody = getBody("case2"); - - List expectedLocals = new ArrayList<>(); - expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - - Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, view); - - Body newBody = builder.build(); - assertTrue(expectedLocals.containsAll(newBody.getLocals())); - assertTrue(newBody.getLocals().containsAll(expectedLocals)); - - String expectedStmts = "l0 := @this: LocalSplitterTarget;\n" + - "l1#1 = 0;\n" + - "\n" + - "if l1#1 >= 0 goto label1;\n" + - "l1#2 = l1#1 + 1;\n" + - "\n" + - "goto label2;\n" + - "\n" + - "label1:\n" + - "l1#3 = l1#1 - 1;\n" + - "l1#2 = l1#3 + 2;\n" + - "\n" + - "label2:\n" + - "return l1#2;"; - assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); - } - - - @Test - public void testBranchMoreLocals() { - Body originalBody = getBody("case3"); - - List expectedLocals = new ArrayList<>(); - expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#6", UnknownType.getInstance(), NoPositionInformation.getInstance())); - - Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, view); - - Body newBody = builder.build(); - assertTrue(expectedLocals.containsAll(newBody.getLocals())); - assertTrue(newBody.getLocals().containsAll(expectedLocals)); - - String expectedStmts = "l0 := @this: LocalSplitterTarget;\n" + - "l1#1 = 0;\n" + - "\n" + - "if l1#1 >= 0 goto label1;\n" + - "l1#2 = l1#1 + 1;\n" + - "l1#3 = l1#2 + 2;\n" + - "l1#4 = l1#3 + 3;\n" + - "\n" + - "goto label2;\n" + - "\n" + - "label1:\n" + - "l1#5 = l1#1 - 1;\n" + - "l1#6 = l1#5 - 2;\n" + - "l1#4 = l1#6 - 3;\n" + - "\n" + - "label2:\n" + - "return l1#4;"; - assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); - } - - - @Test - public void testBranchMoreBranches() { - Body originalBody = getBody("case4"); - - List expectedLocals = new ArrayList<>(); - expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#6", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#7", UnknownType.getInstance(), NoPositionInformation.getInstance())); - - Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, view); - - Body newBody = builder.build(); - assertTrue(expectedLocals.containsAll(newBody.getLocals())); - assertTrue(newBody.getLocals().containsAll(expectedLocals)); - - String expectedStmts = - "l0 := @this: LocalSplitterTarget;\n" + - "l1#1 = 0;\n" + - "\n" + - "if l1#1 >= 0 goto label1;\n" + - "l1#2 = l1#1 + 1;\n" + - "l1#3 = l1#2 + 2;\n" + - "\n" + - "goto label2;\n" + - "\n" + - "label1:\n" + - "l1#4 = l1#1 - 1;\n" + - "l1#3 = l1#4 - 2;\n" + - "\n" + - "label2:\n" + - "if l1#3 <= 1 goto label3;\n" + - "l1#5 = l1#3 + 3;\n" + - "l1#6 = l1#5 + 5;\n" + - "\n" + - "goto label4;\n" + - "\n" + - "label3:\n" + - "l1#7 = l1#3 - 3;\n" + - "l1#6 = l1#7 - 5;\n" + - "\n" + - "label4:\n" + - "return l1#6;"; - assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); - } - - - @Test - public void testBranchElseIf() { - Body originalBody = getBody("case5"); - - List expectedLocals = new ArrayList<>(); - expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); - - Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, view); - - Body newBody = builder.build(); - assertTrue(expectedLocals.containsAll(newBody.getLocals())); - assertTrue(newBody.getLocals().containsAll(expectedLocals)); - - String expectedStmts = - "l0 := @this: LocalSplitterTarget;\n" + - "l1#1 = 0;\n" + - "\n" + - "if l1#1 >= 0 goto label1;\n" + - "l1#2 = l1#1 + 1;\n" + - "l1#3 = l1#2 + 2;\n" + - "\n" + - "goto label3;\n" + - "\n" + - "label1:\n" + - "if l1#1 >= 5 goto label2;\n" + - "l1#4 = l1#1 - 1;\n" + - "l1#3 = l1#4 - 2;\n" + - "\n" + - "goto label3;\n" + - "\n" + - "label2:\n" + - "l1#5 = l1#1 * 1;\n" + - "l1#3 = l1#5 * 2;\n" + - "\n" + - "label3:\n" + - "return l1#3;"; - assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); - } - - - @Test - public void testForLoop() { - Body originalBody = getBody("case6"); - - List expectedLocals = new ArrayList<>(); - expectedLocals.addAll(originalBody.getLocals()); - expectedLocals.add(new Local("l1#1", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#2", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#3", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#4", UnknownType.getInstance(), NoPositionInformation.getInstance())); - expectedLocals.add(new Local("l1#5", UnknownType.getInstance(), NoPositionInformation.getInstance())); - - Body.BodyBuilder builder = Body.builder(originalBody, Collections.emptySet()); - LocalSplitter localSplitter = new LocalSplitter(); - localSplitter.interceptBody(builder, view); - - - Body newBody = builder.build(); - - System.out.println(newBody); - -// assertTrue(expectedLocals.containsAll(newBody.getLocals())); -// assertTrue(newBody.getLocals().containsAll(expectedLocals)); - - String expectedStmts = - "l0 := @this: LocalSplitterTarget;\n" + - "l1#1 = 0;\n" + - "\n" + - "if l1#1 >= 0 goto label1;\n" + - "l1#2 = l1#1 + 1;\n" + - "l1#3 = l1#2 + 2;\n" + - "\n" + - "goto label3;\n" + - "\n" + - "label1:\n" + - "if l1#1 >= 5 goto label2;\n" + - "l1#4 = l1#1 - 1;\n" + - "l1#3 = l1#4 - 2;\n" + - "\n" + - "goto label3;\n" + - "\n" + - "label2:\n" + - "l1#5 = l1#1 * 1;\n" + - "l1#3 = l1#5 * 2;\n" + - "\n" + - "label3:\n" + - "return l1#3;"; -// assertEquals(expectedStmts, newBody.getStmtGraph().toString().trim()); - } - + JavaView view; + LocalSplitter localSplitter = new LocalSplitter(); + + @Before + public void setup() { + String classPath = "src/test/java/resources/interceptors"; + JavaClassPathAnalysisInputLocation inputLocation = + new JavaClassPathAnalysisInputLocation(classPath); + view = new JavaView(inputLocation); + } + + private Body getBody(String methodName) { + ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); + SootMethod sootMethod = + view.getClass(type).get().getMethods().stream() + .filter(method -> method.getName().equals(methodName)) + .findFirst() + .get(); + return sootMethod.getBody(); + } + + void assertLocals(Set localNames, Body.BodyBuilder builder) { + assertEquals( + localNames, builder.getLocals().stream().map(Local::getName).collect(Collectors.toSet())); + } + + @Test + public void testSimpleAssignment() { + Body.BodyBuilder builder = Body.builder(getBody("simpleAssignment"), Collections.emptySet()); + localSplitter.interceptBody(builder, view); + + Set expectedLocals = new HashSet<>(); + expectedLocals.add("l0"); + expectedLocals.add("l1#0"); + expectedLocals.add("l1#1"); + expectedLocals.add("l2#0"); + expectedLocals.add("l2#1"); + + assertLocals(expectedLocals, builder); + + String expectedStmts = + "l0 := @this: LocalSplitterTarget;\n" + + "l1#0 = 0;\n" + + "l2#0 = 1;\n" + + "l1#1 = l2#0 + 1;\n" + + "l2#1 = l1#1 + 1;\n" + + "\n" + + "return;"; + + assertEquals(expectedStmts, builder.getStmtGraph().toString().trim()); + } + + @Test + public void testSelfAssignment() { + Body.BodyBuilder builder = Body.builder(getBody("selfAssignment"), Collections.emptySet()); + localSplitter.interceptBody(builder, view); + + Set expectedLocals = new HashSet<>(); + expectedLocals.add("l0"); + expectedLocals.add("l1#0"); + expectedLocals.add("l1#1"); + expectedLocals.add("l2#0"); + expectedLocals.add("l2#1"); + + assertLocals(expectedLocals, builder); + + String expectedStmts = + "l0 := @this: LocalSplitterTarget;\n" + + "l1#0 = 0;\n" + + "l2#0 = 1;\n" + + "l1#1 = l1#0 + 1;\n" + + "l2#1 = l2#0 + 1;\n" + + "\n" + + "return;"; + + assertEquals(expectedStmts, builder.getStmtGraph().toString().trim()); + } + + @Test + public void testBranch() { + Body.BodyBuilder builder = Body.builder(getBody("branch"), Collections.emptySet()); + localSplitter.interceptBody(builder, view); + + Set expectedLocals = new HashSet<>(); + expectedLocals.add("l0"); + expectedLocals.add("l1#0"); + expectedLocals.add("l1#1"); + expectedLocals.add("l1#2"); + + assertLocals(expectedLocals, builder); + + String expectedStmts = + "l0 := @this: LocalSplitterTarget;\n" + + "l1#0 = 0;\n" + + "\n" + + "if l1#0 >= 0 goto label1;\n" + + "l1#1 = l1#0 + 1;\n" + + "\n" + + "goto label2;\n" + + "\n" + + "label1:\n" + + "l1#2 = l1#0 - 1;\n" + + "l1#1 = l1#2 + 2;\n" + + "\n" + + "label2:\n" + + "return l1#1;"; + assertEquals(expectedStmts, builder.getStmtGraph().toString().trim()); + } + + @Test + public void testBranchMoreLocals() { + Body.BodyBuilder builder = Body.builder(getBody("branchMoreLocals"), Collections.emptySet()); + localSplitter.interceptBody(builder, view); + + Set expectedLocals = new HashSet<>(); + expectedLocals.add("l0"); + expectedLocals.add("l1#0"); + expectedLocals.add("l1#1"); + expectedLocals.add("l1#2"); + expectedLocals.add("l1#3"); + expectedLocals.add("l1#4"); + expectedLocals.add("l1#5"); + + assertLocals(expectedLocals, builder); + + String expectedStmts = + "l0 := @this: LocalSplitterTarget;\n" + + "l1#0 = 0;\n" + + "\n" + + "if l1#0 >= 0 goto label1;\n" + + "l1#1 = l1#0 + 1;\n" + + "l1#2 = l1#1 + 2;\n" + + "l1#3 = l1#2 + 3;\n" + + "\n" + + "goto label2;\n" + + "\n" + + "label1:\n" + + "l1#4 = l1#0 - 1;\n" + + "l1#5 = l1#4 - 2;\n" + + "l1#3 = l1#5 - 3;\n" + + "\n" + + "label2:\n" + + "return l1#3;"; + assertEquals(expectedStmts, builder.getStmtGraph().toString().trim()); + } + + @Test + public void testBranchMoreBranches() { + Body.BodyBuilder builder = Body.builder(getBody("branchMoreBranches"), Collections.emptySet()); + localSplitter.interceptBody(builder, view); + + Set expectedLocals = new HashSet<>(); + expectedLocals.add("l0"); + expectedLocals.add("l1#0"); + expectedLocals.add("l1#1"); + expectedLocals.add("l1#2"); + expectedLocals.add("l1#3"); + expectedLocals.add("l1#4"); + expectedLocals.add("l1#5"); + expectedLocals.add("l1#6"); + + assertLocals(expectedLocals, builder); + + String expectedStmts = + "l0 := @this: LocalSplitterTarget;\n" + + "l1#0 = 0;\n" + + "\n" + + "if l1#0 >= 0 goto label1;\n" + + "l1#1 = l1#0 + 1;\n" + + "l1#2 = l1#1 + 2;\n" + + "\n" + + "goto label2;\n" + + "\n" + + "label1:\n" + + "l1#3 = l1#0 - 1;\n" + + "l1#2 = l1#3 - 2;\n" + + "\n" + + "label2:\n" + + "if l1#2 <= 1 goto label3;\n" + + "l1#4 = l1#2 + 3;\n" + + "l1#5 = l1#4 + 5;\n" + + "\n" + + "goto label4;\n" + + "\n" + + "label3:\n" + + "l1#6 = l1#2 - 3;\n" + + "l1#5 = l1#6 - 5;\n" + + "\n" + + "label4:\n" + + "return l1#5;"; + assertEquals(expectedStmts, builder.getStmtGraph().toString().trim()); + } + + @Test + public void testBranchElseIf() { + Body.BodyBuilder builder = Body.builder(getBody("branchElseIf"), Collections.emptySet()); + localSplitter.interceptBody(builder, view); + + Set expectedLocals = new HashSet<>(); + expectedLocals.add("l0"); + expectedLocals.add("l1#0"); + expectedLocals.add("l1#1"); + expectedLocals.add("l1#2"); + expectedLocals.add("l1#3"); + expectedLocals.add("l1#4"); + + assertLocals(expectedLocals, builder); + + String expectedStmts = + "l0 := @this: LocalSplitterTarget;\n" + + "l1#0 = 0;\n" + + "\n" + + "if l1#0 >= 0 goto label1;\n" + + "l1#1 = l1#0 + 1;\n" + + "l1#2 = l1#1 + 2;\n" + + "\n" + + "goto label3;\n" + + "\n" + + "label1:\n" + + "if l1#0 >= 5 goto label2;\n" + + "l1#3 = l1#0 - 1;\n" + + "l1#2 = l1#3 - 2;\n" + + "\n" + + "goto label3;\n" + + "\n" + + "label2:\n" + + "l1#4 = l1#0 * 1;\n" + + "l1#2 = l1#4 * 2;\n" + + "\n" + + "label3:\n" + + "return l1#2;"; + assertEquals(expectedStmts, builder.getStmtGraph().toString().trim()); + } + + @Test + public void testForLoop() { + Body.BodyBuilder builder = Body.builder(getBody("forLoop"), Collections.emptySet()); + localSplitter.interceptBody(builder, view); + + Set expectedLocals = new HashSet<>(); + expectedLocals.add("l0"); + expectedLocals.add("l1"); + expectedLocals.add("l2#0"); + expectedLocals.add("l2#1"); + + assertLocals(expectedLocals, builder); + + String expectedStmts = + "l0 := @this: LocalSplitterTarget;\n" + + "l1 = 0;\n" + + "l2#0 = 0;\n" + + "\n" + + "label1:\n" + + "if l2#0 >= 10 goto label2;\n" + + "l2#1 = l2#0 + 1;\n" + + "l1 = l1 + 1;\n" + + "l2#0 = l2#1 + 1;\n" + + "\n" + + "goto label1;\n" + + "\n" + + "label2:\n" + + "return l1;"; + assertEquals(expectedStmts, builder.getStmtGraph().toString().trim()); + } + + @Test + public void testReusedLocals() { + Body.BodyBuilder builder = Body.builder(getBody("reusedLocals"), Collections.emptySet()); + localSplitter.interceptBody(builder, view); + + Set expectedLocals = new HashSet<>(); + expectedLocals.add("l0"); + expectedLocals.add("l1#0"); + expectedLocals.add("l1#1"); + expectedLocals.add("l2#0"); + expectedLocals.add("l2#1"); + expectedLocals.add("$stack3"); + expectedLocals.add("$stack4"); + expectedLocals.add("$stack5"); + expectedLocals.add("$stack6"); + + assertLocals(expectedLocals, builder); + + String expectedStmts = + "l0 := @this: LocalSplitterTarget;\n" + + "$stack3 = staticinvoke ();\n" + + "$stack4 = $stack3 cmpl 0.0;\n" + + "\n" + + "if $stack4 != 0 goto label1;\n" + + "l2#0 = staticinvoke (1);\n" + + "l1#0 = l2#0;\n" + + "\n" + + "goto label2;\n" + + "\n" + + "label1:\n" + + "l2#1 = \"\";\n" + + "l1#0 = l2#1;\n" + + "\n" + + "label2:\n" + + "$stack5 = ;\n" + + "virtualinvoke $stack5.(l1#0);\n" + + "l1#1 = null;\n" + + "$stack6 = ;\n" + + "virtualinvoke $stack6.(l1#1);\n" + + "\n" + + "return;"; + assertEquals(expectedStmts, builder.getStmtGraph().toString().trim()); + } } From c3098e9f2b8d5fa40d779542ada3a299f0b14b3e Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Wed, 7 Feb 2024 17:02:12 +0100 Subject: [PATCH 17/24] remove leftovers from previous `LocalSplitter` implementation --- .../java/sootup/core/graph/StmtGraph.java | 1 + .../src/main/java/sootup/core/model/Body.java | 6 - .../interceptors/defuse/DefUseHelper.java | 48 ------- .../interceptors/defuse/DefUseHelperTest.java | 127 ------------------ 4 files changed, 1 insertion(+), 181 deletions(-) delete mode 100644 sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/defuse/DefUseHelper.java delete mode 100644 sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/defuse/DefUseHelperTest.java diff --git a/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java b/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java index b828d94fcf5..f631a7a1c37 100644 --- a/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java @@ -538,6 +538,7 @@ private BasicBlock retrieveNextBlock() { return nestedBlocks.pollFirst(); } } + return null; } diff --git a/sootup.core/src/main/java/sootup/core/model/Body.java b/sootup.core/src/main/java/sootup/core/model/Body.java index 0db40b65091..30deb635bc2 100644 --- a/sootup.core/src/main/java/sootup/core/model/Body.java +++ b/sootup.core/src/main/java/sootup/core/model/Body.java @@ -370,12 +370,6 @@ public BodyBuilder addLocal(@Nonnull Local local) { return this; } - @Nonnull - public BodyBuilder removeLocal(@Nonnull Local local) { - locals.remove(local); - return this; - } - public void replaceLocal(@Nonnull Local oldLocal, @Nonnull Local newLocal) { if (!locals.contains(oldLocal)) { throw new RuntimeException("The given old local: '" + oldLocal + "' is not in the body!"); diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/defuse/DefUseHelper.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/defuse/DefUseHelper.java deleted file mode 100644 index 8393c9fb620..00000000000 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/defuse/DefUseHelper.java +++ /dev/null @@ -1,48 +0,0 @@ -package sootup.java.bytecode.interceptors.defuse; - -import sootup.core.graph.BasicBlock; -import sootup.core.jimple.basic.Local; -import sootup.core.jimple.basic.Value; -import sootup.core.jimple.common.stmt.Stmt; - -import java.util.*; - -public class DefUseHelper { - - /** - * Returns a map of locals to Stmts, such that the Stmt defines the local in the given block - * if a local is defined multiple times, we keep the most recent definition. - * - * @param block - * @return - */ - public static Map getDefsInBlock(BasicBlock block) { - Map localToDefStmt = new HashMap<>(); - for (Stmt stmt : block.getStmts()) { - if (stmt.getDefs().size() == 1) { - Local def = (Local) stmt.getDefs().get(0); - localToDefStmt.put(def, stmt); // subsequent defs will overwrite - } - } - return localToDefStmt; - } - - /** - * Returns a map of locals to Stmts, such that the Stmt uses the local in the given block. - * We only keep the first use the local - * @param block - * @return - */ - public static Map getFirstUsesInBlock(BasicBlock block) { - Map localToUseStmt = new HashMap<>(); - for (Stmt stmt : block.getStmts()) { - for (Value use : stmt.getUses()) { - if (use instanceof Local) { - localToUseStmt.putIfAbsent((Local) use, stmt); - } - } - } - return localToUseStmt; - } - -} diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/defuse/DefUseHelperTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/defuse/DefUseHelperTest.java deleted file mode 100644 index 76db7adc589..00000000000 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/defuse/DefUseHelperTest.java +++ /dev/null @@ -1,127 +0,0 @@ -package sootup.java.bytecode.interceptors.defuse; - -import categories.Java8Test; -import org.junit.Before; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import sootup.core.jimple.basic.Local; -import sootup.core.jimple.common.stmt.Stmt; -import sootup.core.model.Body; -import sootup.core.model.SootMethod; -import sootup.core.signatures.MethodSignature; -import sootup.core.signatures.MethodSubSignature; -import sootup.core.signatures.PackageName; -import sootup.core.types.ClassType; -import sootup.core.types.PrimitiveType; -import sootup.core.types.Type; -import sootup.core.types.VoidType; -import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; -import sootup.java.core.types.JavaClassType; -import sootup.java.core.views.JavaView; - -import java.util.*; - -import static org.junit.Assert.assertTrue; - -@Category(Java8Test.class) -public class DefUseHelperTest { - - JavaView view; - - @Before - public void Setup() { - String classPath = "src/test/java/resources/interceptors"; - JavaClassPathAnalysisInputLocation inputLocation = new JavaClassPathAnalysisInputLocation(classPath); - view = new JavaView(inputLocation); - } - - private Body getBody(String methodName, Type returnType) { - ClassType type = new JavaClassType("LocalSplitterTarget", PackageName.DEFAULT_PACKAGE); - MethodSignature sig = new MethodSignature(type, new MethodSubSignature(methodName, Collections.EMPTY_LIST, returnType)); - SootMethod sootMethod = view.getMethod(sig).get(); - Body originalBody = sootMethod.getBody(); - return originalBody; - } - - private Stmt getStmt(Body body, String s){ - return body.getStmts().stream().filter(e -> e.toString().equals(s)).findFirst().get(); - } - - @Test - public void testSimpleAssign(){ - Body originalBody = getBody("case0", VoidType.getInstance()); - Map actual = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(0)); - - Stmt s1 = getStmt(originalBody, "$l1 = $l2 + 1"); - Local def1 = (Local) s1.getDefs().get(0); - - Stmt s2 = getStmt(originalBody, "$l2 = $l1 + 1"); - Local def2 = (Local) s2.getDefs().get(0); - - assertTrue(actual.get(def1).equals(s1)); - assertTrue(actual.get(def2).equals(s2)); - } - - @Test - public void testSelfAssign(){ - Body originalBody = getBody("case1", VoidType.getInstance()); - Map actual = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(0)); - - Stmt s1 = getStmt(originalBody, "$l1 = $l1 + 1"); - Local def1 = (Local) s1.getDefs().get(0); - - Stmt s2 = getStmt(originalBody, "$l2 = $l2 + 1"); - Local def2 = (Local) s2.getDefs().get(0); - - assertTrue(actual.get(def1).equals(s1)); - assertTrue(actual.get(def2).equals(s2)); - } - - @Test - public void testBranch(){ - Body originalBody = getBody("case2", PrimitiveType.IntType.getInstance()); - Map defsInBlock0 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(0)); - Stmt s0 = getStmt(originalBody, "$l1 = 0"); - Local def0 = (Local) s0.getDefs().get(0); - assertTrue(defsInBlock0.get(def0).equals(s0)); - - Map defsInBlock1 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(1)); - Stmt s1 = getStmt(originalBody, "$l1 = $l1 + 1"); - Local def1 = (Local) s1.getDefs().get(0); - assertTrue(defsInBlock1.get(def1).equals(s1)); - - Map defsInBlock2 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(2)); - Stmt s2 = getStmt(originalBody, "$l1 = $l1 + 2"); - Local def2 = (Local) s1.getDefs().get(0); - assertTrue(defsInBlock2.get(def2).equals(s2)); - - - System.out.println(defsInBlock2); - System.out.println(originalBody); - - } - - @Test - public void testMultiBranch(){ - Body originalBody = getBody("case3", PrimitiveType.IntType.getInstance()); - Map defsInBlock0 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(0)); - Stmt s0 = getStmt(originalBody, "$l1 = 0"); - Local def0 = (Local) s0.getDefs().get(0); - assertTrue(defsInBlock0.get(def0).equals(s0)); - - Map defsInBlock1 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(1)); - Stmt s1 = getStmt(originalBody, "$l1 = $l1 + 3"); - Local def1 = (Local) s1.getDefs().get(0); - assertTrue(defsInBlock1.get(def1).equals(s1)); - - Map defsInBlock2 = DefUseHelper.getDefsInBlock(originalBody.getStmtGraph().getBlocksSorted().get(2)); - Stmt s2 = getStmt(originalBody, "$l1 = $l1 - 3"); - Local def2 = (Local) s1.getDefs().get(0); - assertTrue(defsInBlock2.get(def2).equals(s2)); - - - System.out.println(defsInBlock2); - System.out.println(originalBody); - - } -} From 0382b0e2f2bc11d322890a8d02e179db36bde5c3 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Wed, 7 Feb 2024 17:50:14 +0100 Subject: [PATCH 18/24] don't explicitly track the number of sets in `DisjointSetForest` --- .../sootup/java/bytecode/interceptors/LocalSplitter.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 6714d28c2bd..8716da10126 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -50,8 +50,6 @@ static class DisjointSetForest { /** Stores the size of a tree under the key. Only updated for roots of trees. */ private final Map sizes = new HashMap<>(); - private int setCount = 0; - /** * Creates a new set that only contains the {@code node}. Does nothing when the forest already * contains the {@code node}. @@ -61,7 +59,6 @@ void add(T node) { parent.put(node, node); sizes.put(node, 1); - setCount++; } /** Finds the representative of the set that contains the {@code node}. */ @@ -97,13 +94,13 @@ T union(T first, T second) { sizes.put(larger, sizes.get(smaller) + sizes.get(larger)); sizes.remove(smaller); - setCount--; - return larger; } int getSetCount() { - return setCount; + // `sizes` only contains values for the root of the trees, + // so its size matches the total number of sets + return sizes.size(); } } From f2612f0be0164b3e805ed657227ac2791b12a178 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 8 Feb 2024 12:58:13 +0100 Subject: [PATCH 19/24] fix `LocalSplitter` different locals with the same name Debug names can cause different locals (with different local indices) to be given the same name. This is probably not correct, but the `LocalSplitter` has to support it anyway. --- .../java/sootup/java/bytecode/interceptors/LocalSplitter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 8716da10126..03e02178373 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -223,7 +223,7 @@ List findAllAssignmentsToLocal( return builder.getStmts().stream() .filter(stmt -> stmt instanceof AbstractDefinitionStmt) .map(stmt -> (AbstractDefinitionStmt) stmt) - .filter(stmt -> stmt.getLeftOp() == local) + .filter(stmt -> stmt.getLeftOp().equals(local)) .collect(Collectors.toList()); } } From 42ad51f39ed81365ca4f7c065f9b1b682d3fdb3f Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 8 Feb 2024 13:29:00 +0100 Subject: [PATCH 20/24] fix `LocalSplitter` not working with exceptional successors (traps/exceptions) --- .../bytecode/interceptors/LocalSplitter.java | 2 + .../interceptors/LocalSplitterTarget.class | Bin 1462 -> 1774 bytes .../interceptors/LocalSplitterTarget.java | 18 +++++ .../interceptors/LocalSplitterTest.java | 63 ++++++++++++++++++ 4 files changed, 83 insertions(+) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 03e02178373..eeac652d63e 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -152,6 +152,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) disjointSet.add(defStmt); Queue queue = new ArrayDeque<>(graph.successors(assignment)); + queue.addAll(graph.exceptionalSuccessors(assignment).values()); Set visited = new HashSet<>(); while (!queue.isEmpty()) { @@ -173,6 +174,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) // otherwise continue by adding all successors to the queue if (!stmt.getDefs().contains(local)) { queue.addAll(graph.successors(stmt)); + queue.addAll(graph.exceptionalSuccessors(stmt).values()); } } } diff --git a/sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class b/sootup.java.bytecode/src/test/java/resources/interceptors/LocalSplitterTarget.class index 42ccbaa08193ab27dd6a83c4d775ce5e0d5a7625..fbf57011fa29049b8df8cfdce83d1ac659214a05 100644 GIT binary patch delta 690 zcmZ9KO-~b16o%h>=S~Nv9W2FG5wIZ$lzuQO6hx*Nle!@y3tg~eAcT()N=lIcM*RV< zoSlDwZE=A_6IZTW`X}7EHl~R0EfcG3&ewhJd(J&GC($2{{q^_9&j21^=Gb<{!iN9c zcQ^g5RrlFiwX)|gZLZkp#jP09h}amyu!T$vofr{_x8QPRm!v>4QyzJIxjbzl7fgsR zV`i=DZ|?|P&HM|^FKw(W*9cAjC;7BiUEf*_eu~^o7Lp?ZHUvyWX!Fea=mou*{Qz;y zK%hXYk97=;UZ8<5Z;C?jTK3Jle=ip)X^?RYPnCMqqSvbyOD&NU`{;fWQ7FXF28XtT zwcEHu@qF-IW+)mr(y6zNsvUGnMYv1m``265RfNnLY7~XqUJee R44qoI->gWE=n<7D{Q>LsYnT84 delta 381 zcmYMvOG*Pl6a~=x>SHI}wx57eF)>J@CLOgE6cIW)aip6FZo)kXf>18NEo7qLKycv1 zxnp-AIFlA0}=rX+R&UKfeH@a2taT-CDNF`#M1@NMG31ZV9gE>hE`T?ApV}dKQzu*(9 zFePRsn)VqRfxMc6q+#=UPb$!!{EE#W&TK|;X expectedLocals = new HashSet<>(); + expectedLocals.add("l0"); + expectedLocals.add("l1#0"); + expectedLocals.add("l1#1"); + expectedLocals.add("l2#0"); + expectedLocals.add("l2#1"); + expectedLocals.add("l3"); + expectedLocals.add("$stack4"); + expectedLocals.add("$stack5"); + expectedLocals.add("$stack6"); + expectedLocals.add("$stack7"); + expectedLocals.add("$stack8"); + + assertLocals(expectedLocals, builder); + + String expectedStmts = + "l0 := @this: LocalSplitterTarget;\n" + + "l1#0 = 1;\n" + + "\n" + + "label1:\n" + + "l1#1 = 2;\n" + + "\n" + + "label2:\n" + + "goto label4;\n" + + "\n" + + "label3:\n" + + "$stack7 := @caughtexception;\n" + + "l2#0 = $stack7;\n" + + "$stack8 = staticinvoke (l1#1);\n" + + "\n" + + "return $stack8;\n" + + "\n" + + "label4:\n" + + "l2#1 = \"\";\n" + + "\n" + + "label5:\n" + + "$stack4 = ;\n" + + "virtualinvoke $stack4.();\n" + + "\n" + + "label6:\n" + + "goto label8;\n" + + "\n" + + "label7:\n" + + "$stack6 := @caughtexception;\n" + + "l3 = $stack6;\n" + + "\n" + + "return l2#1;\n" + + "\n" + + "label8:\n" + + "$stack5 = staticinvoke (0.0);\n" + + "\n" + + "return $stack5;\n" + + "\n" + + " catch java.lang.Throwable from label1 to label2 with label3;\n" + + " catch java.lang.Throwable from label5 to label6 with label7;"; + assertEquals(expectedStmts, builder.getStmtGraph().toString().trim()); + } } From 0be76b1e3b4a32e53bbcab2ae51e705bf800d303 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 8 Feb 2024 15:32:36 +0100 Subject: [PATCH 21/24] `LocalSplitter` performance optimizations - use `graph.getNodes()` instead of `builder.getStmts()` because the linearized ordering is not important for the `LocalSplitter` (it uses `successors`/`exceptionalSuccessors` to step through the statements) - exit early when there is only one assignment or when the local can't be split - use a stack instead of a queue (iteration order makes no difference) - only search through all statements for definitions once instead of repeating it for every local Potential speedups still remaining: - Calls to `successors` use `indexOf` which can be slow; this only affects cases where there are thousands of statements without any control flow and heavy re-use of locals - `getUses`, `getDefs` and `getUsesAndDefs` show up in the profiler; these could probably be cached - Most of the time is spent hashing. There are ways to improve this. --- .../bytecode/interceptors/LocalSplitter.java | 59 +++++++++++++------ .../interceptors/LocalSplitterTest.java | 19 ++++++ 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index eeac652d63e..07979cccc41 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -140,41 +140,54 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) // so it can't be in any of the original local names since it might cause name collisions assert builder.getLocals().stream().noneMatch(local -> local.getName().contains("#")); + // Cache the statements to not have to retrieve them for every local + List statements = new ArrayList<>(graph.getNodes()); + // Maps every local to its assignment statements. + // Contains indices to the above list to reduce bookkeeping when modifying statements. + Map> assignmentsByLocal = groupAssignmentsByLocal(statements); + Set newLocals = new HashSet<>(); for (Local local : builder.getLocals()) { // TODO explain why a disjoint set is used here DisjointSetForest disjointSet = new DisjointSetForest<>(); - List assignments = findAllAssignmentsToLocal(builder, local); + List assignments = + assignmentsByLocal.getOrDefault(local, Collections.emptyList()).stream() + .map(i -> (AbstractDefinitionStmt) statements.get(i)) + .collect(Collectors.toList()); + + if (assignments.size() <= 1) { + // There is only a single assignment to the local, so no splitting is necessary + newLocals.add(local); + continue; + } + for (AbstractDefinitionStmt assignment : assignments) { WrappedStmt defStmt = new WrappedStmt(assignment, true); disjointSet.add(defStmt); - Queue queue = new ArrayDeque<>(graph.successors(assignment)); - queue.addAll(graph.exceptionalSuccessors(assignment).values()); + List stack = new ArrayList<>(graph.successors(assignment)); + stack.addAll(graph.exceptionalSuccessors(assignment).values()); Set visited = new HashSet<>(); - while (!queue.isEmpty()) { - Stmt stmt = queue.remove(); + while (!stack.isEmpty()) { + Stmt stmt = stack.remove(stack.size() - 1); if (!visited.add(stmt)) { continue; } if (stmt.getUses().contains(local)) { - // TODO might be able to stop short when running into a non-trivial set - // since from that point onward the previous walk that crated - // that set already walked everything following the current statement WrappedStmt useStmt = new WrappedStmt(stmt, false); disjointSet.add(useStmt); disjointSet.union(defStmt, useStmt); } // a new assignment to the local -> end walk here - // otherwise continue by adding all successors to the queue + // otherwise continue by adding all successors to the stack if (!stmt.getDefs().contains(local)) { - queue.addAll(graph.successors(stmt)); - queue.addAll(graph.exceptionalSuccessors(stmt).values()); + stack.addAll(graph.successors(stmt)); + stack.addAll(graph.exceptionalSuccessors(stmt).values()); } } } @@ -188,7 +201,8 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) Map representativeToNewLocal = new HashMap<>(); final int[] nextId = {0}; - for (Stmt stmt : builder.getStmts()) { + for (int i = 0; i < statements.size(); i++) { + Stmt stmt = statements.get(i); if (!stmt.getUsesAndDefs().contains(local)) { continue; } @@ -214,18 +228,25 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) } graph.replaceNode(oldStmt, stmt); + statements.set(i, stmt); } } builder.setLocals(newLocals); } - List findAllAssignmentsToLocal( - @Nonnull Body.BodyBuilder builder, Local local) { - return builder.getStmts().stream() - .filter(stmt -> stmt instanceof AbstractDefinitionStmt) - .map(stmt -> (AbstractDefinitionStmt) stmt) - .filter(stmt -> stmt.getLeftOp().equals(local)) - .collect(Collectors.toList()); + Map> groupAssignmentsByLocal(List statements) { + Map> groupings = new HashMap<>(); + + for (int i = 0; i < statements.size(); i++) { + Stmt stmt = statements.get(i); + if (!(stmt instanceof AbstractDefinitionStmt)) continue; + AbstractDefinitionStmt defStmt = (AbstractDefinitionStmt) stmt; + if (!(defStmt.getLeftOp() instanceof Local)) continue; + + groupings.computeIfAbsent((Local) defStmt.getLeftOp(), x -> new ArrayList<>()).add(i); + } + + return groupings; } } diff --git a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java index d48c85e7638..cc40f60384c 100644 --- a/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java +++ b/sootup.java.bytecode/src/test/java/sootup/java/bytecode/interceptors/LocalSplitterTest.java @@ -6,14 +6,17 @@ import java.util.*; import java.util.stream.Collectors; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import sootup.core.jimple.basic.Local; import sootup.core.model.Body; import sootup.core.model.SootMethod; +import sootup.core.model.SourceType; import sootup.core.signatures.PackageName; import sootup.core.types.ClassType; import sootup.java.bytecode.inputlocation.JavaClassPathAnalysisInputLocation; +import sootup.java.bytecode.inputlocation.JrtFileSystemAnalysisInputLocation; import sootup.java.core.types.JavaClassType; import sootup.java.core.views.JavaView; @@ -45,6 +48,22 @@ void assertLocals(Set localNames, Body.BodyBuilder builder) { localNames, builder.getLocals().stream().map(Local::getName).collect(Collectors.toSet())); } + @Test + @Ignore("Takes too long. Good for profiling though.") + public void JRT() { + JrtFileSystemAnalysisInputLocation inputLocation = + new JrtFileSystemAnalysisInputLocation( + SourceType.Library, Collections.singletonList(localSplitter)); + JavaView view = new JavaView(Collections.singletonList(inputLocation)); + + // ~200_000 methods + view.getClasses() + .parallelStream() + .flatMap(clazz -> clazz.getMethods().stream()) + .filter(method -> !method.isAbstract() && !method.isNative()) + .forEach(SootMethod::getBody); + } + @Test public void testSimpleAssignment() { Body.BodyBuilder builder = Body.builder(getBody("simpleAssignment"), Collections.emptySet()); From 56fdb589803a6e9a32b9111b86b15bb9c2992a07 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 8 Feb 2024 15:52:00 +0100 Subject: [PATCH 22/24] add comments to `LocalSplitter` --- .../bytecode/interceptors/LocalSplitter.java | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 07979cccc41..1e9577b4e31 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -23,6 +23,7 @@ */ import java.util.*; +import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nonnull; import sootup.core.graph.*; @@ -80,11 +81,11 @@ T find(T node) { * Combines the sets of {@code first} and {@code second}. Returns the representative of the * combined set. */ - T union(T first, T second) { + void union(T first, T second) { first = find(first); second = find(second); - if (first == second) return first; + if (first == second) return; T smaller = (sizes.get(first) > sizes.get(second)) ? second : first; T larger = (smaller == first) ? second : first; @@ -93,8 +94,6 @@ T union(T first, T second) { parent.put(smaller, larger); sizes.put(larger, sizes.get(smaller) + sizes.get(larger)); sizes.remove(smaller); - - return larger; } int getSetCount() { @@ -104,11 +103,16 @@ int getSetCount() { } } - static class WrappedStmt { + static class PartialStmt { @Nonnull final Stmt inner; + + /** + * Whether the partial statement refers to only the definitions of the inner statement or only + * to the uses. + */ final boolean isDef; - WrappedStmt(@Nonnull Stmt inner, boolean isDef) { + PartialStmt(@Nonnull Stmt inner, boolean isDef) { this.inner = inner; this.isDef = isDef; } @@ -118,7 +122,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - WrappedStmt that = (WrappedStmt) o; + PartialStmt that = (PartialStmt) o; if (isDef != that.isDef) return false; return inner.equals(that.inner); @@ -149,8 +153,12 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) Set newLocals = new HashSet<>(); for (Local local : builder.getLocals()) { - // TODO explain why a disjoint set is used here - DisjointSetForest disjointSet = new DisjointSetForest<>(); + // Use a disjoint set while walking the statement graph to union all uses of the local that + // can be reached from each definition. This will automatically union definitions that have + // overlapping uses and therefore can't be split. + // It uses a `PartialStmt` instead of `Stmt` because a statement might contain both a + // definition and use of a local, and they need to be processed separately. + DisjointSetForest disjointSet = new DisjointSetForest<>(); List assignments = assignmentsByLocal.getOrDefault(local, Collections.emptyList()).stream() @@ -163,8 +171,10 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) continue; } + // Walk the statement graph starting from every definition and union all uses until a + // different definition is encountered. for (AbstractDefinitionStmt assignment : assignments) { - WrappedStmt defStmt = new WrappedStmt(assignment, true); + PartialStmt defStmt = new PartialStmt(assignment, true); disjointSet.add(defStmt); List stack = new ArrayList<>(graph.successors(assignment)); @@ -178,7 +188,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) } if (stmt.getUses().contains(local)) { - WrappedStmt useStmt = new WrappedStmt(stmt, false); + PartialStmt useStmt = new PartialStmt(stmt, false); disjointSet.add(useStmt); disjointSet.union(defStmt, useStmt); } @@ -198,8 +208,9 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) continue; } - Map representativeToNewLocal = new HashMap<>(); - final int[] nextId = {0}; + // Split locals, according to the disjoint sets found above. + Map representativeToNewLocal = new HashMap<>(); + final int[] nextId = {0}; // Java quirk; just an `int` doesn't work for (int i = 0; i < statements.size(); i++) { Stmt stmt = statements.get(i); @@ -209,20 +220,20 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) Stmt oldStmt = stmt; + Function getNewLocal = + isDef -> + representativeToNewLocal.computeIfAbsent( + disjointSet.find(new PartialStmt(oldStmt, isDef)), + s -> local.withName(local.getName() + "#" + (nextId[0]++))); + if (stmt.getDefs().contains(local)) { - Local newDefLocal = - representativeToNewLocal.computeIfAbsent( - disjointSet.find(new WrappedStmt(oldStmt, true)), - s -> local.withName(local.getName() + "#" + (nextId[0]++))); + Local newDefLocal = getNewLocal.apply(true); newLocals.add(newDefLocal); stmt = ((AbstractDefinitionStmt) stmt).withNewDef(newDefLocal); } if (stmt.getUses().contains(local)) { - Local newUseLocal = - representativeToNewLocal.computeIfAbsent( - disjointSet.find(new WrappedStmt(oldStmt, false)), - s -> local.withName(local.getName() + "#" + (nextId[0]++))); + Local newUseLocal = getNewLocal.apply(false); newLocals.add(newUseLocal); stmt = stmt.withNewUse(local, newUseLocal); } From 510fdffd6279f80de45d60b84941becb57519b5e Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 8 Feb 2024 15:56:02 +0100 Subject: [PATCH 23/24] ensure deterministic naming in `LocalSplitter` Previously used `getNodes` instead of `getStmts` for a small performance improvement. This unintentionally made the naming of locals non-deterministic. --- .../java/sootup/java/bytecode/interceptors/LocalSplitter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 1e9577b4e31..05be2005a19 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -145,7 +145,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) assert builder.getLocals().stream().noneMatch(local -> local.getName().contains("#")); // Cache the statements to not have to retrieve them for every local - List statements = new ArrayList<>(graph.getNodes()); + List statements = new ArrayList<>(graph.getStmts()); // Maps every local to its assignment statements. // Contains indices to the above list to reduce bookkeeping when modifying statements. Map> assignmentsByLocal = groupAssignmentsByLocal(statements); From 3cd67214174bc940150ce06459f1fcafb4273db3 Mon Sep 17 00:00:00 2001 From: Tim Balsfulland Date: Thu, 8 Feb 2024 16:21:34 +0100 Subject: [PATCH 24/24] re-add `LocalSplitter` javadoc --- .../bytecode/interceptors/LocalSplitter.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java index 05be2005a19..f860a9621fc 100644 --- a/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java +++ b/sootup.java.bytecode/src/main/java/sootup/java/bytecode/interceptors/LocalSplitter.java @@ -34,6 +34,32 @@ import sootup.core.transform.BodyInterceptor; import sootup.core.views.View; +/** + * A BodyInterceptor that attempts to identify and separate uses of a local variable (definition) + * that are independent of each other. + * + *

For example the code: + * + *

+ *    l0 := @this Test
+ *    l1 = 0
+ *    l2 = 1
+ *    l1 = l1 + 1
+ *    l2 = l2 + 1
+ *    return
+ * 
+ * + * to: + * + *
+ *    l0 := @this Test
+ *    l1#0 = 0
+ *    l2#0 = 1
+ *    l1#1 = l1#0 + 1
+ *    l2#1 = l2#0 + 1
+ *    return
+ * 
+ */ public class LocalSplitter implements BodyInterceptor { /**