diff --git a/docs/assets/figures/SSA Example_1.png b/docs/assets/figures/SSA Example_1.png deleted file mode 100644 index 31dcde3e7eb..00000000000 Binary files a/docs/assets/figures/SSA Example_1.png and /dev/null differ diff --git a/docs/assets/figures/SSA Example_2.png b/docs/assets/figures/SSA Example_2.png deleted file mode 100644 index fb345a8926b..00000000000 Binary files a/docs/assets/figures/SSA Example_2.png and /dev/null differ diff --git a/docs/assets/figures/SSA_Example.png b/docs/assets/figures/SSA_Example.png new file mode 100644 index 00000000000..87c0d80e702 Binary files /dev/null and b/docs/assets/figures/SSA_Example.png differ diff --git a/shared-test-resources/bugfixes/ForLoopSSA.class b/shared-test-resources/bugfixes/ForLoopSSA.class new file mode 100644 index 00000000000..2925722db7c Binary files /dev/null and b/shared-test-resources/bugfixes/ForLoopSSA.class differ diff --git a/shared-test-resources/bugfixes/ForLoopSSA.java b/shared-test-resources/bugfixes/ForLoopSSA.java new file mode 100644 index 00000000000..29fbd274fdd --- /dev/null +++ b/shared-test-resources/bugfixes/ForLoopSSA.java @@ -0,0 +1,9 @@ +class ForLoopSSA { + public static void main(String[] args) { + String input = ""; + for (int i = 0; i < args.length; i++) { + input = input + args[i]; + } + System.out.println(input); + } +} \ No newline at end of file diff --git a/shared-test-resources/bugfixes/TrapSSA.class b/shared-test-resources/bugfixes/TrapSSA.class new file mode 100644 index 00000000000..c5a80d66390 Binary files /dev/null and b/shared-test-resources/bugfixes/TrapSSA.class differ diff --git a/shared-test-resources/bugfixes/TrapSSA.java b/shared-test-resources/bugfixes/TrapSSA.java new file mode 100644 index 00000000000..164e5efd46c --- /dev/null +++ b/shared-test-resources/bugfixes/TrapSSA.java @@ -0,0 +1,11 @@ +import java.io.*; +class TrapSSA { + public static void main(String[] args) throws IOException { + byte[] data; + try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { + output.write(args[0].getBytes("UTF-8")); + data = output.toByteArray(); + } + System.out.println(new String(data, "UTF-8")); + } +} \ No newline at end of file diff --git a/sootup.core/src/main/java/sootup/core/graph/BlockAnalysisDirection.java b/sootup.core/src/main/java/sootup/core/graph/BlockAnalysisDirection.java new file mode 100644 index 00000000000..f9419d5716c --- /dev/null +++ b/sootup.core/src/main/java/sootup/core/graph/BlockAnalysisDirection.java @@ -0,0 +1,63 @@ +package sootup.core.graph; +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2024 Junjie Shen + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.Collections; +import java.util.List; +import javax.annotation.Nonnull; + +public enum BlockAnalysisDirection { + POSTORDERBACKWARD { + @Override + @Nonnull + List> getPredecessors(BasicBlock block) { + // todo: Blocks in PostOrderBackward contain no exceptional blocks!! + return (List>) block.getSuccessors(); + } + + @Nonnull + @Override + List> getSortedBlocks(StmtGraph blockGraph) { + return Collections.unmodifiableList(PostOrderBlockTraversal.getBlocksSorted(blockGraph)); + } + }, + REVERSEPOSTORDERFORWARD { + @Override + @Nonnull + List> getPredecessors(BasicBlock block) { + return (List>) block.getPredecessors(); + } + + @Nonnull + @Override + List> getSortedBlocks(StmtGraph blockGraph) { + return Collections.unmodifiableList( + ReversePostOrderBlockTraversal.getBlocksSorted(blockGraph)); + } + }; + + @Nonnull + abstract List> getPredecessors(BasicBlock block); + + @Nonnull + abstract List> getSortedBlocks(StmtGraph blockGraph); +} diff --git a/sootup.core/src/main/java/sootup/core/graph/BlockIterator.java b/sootup.core/src/main/java/sootup/core/graph/BlockIterator.java new file mode 100644 index 00000000000..aaeb37c541d --- /dev/null +++ b/sootup.core/src/main/java/sootup/core/graph/BlockIterator.java @@ -0,0 +1,25 @@ +package sootup.core.graph; +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2024 Junjie Shen + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +import java.util.Iterator; + +public interface BlockIterator extends Iterator> {} diff --git a/sootup.core/src/main/java/sootup/core/graph/BlockTraversal.java b/sootup.core/src/main/java/sootup/core/graph/BlockTraversal.java new file mode 100644 index 00000000000..5bcb4fb635c --- /dev/null +++ b/sootup.core/src/main/java/sootup/core/graph/BlockTraversal.java @@ -0,0 +1,23 @@ +package sootup.core.graph; +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2024 Junjie Shen + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +public interface BlockTraversal {} diff --git a/sootup.core/src/main/java/sootup/core/graph/DominanceFinder.java b/sootup.core/src/main/java/sootup/core/graph/DominanceFinder.java index 653c87bac25..27d722b70e0 100644 --- a/sootup.core/src/main/java/sootup/core/graph/DominanceFinder.java +++ b/sootup.core/src/main/java/sootup/core/graph/DominanceFinder.java @@ -24,78 +24,55 @@ import java.util.*; import javax.annotation.Nonnull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * @author Zun Wang * @see * https://www.researchgate.net/publication/2569680_A_Simple_Fast_Dominance_Algorithm */ public class DominanceFinder { + private static Logger LOGGER = LoggerFactory.getLogger(DominanceFinder.class); + private List> blocks; private Map, Integer> blockToIdx = new HashMap<>(); private int[] doms; private ArrayList[] domFrontiers; + private BlockAnalysisDirection direction; - protected AnalysisDirection direction; - - public enum AnalysisDirection { - BACKWARD { - @Override - @Nonnull - List> getPredecessors(BasicBlock block) { - return block.getSuccessors(); - } - - @Nonnull - @Override - List> getSortedBlocks(StmtGraph blockGraph) { - return Collections.unmodifiableList(new BackwardsStmtGraph(blockGraph).getBlocksSorted()); - } - }, - FORWARD { - @Override - @Nonnull - List> getPredecessors(BasicBlock block) { - return block.getPredecessors(); - } - - @Nonnull - @Override - List> getSortedBlocks(StmtGraph blockGraph) { - return Collections.unmodifiableList(blockGraph.getBlocksSorted()); - } - }; - - @Nonnull - abstract List> getPredecessors(BasicBlock block); - - @Nonnull - abstract List> getSortedBlocks(StmtGraph blockGraph); + public DominanceFinder(StmtGraph blockGraph) { + // normal DominanceFinder should be in reverse post order + this(blockGraph, BlockAnalysisDirection.REVERSEPOSTORDERFORWARD); } - public DominanceFinder(@Nonnull StmtGraph blockGraph) { - this(blockGraph, AnalysisDirection.FORWARD); - } + protected DominanceFinder(@Nonnull StmtGraph blockGraph, BlockAnalysisDirection direction) { - protected DominanceFinder(@Nonnull StmtGraph blockGraph, AnalysisDirection direction) { + // define the blocks' order this.direction = direction; - - // we're locked into providing a List>, not a List>, so - // we'll use the block iterator directly (which provides this type) rather than - // #getBlocksSorted. blocks = direction.getSortedBlocks(blockGraph); - - // assign each block a integer id. The starting block must have id 0; rely on - // getBlocksSorted to have put the starting block first. for (int i = 0; i < blocks.size(); i++) { BasicBlock block = blocks.get(i); blockToIdx.put(block, i); } - final BasicBlock startingStmtBlock = blocks.get(0); // initialize doms + final BasicBlock startBlock; + if (direction == BlockAnalysisDirection.REVERSEPOSTORDERFORWARD + || direction == BlockAnalysisDirection.POSTORDERBACKWARD) { + startBlock = blocks.get(0); + if (direction == BlockAnalysisDirection.POSTORDERBACKWARD) { + // todo: Postdominantor (POSTORDERBACKWARD) doesn't work for with multiple tail-blocks. + List> tails = blockGraph.getTailStmtBlocks(); + if (tails.size() > 1) { + LOGGER.warn( + "BlockGraph has multiple tail-blocks, the Post-Dominators Computation could be incorrect!"); + } + } + } else { + throw new RuntimeException("Invalid BlockAnalysisDirection!"); + } doms = new int[blocks.size()]; Arrays.fill(doms, -1); doms[0] = 0; @@ -105,12 +82,11 @@ protected DominanceFinder(@Nonnull StmtGraph blockGraph, AnalysisDirection di while (isChanged) { isChanged = false; for (BasicBlock block : blocks) { - if (block.equals(startingStmtBlock)) { + if (block.equals(startBlock)) { continue; } int blockIdx = blockToIdx.get(block); List> preds = new ArrayList<>(direction.getPredecessors(block)); - // ms: should not be necessary preds.addAll(block.getExceptionalPredecessors()); int newIdom = getFirstDefinedBlockPredIdx(preds); if (!preds.isEmpty() && newIdom != -1) { BasicBlock processed = blocks.get(newIdom); @@ -131,19 +107,16 @@ protected DominanceFinder(@Nonnull StmtGraph blockGraph, AnalysisDirection di } } - // startBlockId should not have immediate dominator, actually. - doms[0] = -1; - // initialize domFrontiers - domFrontiers = new ArrayList[blockGraph.getBlocks().size()]; + domFrontiers = new ArrayList[blocks.size()]; for (int i = 0; i < domFrontiers.length; i++) { domFrontiers[i] = new ArrayList<>(); } + doms[0] = -1; // calculate dominance frontiers for each block for (BasicBlock block : blocks) { List> preds = new ArrayList<>(direction.getPredecessors(block)); - // ms: should not be necessary preds.addAll(block.getExceptionalPredecessors()); if (preds.size() > 1) { int blockId = blockToIdx.get(block); for (BasicBlock pred : preds) { @@ -155,6 +128,14 @@ protected DominanceFinder(@Nonnull StmtGraph blockGraph, AnalysisDirection di } } } + + if (direction == BlockAnalysisDirection.POSTORDERBACKWARD) { + for (int i = 0; i < domFrontiers.length; i++) { + if (domFrontiers[i].contains(i)) { + domFrontiers[i].remove(new Integer(i)); + } + } + } } public void replaceBlock(@Nonnull BasicBlock newBlock, BasicBlock oldBlock) { diff --git a/sootup.core/src/main/java/sootup/core/graph/DominanceTree.java b/sootup.core/src/main/java/sootup/core/graph/DominanceTree.java index c77b069e317..1234556558b 100644 --- a/sootup.core/src/main/java/sootup/core/graph/DominanceTree.java +++ b/sootup.core/src/main/java/sootup/core/graph/DominanceTree.java @@ -47,7 +47,7 @@ public DominanceTree(@Nonnull DominanceFinder dominanceFinder) { } for (int i = 0; i < treeSize; i++) { - if (iDoms[i] != i) { + if (iDoms[i] != -1 && iDoms[i] != i) { parents[i] = iDoms[i]; children[iDoms[i]].add(i); } diff --git a/sootup.core/src/main/java/sootup/core/graph/ForwardingStmtGraph.java b/sootup.core/src/main/java/sootup/core/graph/ForwardingStmtGraph.java index 80c4393323e..9feb725460d 100644 --- a/sootup.core/src/main/java/sootup/core/graph/ForwardingStmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/ForwardingStmtGraph.java @@ -50,6 +50,11 @@ public BasicBlock getStartingStmtBlock() { return backingGraph.getStartingStmtBlock(); } + @Override + public List> getTailStmtBlocks() { + return backingGraph.getTailStmtBlocks(); + } + @Override public BasicBlock getBlockOf(@Nonnull Stmt stmt) { return backingGraph.getBlockOf(stmt); diff --git a/sootup.core/src/main/java/sootup/core/graph/ImmutableBlockStmtGraph.java b/sootup.core/src/main/java/sootup/core/graph/ImmutableBlockStmtGraph.java index 84e0b4fe91a..9dacaa3a3f3 100644 --- a/sootup.core/src/main/java/sootup/core/graph/ImmutableBlockStmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/ImmutableBlockStmtGraph.java @@ -61,6 +61,11 @@ public BasicBlock getStartingStmtBlock() { throw new UnsupportedOperationException("Not implemented yet!"); } + @Override + public List> getTailStmtBlocks() { + throw new UnsupportedOperationException("Not implemented yet!"); + } + @Override public BasicBlock getBlockOf(@Nonnull Stmt stmt) { throw new UnsupportedOperationException("Not implemented yet!"); diff --git a/sootup.core/src/main/java/sootup/core/graph/MutableBlockStmtGraph.java b/sootup.core/src/main/java/sootup/core/graph/MutableBlockStmtGraph.java index d27f2170116..d63e5d6e53f 100644 --- a/sootup.core/src/main/java/sootup/core/graph/MutableBlockStmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/MutableBlockStmtGraph.java @@ -25,6 +25,7 @@ import com.google.common.collect.ComparisonChain; import com.google.common.collect.Lists; import java.util.*; +import java.util.stream.Collectors; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.apache.commons.lang3.tuple.MutablePair; @@ -789,7 +790,8 @@ protected boolean isMergeable( } // if we are here the datastructure should have managed that the next if is true.. final List sBlockPredecessors = followingBlock.getPredecessors(); - if (sBlockPredecessors.size() != 1 || sBlockPredecessors.get(0) != firstBlock) { + if (sBlockPredecessors.size() > 1 + || (sBlockPredecessors.size() == 1 && sBlockPredecessors.get(0) != firstBlock)) { return false; } // check if the same traps are applied to both blocks @@ -1072,12 +1074,12 @@ public void validateBlocks() { * simplifies the handling of BranchingStmts) * @param stmts */ - public void insertBefore( + public BasicBlock insertBefore( @Nonnull Stmt beforeStmt, @Nonnull List stmts, @Nonnull Map exceptionMap) { if (stmts.isEmpty()) { - return; + return stmtToBlock.get(beforeStmt).getRight(); } final Pair beforeStmtBlockPair = stmtToBlock.get(beforeStmt); if (beforeStmtBlockPair == null) { @@ -1100,7 +1102,8 @@ public void insertBefore( // all inserted Stmts are FallingThrough: so successorIdx = 0 predecessorBlock.linkSuccessor(0, block); } - + checkAndResetStartingStmt(beforeStmt, stmts); + return predecessorBlock; } else { // TODO: check conditions before splitting if split will be necessary instead of // split-and-merge @@ -1124,8 +1127,12 @@ public void insertBefore( } blocks.add(successorBlock); } + checkAndResetStartingStmt(beforeStmt, stmts); + return block; } + } + private void checkAndResetStartingStmt(Stmt beforeStmt, List stmts) { if (beforeStmt == getStartingStmt()) { setStartingStmt(stmts.get(0)); } @@ -1384,6 +1391,11 @@ public BasicBlock getStartingStmtBlock() { return getBlockOf(startingStmt); } + @Override + public List> getTailStmtBlocks() { + return getTails().stream().map(stmt -> getBlockOf(stmt)).collect(Collectors.toList()); + } + @Override @Nullable public BasicBlock getBlockOf(@Nonnull Stmt stmt) { diff --git a/sootup.core/src/main/java/sootup/core/graph/MutableStmtGraph.java b/sootup.core/src/main/java/sootup/core/graph/MutableStmtGraph.java index 48ebfe01a54..3861243f57c 100644 --- a/sootup.core/src/main/java/sootup/core/graph/MutableStmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/MutableStmtGraph.java @@ -67,7 +67,7 @@ public void addBlock(@Nonnull List stmts) { */ public abstract void replaceNode(@Nonnull Stmt oldStmt, @Nonnull Stmt newStmt); - public abstract void insertBefore( + public abstract BasicBlock insertBefore( @Nonnull Stmt beforeStmt, @Nonnull List stmts, @Nonnull Map exceptionMap); @@ -76,8 +76,8 @@ public abstract void insertBefore( * inserts the "newStmt" before the position of "beforeStmt" i.e. * newStmt.successors().contains(beforeStmt) will be true */ - public void insertBefore(@Nonnull Stmt beforeStmt, @Nonnull FallsThroughStmt newStmt) { - insertBefore(beforeStmt, Collections.singletonList(newStmt), Collections.emptyMap()); + public BasicBlock insertBefore(@Nonnull Stmt beforeStmt, @Nonnull FallsThroughStmt newStmt) { + return insertBefore(beforeStmt, Collections.singletonList(newStmt), Collections.emptyMap()); } /** removes "stmt" from the StmtGraph */ diff --git a/sootup.core/src/main/java/sootup/core/graph/PostDominanceFinder.java b/sootup.core/src/main/java/sootup/core/graph/PostDominanceFinder.java index 60751ed7ae3..6ba2895af63 100644 --- a/sootup.core/src/main/java/sootup/core/graph/PostDominanceFinder.java +++ b/sootup.core/src/main/java/sootup/core/graph/PostDominanceFinder.java @@ -25,6 +25,6 @@ public class PostDominanceFinder extends DominanceFinder { public PostDominanceFinder(StmtGraph blockGraph) { - super(blockGraph, AnalysisDirection.BACKWARD); + super(blockGraph, BlockAnalysisDirection.POSTORDERBACKWARD); } } diff --git a/sootup.core/src/main/java/sootup/core/graph/PostOrderBlockIterator.java b/sootup.core/src/main/java/sootup/core/graph/PostOrderBlockIterator.java new file mode 100644 index 00000000000..67540bcfc60 --- /dev/null +++ b/sootup.core/src/main/java/sootup/core/graph/PostOrderBlockIterator.java @@ -0,0 +1,80 @@ +package sootup.core.graph; +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2024 Junjie Shen + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.*; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; + +public class PostOrderBlockIterator implements BlockIterator { + + private final Stack stack = new Stack<>(); + private final Set> visited = new HashSet<>(); + + public PostOrderBlockIterator(@Nonnull BasicBlock startNode) { + visitNode(startNode); + stack.push(new Frame(startNode, ((List>) startNode.getSuccessors()).iterator())); + } + + private boolean visitNode(@Nonnull BasicBlock node) { + return visited.add(node); + } + + @Override + public boolean hasNext() { + return !stack.isEmpty(); + } + + @Override + public BasicBlock next() { + if (!hasNext()) { + throw new NoSuchElementException("There is no more block."); + } + while (!stack.isEmpty()) { + Frame frame = stack.peek(); + if (frame.succIterator.hasNext()) { + BasicBlock succ = frame.succIterator.next(); + if (visitNode(succ)) { + List> esuccs = + succ.getExceptionalSuccessors().values().stream().collect(Collectors.toList()); + List> succs = (List>) succ.getSuccessors(); + succs.addAll(esuccs); + stack.push(new Frame(succ, succs.iterator())); + } + } else { + stack.pop(); + return frame.node; + } + } + return null; + } + + private static class Frame { + final BasicBlock node; + final Iterator> succIterator; + + Frame(BasicBlock node, Iterator> childIterator) { + this.node = node; + this.succIterator = childIterator; + } + } +} diff --git a/sootup.core/src/main/java/sootup/core/graph/PostOrderBlockTraversal.java b/sootup.core/src/main/java/sootup/core/graph/PostOrderBlockTraversal.java index 7393dda3711..51c9abf2e54 100644 --- a/sootup.core/src/main/java/sootup/core/graph/PostOrderBlockTraversal.java +++ b/sootup.core/src/main/java/sootup/core/graph/PostOrderBlockTraversal.java @@ -27,7 +27,7 @@ import java.util.stream.StreamSupport; import javax.annotation.Nonnull; -public class PostOrderBlockTraversal { +public class PostOrderBlockTraversal implements BlockTraversal { private final BasicBlock startNode; @@ -44,7 +44,7 @@ public Iterable> getOrder() { } public BlockIterator iterator() { - return new BlockIterator(startNode); + return new PostOrderBlockIterator(startNode); } @Nonnull @@ -55,54 +55,4 @@ public static List> getBlocksSorted(StmtGraph cfg) { false) .collect(Collectors.toList()); } - - public static class BlockIterator implements Iterator> { - private final Stack stack = new Stack<>(); - private final Set> visited = new HashSet<>(); - - public BlockIterator(@Nonnull BasicBlock startNode) { - visitNode(startNode); - stack.push( - new Frame(startNode, ((List>) startNode.getSuccessors()).iterator())); - } - - private boolean visitNode(@Nonnull BasicBlock node) { - return visited.add(node); - } - - @Override - public boolean hasNext() { - return !stack.isEmpty(); - } - - @Override - public BasicBlock next() { - if (!hasNext()) { - throw new NoSuchElementException("There is no more block."); - } - while (!stack.isEmpty()) { - Frame frame = stack.peek(); - if (frame.succIterator.hasNext()) { - BasicBlock succ = frame.succIterator.next(); - if (visitNode(succ)) { - stack.push(new Frame(succ, ((List>) succ.getSuccessors()).iterator())); - } - } else { - stack.pop(); - return frame.node; - } - } - return null; - } - - private static class Frame { - final BasicBlock node; - final Iterator> succIterator; - - Frame(BasicBlock node, Iterator> childIterator) { - this.node = node; - this.succIterator = childIterator; - } - } - } } diff --git a/sootup.core/src/main/java/sootup/core/graph/ReversePostOrderBlockIterator.java b/sootup.core/src/main/java/sootup/core/graph/ReversePostOrderBlockIterator.java new file mode 100644 index 00000000000..4780d773257 --- /dev/null +++ b/sootup.core/src/main/java/sootup/core/graph/ReversePostOrderBlockIterator.java @@ -0,0 +1,56 @@ +package sootup.core.graph; +/*- + * #%L + * Soot - a J*va Optimization Framework + * %% + * Copyright (C) 2024 Junjie Shen + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import javax.annotation.Nonnull; + +public class ReversePostOrderBlockIterator implements BlockIterator { + private List> blocks; + private int i = 0; + + public ReversePostOrderBlockIterator(@Nonnull BasicBlock startNode) { + blocks = + StreamSupport.stream( + Spliterators.spliteratorUnknownSize( + new PostOrderBlockIterator(startNode), Spliterator.ORDERED), + false) + .collect(Collectors.toList()); + Collections.reverse(blocks); + } + + @Override + public boolean hasNext() { + return i < blocks.size(); + } + + @Override + public BasicBlock next() { + if (!hasNext()) { + throw new NoSuchElementException("There is no more block."); + } + i++; + return blocks.get(i - 1); + } +} diff --git a/sootup.core/src/main/java/sootup/core/graph/ReversePostOrderBlockTraversal.java b/sootup.core/src/main/java/sootup/core/graph/ReversePostOrderBlockTraversal.java index de35f349895..620201df789 100644 --- a/sootup.core/src/main/java/sootup/core/graph/ReversePostOrderBlockTraversal.java +++ b/sootup.core/src/main/java/sootup/core/graph/ReversePostOrderBlockTraversal.java @@ -45,7 +45,7 @@ public Iterable> getOrder() { @Nonnull public BlockIterator iterator() { - return new BlockIterator(startNode); + return new ReversePostOrderBlockIterator(startNode); } @Nonnull @@ -56,33 +56,4 @@ public static List> getBlocksSorted(StmtGraph cfg) { false) .collect(Collectors.toList()); } - - public static class BlockIterator implements Iterator> { - private List> blocks; - private int i = 0; - - public BlockIterator(@Nonnull BasicBlock startNode) { - blocks = - StreamSupport.stream( - Spliterators.spliteratorUnknownSize( - new PostOrderBlockTraversal.BlockIterator(startNode), Spliterator.ORDERED), - false) - .collect(Collectors.toList()); - Collections.reverse(blocks); - } - - @Override - public boolean hasNext() { - return i < blocks.size(); - } - - @Override - public BasicBlock next() { - if (!hasNext()) { - throw new NoSuchElementException("There is no more block."); - } - i++; - return blocks.get(i - 1); - } - } } 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 17482b62e41..92b675cfa0e 100644 --- a/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java @@ -69,6 +69,8 @@ public abstract class StmtGraph> implements Iterable getStartingStmtBlock(); + public abstract List> getTailStmtBlocks(); + /** * returns the nodes in this graph in a non-deterministic order (->Set) to get the nodes in * linearized, ordered manner use iterator() or getStmts. diff --git a/sootup.core/src/test/java/sootup/core/graph/DominanceFinderTest.java b/sootup.core/src/test/java/sootup/core/graph/DominanceFinderTest.java index 919da1e481f..2372262b4f2 100644 --- a/sootup.core/src/test/java/sootup/core/graph/DominanceFinderTest.java +++ b/sootup.core/src/test/java/sootup/core/graph/DominanceFinderTest.java @@ -6,79 +6,52 @@ import java.util.stream.Collectors; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import sootup.core.jimple.basic.Local; -import sootup.core.jimple.basic.StmtPositionInfo; -import sootup.core.jimple.common.constant.IntConstant; -import sootup.core.jimple.common.expr.JAddExpr; -import sootup.core.jimple.common.expr.JLeExpr; import sootup.core.jimple.common.stmt.*; -import sootup.core.types.PrimitiveType; @Tag("Java8") public class DominanceFinderTest { - StmtPositionInfo noPosInfo = StmtPositionInfo.getNoStmtPositionInfo(); + TestGraphGenerator graphGenerator = new TestGraphGenerator(); @Test public void testDominanceFinder() { - MutableBlockStmtGraph graph = createStmtGraph(); - Map, Integer> blockToId = new HashMap<>(); - // assign ids according to blocks sorted by BasicBlock::toString - List> blocks = - graph.getBlocks().stream() - .sorted(Comparator.comparing(BasicBlock::toString)) - .collect(Collectors.toList()); - int i = 0; - for (BasicBlock block : blocks) { - blockToId.put(block, i); - i++; - } + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph(); + DominanceFinder dominanceFinder = new DominanceFinder(graph); - DominanceFinder dom = new DominanceFinder(graph); + int[] domsArr = dominanceFinder.getImmediateDominators(); + List doms = Arrays.stream(domsArr).boxed().collect(Collectors.toList()); - Map> expectedFrontiers = new HashMap<>(); - expectedFrontiers.put(0, new HashSet<>(Collections.singletonList(2))); - expectedFrontiers.put(1, new HashSet<>(Collections.singletonList(2))); - expectedFrontiers.put(2, new HashSet<>(Collections.singletonList(2))); - expectedFrontiers.put(3, new HashSet<>()); - expectedFrontiers.put(4, new HashSet<>(Collections.singletonList(0))); - expectedFrontiers.put(5, new HashSet<>(Collections.singletonList(0))); - expectedFrontiers.put(6, new HashSet<>()); - - Map expectedDominators = new HashMap<>(); - expectedDominators.put(0, 1); - expectedDominators.put(1, 2); - expectedDominators.put(2, 3); - expectedDominators.put(3, -1); - expectedDominators.put(4, 1); - expectedDominators.put(5, 1); - expectedDominators.put(6, 2); - - // check dominators - for (BasicBlock block : blocks) { - Integer dominatorId = -1; - BasicBlock dominator = dom.getImmediateDominator(block); - if (dominator != null) { - dominatorId = blockToId.get(dominator); - } - Integer expectedId = expectedDominators.get(blockToId.get(block)); - - assertEquals(expectedId, dominatorId); - } + List expectedDoms = Arrays.asList(-1, 0, 1, 2, 2, 2, 1); + assertEquals(expectedDoms, doms); + } - // check frontiers - for (BasicBlock block : blocks) { - Set> frontier = dom.getDominanceFrontiers(block); - Set frontierIds = frontier.stream().map(blockToId::get).collect(Collectors.toSet()); - Set expectedIds = expectedFrontiers.get(blockToId.get(block)); + @Test + public void testDominanceFrontiers() { + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph(); + DominanceFinder dominanceFinder = new DominanceFinder(graph); + + List> blocks = dominanceFinder.getIdxToBlock(); + List>> dfList = + blocks.stream() + .map(block -> dominanceFinder.getDominanceFrontiers(block)) + .collect(Collectors.toList()); - assertEquals(expectedIds, frontierIds); - } + // create expectedDFList + List>> expectedDFList = new ArrayList<>(); + expectedDFList.add(Collections.emptySet()); + expectedDFList.add(Collections.singleton(blocks.get(1))); + expectedDFList.add(Collections.singleton(blocks.get(1))); + expectedDFList.add(Collections.singleton(blocks.get(5))); + expectedDFList.add(Collections.singleton(blocks.get(5))); + expectedDFList.add(Collections.singleton(blocks.get(1))); + expectedDFList.add(Collections.emptySet()); + + assertEquals(expectedDFList, dfList); } @Test public void testBlockToIdxInverse() { - MutableBlockStmtGraph graph = createStmtGraph(); + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph(); DominanceFinder dom = new DominanceFinder(graph); // check that getBlockToIdx and getIdxToBlock are inverses @@ -89,59 +62,39 @@ public void testBlockToIdxInverse() { } } - private MutableBlockStmtGraph createStmtGraph() { - // reconstruct the example given in - // https://soot-oss.github.io/SootUp/v1.1.2/advanced-topics/#dominancefinder. - MutableBlockStmtGraph graph = new MutableBlockStmtGraph(); - - Local l1 = new Local("l1", PrimitiveType.IntType.getInstance()); - Local l2 = new Local("l2", PrimitiveType.IntType.getInstance()); - Local l3 = new Local("l3", PrimitiveType.IntType.getInstance()); - - JAssignStmt assign01 = new JAssignStmt(l1, IntConstant.getInstance(1), noPosInfo); - JAssignStmt assign02 = new JAssignStmt(l2, IntConstant.getInstance(2), noPosInfo); - JAssignStmt assign03 = new JAssignStmt(l2, IntConstant.getInstance(0), noPosInfo); - JAssignStmt assign41 = new JAssignStmt(l2, l3, noPosInfo); - JAssignStmt assign42 = - new JAssignStmt(l3, new JAddExpr(l3, IntConstant.getInstance(2)), noPosInfo); - JAssignStmt assign51 = new JAssignStmt(l2, l1, noPosInfo); - JAssignStmt assign52 = - new JAssignStmt(l3, new JAddExpr(l3, IntConstant.getInstance(1)), noPosInfo); - - BranchingStmt if1 = new JIfStmt(new JLeExpr(l3, IntConstant.getInstance(100)), noPosInfo); - BranchingStmt if3 = new JIfStmt(new JLeExpr(l2, IntConstant.getInstance(20)), noPosInfo); - - JReturnStmt return2 = new JReturnStmt(l2, noPosInfo); - JGotoStmt goto4 = new JGotoStmt(noPosInfo); - JGotoStmt goto5 = new JGotoStmt(noPosInfo); - JGotoStmt goto6 = new JGotoStmt(noPosInfo); - - // block 0 - graph.setStartingStmt(assign01); - graph.putEdge(assign01, assign02); - graph.putEdge(assign02, assign03); - graph.putEdge(assign03, if1); - - // block 1 - graph.putEdge(if1, JIfStmt.FALSE_BRANCH_IDX, return2); - graph.putEdge(if1, JIfStmt.TRUE_BRANCH_IDX, if3); - - // block 3 - graph.putEdge(if3, JIfStmt.FALSE_BRANCH_IDX, assign41); - graph.putEdge(if3, JIfStmt.TRUE_BRANCH_IDX, assign51); - - // block 4 - graph.putEdge(assign41, assign42); - graph.putEdge(assign42, goto4); - graph.putEdge(goto4, JGotoStmt.BRANCH_IDX, goto6); - - // block 5 - graph.putEdge(assign51, assign52); - graph.putEdge(assign52, goto5); - graph.putEdge(goto5, JGotoStmt.BRANCH_IDX, goto6); - - // block 6 - graph.putEdge(goto6, JGotoStmt.BRANCH_IDX, if1); - return graph; + @Test + public void testDominanceFinder2() { + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph2(); + DominanceFinder dominanceFinder = new DominanceFinder(graph); + + int[] domsArr = dominanceFinder.getImmediateDominators(); + List doms = Arrays.stream(domsArr).boxed().collect(Collectors.toList()); + + List expectedDoms = Arrays.asList(-1, 0, 0, 0, 0, 0); + + assertEquals(expectedDoms, doms); + } + + @Test + public void testDominanceFrontiers2() { + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph2(); + DominanceFinder dominanceFinder = new DominanceFinder(graph); + + List> blocks = dominanceFinder.getIdxToBlock(); + List>> dfList = + blocks.stream() + .map(block -> dominanceFinder.getDominanceFrontiers(block)) + .collect(Collectors.toList()); + + // create expectedDFList + List>> expectedDFList = new ArrayList<>(); + expectedDFList.add(Collections.emptySet()); + expectedDFList.add(Collections.singleton(blocks.get(5))); + expectedDFList.add(new HashSet<>(Arrays.asList(blocks.get(3), blocks.get(4)))); + expectedDFList.add(Collections.singleton(blocks.get(4))); + expectedDFList.add(new HashSet<>(Arrays.asList(blocks.get(3), blocks.get(5)))); + expectedDFList.add(Collections.singleton(blocks.get(4))); + + assertEquals(expectedDFList, dfList); } } diff --git a/sootup.core/src/test/java/sootup/core/graph/PostDominanceFinderTest.java b/sootup.core/src/test/java/sootup/core/graph/PostDominanceFinderTest.java index 3c0dd45b80f..d1efda4143c 100644 --- a/sootup.core/src/test/java/sootup/core/graph/PostDominanceFinderTest.java +++ b/sootup.core/src/test/java/sootup/core/graph/PostDominanceFinderTest.java @@ -4,81 +4,67 @@ import java.util.*; import java.util.stream.Collectors; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import sootup.core.jimple.basic.Local; -import sootup.core.jimple.basic.StmtPositionInfo; -import sootup.core.jimple.common.constant.IntConstant; -import sootup.core.jimple.common.expr.JAddExpr; -import sootup.core.jimple.common.expr.JLeExpr; import sootup.core.jimple.common.stmt.*; -import sootup.core.types.PrimitiveType; @Tag("Java8") public class PostDominanceFinderTest { - StmtPositionInfo noPosInfo = StmtPositionInfo.getNoStmtPositionInfo(); + TestGraphGenerator graphGenerator = new TestGraphGenerator(); @Test - public void testDominanceFinder() { - MutableBlockStmtGraph graph = createStmtGraph(); - Map, Integer> blockToId = new HashMap<>(); - // assign ids according to blocks sorted by BasicBlock::toString - List> blocks = - graph.getBlocks().stream() - .sorted(Comparator.comparing(BasicBlock::toString)) + public void testPostDominanceFinder() { + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph(); + PostDominanceFinder postDominanceFinder = new PostDominanceFinder(graph); + + int[] domsArr = postDominanceFinder.getImmediateDominators(); + List pDoms = Arrays.stream(domsArr).boxed().collect(Collectors.toList()); + + List expectedPDoms = Arrays.asList(-1, 5, 1, 1, 1, 0, 5); + assertEquals(expectedPDoms, pDoms); + } + + @Test + public void testPostDominanceFrontiers() { + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph(); + PostDominanceFinder postDominanceFinder = new PostDominanceFinder(graph); + + List> blocks = postDominanceFinder.getIdxToBlock(); + List>> pdfList = + blocks.stream() + .map(block -> postDominanceFinder.getDominanceFrontiers(block)) .collect(Collectors.toList()); - int i = 0; - for (BasicBlock block : blocks) { - blockToId.put(block, i); - i++; - } - PostDominanceFinder postDom = new PostDominanceFinder(graph); + List>> expectedPDFList = new ArrayList<>(); + expectedPDFList.add(Collections.emptySet()); + expectedPDFList.add(Collections.singleton(blocks.get(5))); + expectedPDFList.add(Collections.singleton(blocks.get(4))); + expectedPDFList.add(Collections.singleton(blocks.get(4))); + expectedPDFList.add(Collections.singleton(blocks.get(5))); + expectedPDFList.add(Collections.emptySet()); + expectedPDFList.add(Collections.emptySet()); - Map> expectedFrontiers = new HashMap<>(); - expectedFrontiers.put(0, new HashSet<>(Collections.singletonList(2))); - expectedFrontiers.put(1, new HashSet<>(Collections.singletonList(2))); - expectedFrontiers.put(2, new HashSet<>(Collections.singletonList(2))); - expectedFrontiers.put(3, new HashSet<>()); - expectedFrontiers.put(4, new HashSet<>(Collections.singletonList(1))); - expectedFrontiers.put(5, new HashSet<>(Collections.singletonList(1))); - expectedFrontiers.put(6, new HashSet<>()); - - Map expectedDominators = new HashMap<>(); - expectedDominators.put(0, 2); - expectedDominators.put(1, 0); - expectedDominators.put(2, 6); - expectedDominators.put(3, 2); - expectedDominators.put(4, 0); - expectedDominators.put(5, 0); - expectedDominators.put(6, -1); - - // check dominators - for (BasicBlock block : blocks) { - BasicBlock dominator = postDom.getImmediateDominator(block); - Integer dominatorId = -1; - if (dominator != null) { - dominatorId = blockToId.get(dominator); - } - Integer expectedId = expectedDominators.get(blockToId.get(block)); - - assertEquals(expectedId, dominatorId); - } + assertEquals(expectedPDFList, pdfList); + } - // check frontiers - for (BasicBlock block : blocks) { - Set> frontier = postDom.getDominanceFrontiers(block); - Set frontierIds = frontier.stream().map(blockToId::get).collect(Collectors.toSet()); - Set expectedIds = expectedFrontiers.get(blockToId.get(block)); + @Test + @Disabled("PostDominanceFinder doesn't work, if blockgraph contains two end-blocks") + public void testDominanceFinder2() { + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph3(); + PostDominanceFinder postDom = new PostDominanceFinder(graph); - assertEquals(expectedIds, frontierIds); - } + int[] domsArr = postDom.getImmediateDominators(); + List pDoms = Arrays.stream(domsArr).boxed().collect(Collectors.toList()); + + List expectedPDoms = Arrays.asList(2, 2, 2); + assertEquals(expectedPDoms, pDoms); } @Test public void testBlockToIdxInverse() { - MutableBlockStmtGraph graph = createStmtGraph(); + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph(); DominanceFinder dom = new PostDominanceFinder(graph); // check that getBlockToIdx and getIdxToBlock are inverses @@ -88,58 +74,4 @@ public void testBlockToIdxInverse() { assertEquals(block, idxToBlock.get(blockToIdx.get(block))); } } - - private MutableBlockStmtGraph createStmtGraph() { - MutableBlockStmtGraph graph = new MutableBlockStmtGraph(); - - Local l1 = new Local("l1", PrimitiveType.IntType.getInstance()); - Local l2 = new Local("l2", PrimitiveType.IntType.getInstance()); - Local l3 = new Local("l3", PrimitiveType.IntType.getInstance()); - - JAssignStmt assign01 = new JAssignStmt(l1, IntConstant.getInstance(1), noPosInfo); - JAssignStmt assign02 = new JAssignStmt(l2, IntConstant.getInstance(2), noPosInfo); - JAssignStmt assign03 = new JAssignStmt(l2, IntConstant.getInstance(0), noPosInfo); - JAssignStmt assign41 = new JAssignStmt(l2, l3, noPosInfo); - JAssignStmt assign42 = - new JAssignStmt(l3, new JAddExpr(l3, IntConstant.getInstance(2)), noPosInfo); - JAssignStmt assign51 = new JAssignStmt(l2, l1, noPosInfo); - JAssignStmt assign52 = - new JAssignStmt(l3, new JAddExpr(l3, IntConstant.getInstance(1)), noPosInfo); - - BranchingStmt if1 = new JIfStmt(new JLeExpr(l3, IntConstant.getInstance(100)), noPosInfo); - BranchingStmt if3 = new JIfStmt(new JLeExpr(l2, IntConstant.getInstance(20)), noPosInfo); - - JReturnStmt return2 = new JReturnStmt(l2, noPosInfo); - JGotoStmt goto4 = new JGotoStmt(noPosInfo); - JGotoStmt goto5 = new JGotoStmt(noPosInfo); - JGotoStmt goto6 = new JGotoStmt(noPosInfo); - - // block 0 - graph.setStartingStmt(assign01); - graph.putEdge(assign01, assign02); - graph.putEdge(assign02, assign03); - graph.putEdge(assign03, if1); - - // block 1 - graph.putEdge(if1, JIfStmt.FALSE_BRANCH_IDX, return2); - graph.putEdge(if1, JIfStmt.TRUE_BRANCH_IDX, if3); - - // block 3 - graph.putEdge(if3, JIfStmt.FALSE_BRANCH_IDX, assign41); - graph.putEdge(if3, JIfStmt.TRUE_BRANCH_IDX, assign51); - - // block 4 - graph.putEdge(assign41, assign42); - graph.putEdge(assign42, goto4); - graph.putEdge(goto4, JGotoStmt.BRANCH_IDX, goto6); - - // block 5 - graph.putEdge(assign51, assign52); - graph.putEdge(assign52, goto5); - graph.putEdge(goto5, JGotoStmt.BRANCH_IDX, goto6); - - // block 6 - graph.putEdge(goto6, JGotoStmt.BRANCH_IDX, if1); - return graph; - } } diff --git a/sootup.core/src/test/java/sootup/core/graph/PostOrderTraversalTest.java b/sootup.core/src/test/java/sootup/core/graph/PostOrderTraversalTest.java index b0ef50594d0..7289074b453 100644 --- a/sootup.core/src/test/java/sootup/core/graph/PostOrderTraversalTest.java +++ b/sootup.core/src/test/java/sootup/core/graph/PostOrderTraversalTest.java @@ -2,30 +2,19 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import sootup.core.jimple.basic.Local; -import sootup.core.jimple.basic.StmtPositionInfo; -import sootup.core.jimple.common.constant.IntConstant; -import sootup.core.jimple.common.expr.JAddExpr; -import sootup.core.jimple.common.expr.JLeExpr; -import sootup.core.jimple.common.stmt.*; -import sootup.core.types.PrimitiveType; @Tag("Java8") public class PostOrderTraversalTest { - StmtPositionInfo noPosInfo = StmtPositionInfo.getNoStmtPositionInfo(); + TestGraphGenerator graphGenerator = new TestGraphGenerator(); @Test - void testReversePostorderTraversal() { - MutableBlockStmtGraph graph = createStmtGraph(); - Map, Integer> blocksToId = new HashMap<>(); - Map, Integer> blockToPOId = new HashMap<>(); + void testPostOrderTraversal1() { + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph(); + Map, Integer> blockToId = new HashMap<>(); // assign ids according to blocks sorted by BasicBlock::toString List> blocks = graph.getBlocks().stream() @@ -33,87 +22,39 @@ void testReversePostorderTraversal() { .collect(Collectors.toList()); int i = 0; for (BasicBlock block : blocks) { - blocksToId.put(block, i); - i++; - } - - // assign po ids according to PostOrderBlockTraversal.getOrder - Iterable> PO = new PostOrderBlockTraversal(graph).getOrder(); - i = 0; - for (BasicBlock block : PO) { - blockToPOId.put(block, i); + blockToId.put(block, i); i++; } - Map expectedSortedIdToRPOId = new HashMap<>(); - expectedSortedIdToRPOId.put(0, 1); - expectedSortedIdToRPOId.put(1, 4); - expectedSortedIdToRPOId.put(2, 5); - expectedSortedIdToRPOId.put(3, 6); - expectedSortedIdToRPOId.put(4, 3); - expectedSortedIdToRPOId.put(5, 2); - expectedSortedIdToRPOId.put(6, 0); + List> postOrderBlocks = PostOrderBlockTraversal.getBlocksSorted(graph); + List PO = + postOrderBlocks.stream().map(b -> blockToId.get(b)).collect(Collectors.toList()); - for (BasicBlock block : PO) { - Integer id = blocksToId.get(block); - Integer poId = blockToPOId.get(block); - Integer expectedPOId = expectedSortedIdToRPOId.get(id); - - assertEquals(expectedPOId, poId); - } + List expectedPO = Arrays.asList(6, 0, 5, 4, 1, 2, 3); + assertEquals(expectedPO, PO); } - private MutableBlockStmtGraph createStmtGraph() { - MutableBlockStmtGraph graph = new MutableBlockStmtGraph(); - - Local l1 = new Local("l1", PrimitiveType.IntType.getInstance()); - Local l2 = new Local("l2", PrimitiveType.IntType.getInstance()); - Local l3 = new Local("l3", PrimitiveType.IntType.getInstance()); - - JAssignStmt assign01 = new JAssignStmt(l1, IntConstant.getInstance(1), noPosInfo); - JAssignStmt assign02 = new JAssignStmt(l2, IntConstant.getInstance(2), noPosInfo); - JAssignStmt assign03 = new JAssignStmt(l2, IntConstant.getInstance(0), noPosInfo); - JAssignStmt assign41 = new JAssignStmt(l2, l3, noPosInfo); - JAssignStmt assign42 = - new JAssignStmt(l3, new JAddExpr(l3, IntConstant.getInstance(2)), noPosInfo); - JAssignStmt assign51 = new JAssignStmt(l2, l1, noPosInfo); - JAssignStmt assign52 = - new JAssignStmt(l3, new JAddExpr(l3, IntConstant.getInstance(1)), noPosInfo); - - BranchingStmt if1 = new JIfStmt(new JLeExpr(l3, IntConstant.getInstance(100)), noPosInfo); - BranchingStmt if3 = new JIfStmt(new JLeExpr(l2, IntConstant.getInstance(20)), noPosInfo); - - JReturnStmt return2 = new JReturnStmt(l2, noPosInfo); - JGotoStmt goto4 = new JGotoStmt(noPosInfo); - JGotoStmt goto5 = new JGotoStmt(noPosInfo); - JGotoStmt goto6 = new JGotoStmt(noPosInfo); - - // block 0 - graph.setStartingStmt(assign01); - graph.putEdge(assign01, assign02); - graph.putEdge(assign02, assign03); - graph.putEdge(assign03, if1); - - // block 1 - graph.putEdge(if1, JIfStmt.FALSE_BRANCH_IDX, return2); - graph.putEdge(if1, JIfStmt.TRUE_BRANCH_IDX, if3); - - // block 3 - graph.putEdge(if3, JIfStmt.FALSE_BRANCH_IDX, assign41); - graph.putEdge(if3, JIfStmt.TRUE_BRANCH_IDX, assign51); - - // block 4 - graph.putEdge(assign41, assign42); - graph.putEdge(assign42, goto4); - graph.putEdge(goto4, JGotoStmt.BRANCH_IDX, goto6); + @Test + void testPostOrderTraversal2() { + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph2(); + Map, Integer> blockToId = new HashMap<>(); + Map, Integer> blockToPOId = new HashMap<>(); + // assign ids according to blocks sorted by BasicBlock::toString + List> blocks = + graph.getBlocks().stream() + .sorted(Comparator.comparing(BasicBlock::toString)) + .collect(Collectors.toList()); + int i = 0; + for (BasicBlock block : blocks) { + blockToId.put(block, i); + i++; + } - // block 5 - graph.putEdge(assign51, assign52); - graph.putEdge(assign52, goto5); - graph.putEdge(goto5, JGotoStmt.BRANCH_IDX, goto6); + List> postOrderBlocks = PostOrderBlockTraversal.getBlocksSorted(graph); + List PO = + postOrderBlocks.stream().map(b -> blockToId.get(b)).collect(Collectors.toList()); - // block 6 - graph.putEdge(goto6, JGotoStmt.BRANCH_IDX, if1); - return graph; + List expectedPO = Arrays.asList(2, 5, 3, 0, 4, 1); + assertEquals(expectedPO, PO); } } diff --git a/sootup.core/src/test/java/sootup/core/graph/ReversePostOrderTraversalTest.java b/sootup.core/src/test/java/sootup/core/graph/ReversePostOrderTraversalTest.java index 205bf72902a..c74f5816ad6 100644 --- a/sootup.core/src/test/java/sootup/core/graph/ReversePostOrderTraversalTest.java +++ b/sootup.core/src/test/java/sootup/core/graph/ReversePostOrderTraversalTest.java @@ -6,23 +6,16 @@ import java.util.stream.Collectors; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import sootup.core.jimple.basic.Local; -import sootup.core.jimple.basic.StmtPositionInfo; -import sootup.core.jimple.common.constant.IntConstant; -import sootup.core.jimple.common.expr.JAddExpr; -import sootup.core.jimple.common.expr.JLeExpr; -import sootup.core.jimple.common.stmt.*; -import sootup.core.types.PrimitiveType; @Tag("Java8") public class ReversePostOrderTraversalTest { - StmtPositionInfo noPosInfo = StmtPositionInfo.getNoStmtPositionInfo(); + + TestGraphGenerator graphGenerator = new TestGraphGenerator(); @Test - void testReversePostorderTraversal() { - MutableBlockStmtGraph graph = createStmtGraph(); + void testReversePostOrderTraversal1() { + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph(); Map, Integer> blockToId = new HashMap<>(); - Map, Integer> blockToRPOId = new HashMap<>(); // assign ids according to blocks sorted by BasicBlock::toString List> blocks = graph.getBlocks().stream() @@ -34,83 +27,38 @@ void testReversePostorderTraversal() { i++; } - // assign rpo ids according to ReversePostOrderBlockTraversal.getOrder - Iterable> RPO = new ReversePostOrderBlockTraversal(graph).getOrder(); - i = 0; - for (BasicBlock block : RPO) { - blockToRPOId.put(block, i); - i++; - } + List> reversePostOrderBlocks = + ReversePostOrderBlockTraversal.getBlocksSorted(graph); + List RPO = + reversePostOrderBlocks.stream().map(b -> blockToId.get(b)).collect(Collectors.toList()); - Map expectedSortedIdToRPOId = new HashMap<>(); - expectedSortedIdToRPOId.put(0, 5); - expectedSortedIdToRPOId.put(1, 2); - expectedSortedIdToRPOId.put(2, 1); - expectedSortedIdToRPOId.put(3, 0); - expectedSortedIdToRPOId.put(4, 3); - expectedSortedIdToRPOId.put(5, 4); - expectedSortedIdToRPOId.put(6, 6); - - for (BasicBlock block : RPO) { - Integer id = blockToId.get(block); - Integer rpoId = blockToRPOId.get(block); - Integer expectedRPOId = expectedSortedIdToRPOId.get(id); - - assertEquals(expectedRPOId, rpoId); - } + List expectedRPO = Arrays.asList(3, 2, 1, 4, 5, 0, 6); + assertEquals(expectedRPO, RPO); } - private MutableBlockStmtGraph createStmtGraph() { - MutableBlockStmtGraph graph = new MutableBlockStmtGraph(); - - Local l1 = new Local("l1", PrimitiveType.IntType.getInstance()); - Local l2 = new Local("l2", PrimitiveType.IntType.getInstance()); - Local l3 = new Local("l3", PrimitiveType.IntType.getInstance()); - - JAssignStmt assign01 = new JAssignStmt(l1, IntConstant.getInstance(1), noPosInfo); - JAssignStmt assign02 = new JAssignStmt(l2, IntConstant.getInstance(2), noPosInfo); - JAssignStmt assign03 = new JAssignStmt(l2, IntConstant.getInstance(0), noPosInfo); - JAssignStmt assign41 = new JAssignStmt(l2, l3, noPosInfo); - JAssignStmt assign42 = - new JAssignStmt(l3, new JAddExpr(l3, IntConstant.getInstance(2)), noPosInfo); - JAssignStmt assign51 = new JAssignStmt(l2, l1, noPosInfo); - JAssignStmt assign52 = - new JAssignStmt(l3, new JAddExpr(l3, IntConstant.getInstance(1)), noPosInfo); - - BranchingStmt if1 = new JIfStmt(new JLeExpr(l3, IntConstant.getInstance(100)), noPosInfo); - BranchingStmt if3 = new JIfStmt(new JLeExpr(l2, IntConstant.getInstance(20)), noPosInfo); - - JReturnStmt return2 = new JReturnStmt(l2, noPosInfo); - JGotoStmt goto4 = new JGotoStmt(noPosInfo); - JGotoStmt goto5 = new JGotoStmt(noPosInfo); - JGotoStmt goto6 = new JGotoStmt(noPosInfo); - - // block 0 - graph.setStartingStmt(assign01); - graph.putEdge(assign01, assign02); - graph.putEdge(assign02, assign03); - graph.putEdge(assign03, if1); - - // block 1 - graph.putEdge(if1, JIfStmt.FALSE_BRANCH_IDX, return2); - graph.putEdge(if1, JIfStmt.TRUE_BRANCH_IDX, if3); - - // block 3 - graph.putEdge(if3, JIfStmt.FALSE_BRANCH_IDX, assign41); - graph.putEdge(if3, JIfStmt.TRUE_BRANCH_IDX, assign51); + @Test + void testReversePostOrderTraversal2() { + MutableBlockStmtGraph graph = graphGenerator.createStmtGraph2(); + Map, Integer> blocksToId = new HashMap<>(); + Map, Integer> blockToRPOId = new HashMap<>(); + // assign ids according to blocks sorted by BasicBlock::toString + List> blocks = + graph.getBlocks().stream() + .sorted(Comparator.comparing(BasicBlock::toString)) + .collect(Collectors.toList()); + int i = 0; + for (BasicBlock block : blocks) { + blocksToId.put(block, i); + i++; + } - // block 4 - graph.putEdge(assign41, assign42); - graph.putEdge(assign42, goto4); - graph.putEdge(goto4, JGotoStmt.BRANCH_IDX, goto6); + List> reversePostOrderBlocks = + ReversePostOrderBlockTraversal.getBlocksSorted(graph); + List RPO = + reversePostOrderBlocks.stream().map(b -> blocksToId.get(b)).collect(Collectors.toList()); - // block 5 - graph.putEdge(assign51, assign52); - graph.putEdge(assign52, goto5); - graph.putEdge(goto5, JGotoStmt.BRANCH_IDX, goto6); + List expectedRPO = Arrays.asList(1, 4, 0, 3, 5, 2); - // block 6 - graph.putEdge(goto6, JGotoStmt.BRANCH_IDX, if1); - return graph; + assertEquals(expectedRPO, RPO); } } diff --git a/sootup.core/src/test/java/sootup/core/graph/TestGraphGenerator.java b/sootup.core/src/test/java/sootup/core/graph/TestGraphGenerator.java new file mode 100644 index 00000000000..4d3a6ff3664 --- /dev/null +++ b/sootup.core/src/test/java/sootup/core/graph/TestGraphGenerator.java @@ -0,0 +1,158 @@ +package sootup.core.graph; + +import sootup.core.jimple.basic.Local; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.common.constant.IntConstant; +import sootup.core.jimple.common.expr.JAddExpr; +import sootup.core.jimple.common.expr.JEqExpr; +import sootup.core.jimple.common.expr.JLeExpr; +import sootup.core.jimple.common.expr.JLtExpr; +import sootup.core.jimple.common.stmt.*; +import sootup.core.types.PrimitiveType; + +public class TestGraphGenerator { + + StmtPositionInfo noPosInfo = StmtPositionInfo.getNoStmtPositionInfo(); + + public MutableBlockStmtGraph createStmtGraph() { + MutableBlockStmtGraph graph = new MutableBlockStmtGraph(); + + Local l1 = new Local("l1", PrimitiveType.IntType.getInstance()); + Local l2 = new Local("l2", PrimitiveType.IntType.getInstance()); + Local l3 = new Local("l3", PrimitiveType.IntType.getInstance()); + + JAssignStmt assign01 = new JAssignStmt(l1, IntConstant.getInstance(1), noPosInfo); + JAssignStmt assign02 = new JAssignStmt(l2, IntConstant.getInstance(2), noPosInfo); + JAssignStmt assign03 = new JAssignStmt(l2, IntConstant.getInstance(0), noPosInfo); + JAssignStmt assign41 = new JAssignStmt(l2, l3, noPosInfo); + JAssignStmt assign42 = + new JAssignStmt(l3, new JAddExpr(l3, IntConstant.getInstance(2)), noPosInfo); + JAssignStmt assign51 = new JAssignStmt(l2, l1, noPosInfo); + JAssignStmt assign52 = + new JAssignStmt(l3, new JAddExpr(l3, IntConstant.getInstance(1)), noPosInfo); + + BranchingStmt if1 = new JIfStmt(new JLeExpr(l3, IntConstant.getInstance(100)), noPosInfo); + BranchingStmt if3 = new JIfStmt(new JLeExpr(l2, IntConstant.getInstance(20)), noPosInfo); + + JReturnStmt return2 = new JReturnStmt(l2, noPosInfo); + JGotoStmt goto4 = new JGotoStmt(noPosInfo); + JGotoStmt goto5 = new JGotoStmt(noPosInfo); + JGotoStmt goto6 = new JGotoStmt(noPosInfo); + + // block 0 + graph.setStartingStmt(assign01); + graph.putEdge(assign01, assign02); + graph.putEdge(assign02, assign03); + graph.putEdge(assign03, if1); + + // block 1 + graph.putEdge(if1, JIfStmt.FALSE_BRANCH_IDX, return2); + graph.putEdge(if1, JIfStmt.TRUE_BRANCH_IDX, if3); + + // block 2 + graph.putEdge(if3, JIfStmt.FALSE_BRANCH_IDX, assign41); + graph.putEdge(if3, JIfStmt.TRUE_BRANCH_IDX, assign51); + + // block 3 + graph.putEdge(assign41, assign42); + graph.putEdge(assign42, goto4); + graph.putEdge(goto4, JGotoStmt.BRANCH_IDX, goto6); + + // block 4 + graph.putEdge(assign51, assign52); + graph.putEdge(assign52, goto5); + graph.putEdge(goto5, JGotoStmt.BRANCH_IDX, goto6); + + // block 5 + graph.putEdge(goto6, JGotoStmt.BRANCH_IDX, if1); + return graph; + } + + // a control flow graph without returnStmt, because simulate the situation in the paper + // if doesn't pass the validation, please add a return stmt after ifl2l50 + public MutableBlockStmtGraph createStmtGraph2() { + MutableBlockStmtGraph graph = new MutableBlockStmtGraph(); + + Local l1 = new Local("l1", PrimitiveType.IntType.getInstance()); + Local l2 = new Local("l2", PrimitiveType.IntType.getInstance()); + Local l3 = new Local("l3", PrimitiveType.IntType.getInstance()); + + JAssignStmt assignl1e1 = new JAssignStmt(l1, IntConstant.getInstance(1), noPosInfo); + JAssignStmt assignl2e1 = new JAssignStmt(l2, IntConstant.getInstance(1), noPosInfo); + JAssignStmt assignl3e0 = new JAssignStmt(l3, IntConstant.getInstance(0), noPosInfo); + + JAssignStmt assignl3e1 = new JAssignStmt(l3, IntConstant.getInstance(1), noPosInfo); + + JAssignStmt assignl1aa = + new JAssignStmt(l1, new JAddExpr(l1, IntConstant.getInstance(1)), noPosInfo); + JAssignStmt assignl2aa = + new JAssignStmt(l2, new JAddExpr(l2, IntConstant.getInstance(1)), noPosInfo); + JAssignStmt assignl3el2al1 = new JAssignStmt(l3, new JAddExpr(l1, l2), noPosInfo); + + BranchingStmt ifl1e1 = new JIfStmt(new JEqExpr(l1, IntConstant.getInstance(1)), noPosInfo); + BranchingStmt ifl3e1 = new JIfStmt(new JEqExpr(l3, IntConstant.getInstance(1)), noPosInfo); + BranchingStmt ifl1l50 = new JIfStmt(new JLtExpr(l1, IntConstant.getInstance(50)), noPosInfo); + BranchingStmt ifl2l50 = new JIfStmt(new JLtExpr(l2, IntConstant.getInstance(50)), noPosInfo); + + // JReturnStmt returnl3 = new JReturnStmt(l3, noPosInfo); + + // block 0 + graph.setStartingStmt(assignl1e1); + graph.putEdge(assignl1e1, assignl2e1); + graph.putEdge(assignl2e1, assignl3e0); + graph.putEdge(assignl3e0, ifl1e1); + graph.putEdge(ifl1e1, JIfStmt.TRUE_BRANCH_IDX, assignl3e1); + graph.putEdge(ifl1e1, JIfStmt.FALSE_BRANCH_IDX, ifl3e1); + + // block 1 + graph.putEdge(assignl3e1, assignl1aa); + + // block 2 + graph.putEdge(ifl3e1, JIfStmt.TRUE_BRANCH_IDX, assignl3el2al1); + graph.putEdge(ifl3e1, JIfStmt.FALSE_BRANCH_IDX, assignl2aa); + + // block 3 + graph.putEdge(assignl1aa, assignl3el2al1); + + // block 4 + graph.putEdge(assignl2aa, ifl2l50); + graph.putEdge(ifl2l50, JIfStmt.FALSE_BRANCH_IDX, assignl3el2al1); + // graph.putEdge(ifl2l50, JIfStmt.TRUE_BRANCH_IDX, returnl3); + + // block 5 + graph.putEdge(assignl3el2al1, ifl1l50); + graph.putEdge(ifl1l50, JIfStmt.FALSE_BRANCH_IDX, assignl1aa); + graph.putEdge(ifl1l50, JIfStmt.TRUE_BRANCH_IDX, assignl2aa); + + return graph; + } + + // a graph with two end blocks + public MutableBlockStmtGraph createStmtGraph3() { + MutableBlockStmtGraph graph = new MutableBlockStmtGraph(); + + Local l1 = new Local("l1", PrimitiveType.IntType.getInstance()); + Local l2 = new Local("l2", PrimitiveType.IntType.getInstance()); + Local l3 = new Local("l3", PrimitiveType.IntType.getInstance()); + + JAssignStmt assignl1e1 = new JAssignStmt(l1, IntConstant.getInstance(1), noPosInfo); + JAssignStmt assignl2e1 = new JAssignStmt(l2, IntConstant.getInstance(2), noPosInfo); + JAssignStmt assignl3e0 = new JAssignStmt(l3, IntConstant.getInstance(0), noPosInfo); + + JAssignStmt assignl3e1 = new JAssignStmt(l3, IntConstant.getInstance(1), noPosInfo); + + BranchingStmt ifl2e100 = new JIfStmt(new JLtExpr(l3, IntConstant.getInstance(100)), noPosInfo); + + JReturnStmt returnl1 = new JReturnStmt(l1, noPosInfo); + JReturnStmt returnl2 = new JReturnStmt(l2, noPosInfo); + + graph.setStartingStmt(assignl1e1); + graph.putEdge(assignl1e1, assignl2e1); + graph.putEdge(assignl2e1, assignl3e0); + graph.putEdge(assignl3e0, ifl2e100); + graph.putEdge(ifl2e100, JIfStmt.TRUE_BRANCH_IDX, returnl1); + graph.putEdge(ifl2e100, JIfStmt.FALSE_BRANCH_IDX, returnl2); + + return graph; + } +} diff --git a/sootup.interceptors/src/main/java/sootup/interceptors/StaticSingleAssignmentFormer.java b/sootup.interceptors/src/main/java/sootup/interceptors/StaticSingleAssignmentFormer.java index 0bc4c6bf3b7..f5d1a01d0fe 100644 --- a/sootup.interceptors/src/main/java/sootup/interceptors/StaticSingleAssignmentFormer.java +++ b/sootup.interceptors/src/main/java/sootup/interceptors/StaticSingleAssignmentFormer.java @@ -90,7 +90,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) DominanceFinder dominanceFinder = new DominanceFinder(stmtGraph); - // decide which block should be add a phi assignStmt, and store such info in a map + // decide which block should be added a phi assignStmt, and store such info in a map // key: Block which contains phiStmts. Values : a set of phiStmts which contained by // corresponding Block Map, Set> blockToPhiStmts = @@ -99,7 +99,8 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) // delete meaningless phiStmts and add other phiStmts into stmtGraph addPhiStmts(blockToPhiStmts, stmtGraph, blockToDefs); - DominanceTree tree = new DominanceTree(dominanceFinder); + // some blocks are modified, so DominanceFinder must be updated for building dominance tree + DominanceTree tree = new DominanceTree(new DominanceFinder(stmtGraph)); Map> localToNameStack = new HashMap<>(); for (Local local : builder.getLocals()) { @@ -117,7 +118,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) for (Stmt stmt : block.getStmts()) { // replace use final List uses = stmt.getUses().collect(Collectors.toList()); - if (!uses.isEmpty() && !constainsPhiExpr(stmt)) { + if (!uses.isEmpty() && !containsPhiExpr(stmt)) { for (Value use : uses) { if (use instanceof Local) { Local newUse = localToNameStack.get(use).peek(); @@ -138,7 +139,7 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) localToNameStack.get(def).push(newDef); FallsThroughStmt newStmt = ((AbstractDefinitionStmt) stmt).withNewDef(newDef); stmtGraph.replaceNode(stmt, newStmt); - if (constainsPhiExpr(newStmt)) { + if (containsPhiExpr(newStmt)) { newPhiStmts.add(newStmt); } } @@ -201,11 +202,11 @@ public void interceptBody(@Nonnull Body.BodyBuilder builder, @Nonnull View view) * This method is used to decide which block should add phiStmts. Note: some phiStmts maybe * contain just one argument, it should be not added into StmtGraph * - * @param dominanceFinder an object of DomimanceFinder, it should be created by the given + * @param dominanceFinder an object of DominanceFinder, it should be created by the given * blockGraph * @param blockToDefs maps each block to the set of defs' local in itself * @param localToBlocks maps each def local to the set of blocks where it is defined. - * @return a map, key: block, value: a set of phiStmts that are added in front of the + * @return a map, key: block, value: a set of empty phiStmts that are added in front of the * corresponding block */ private Map, Set> decideBlockToPhiStmts( @@ -233,17 +234,14 @@ private Map, Set> decideBlockToPhiStmts( JAssignStmt phiStmt = createEmptyPhiStmt(local); // store phiStmt into map - if (blockToPhiStmts.containsKey(df)) { - blockToPhiStmts.get(df).add(phiStmt); - blockToPhiLocals.get(df).add(local); - } else { + if (!blockToPhiStmts.containsKey(df)) { Set phiStmts = new LinkedHashSet<>(); - phiStmts.add(phiStmt); blockToPhiStmts.put(df, phiStmts); Set phiLocals = new HashSet<>(); - phiLocals.add(local); blockToPhiLocals.put(df, phiLocals); } + blockToPhiStmts.get(df).add(phiStmt); + blockToPhiLocals.get(df).add(local); // if the dominance frontier contains no such local, its dominance frontier should add a // phiStmt, so add it into queue @@ -303,16 +301,28 @@ private void addPhiStmts( // if the arguments' size of a phiStmt is less than 2, delete it from blockToPhiStmts map // add other phiStmts into corresponding block + Map, BasicBlock> old2UpdatedBlock = new HashMap<>(); for (BasicBlock block : blockToPhiStmts.keySet()) { Set phis = blockToPhiStmts.get(block); Set checkedPhis = new HashSet<>(blockToPhiStmts.get(block)); for (FallsThroughStmt cphi : checkedPhis) { - if (phiToNum.get(cphi) < 2) { + if (!phiToNum.containsKey(cphi) || phiToNum.get(cphi) < 2) { phis.remove(cphi); } } for (FallsThroughStmt phi : phis) { - blockGraph.insertBefore(block.getHead(), phi); + BasicBlock updatedBlock = blockGraph.insertBefore(block.getHead(), phi); + if (!updatedBlock.equals(block)) { + old2UpdatedBlock.put(block, updatedBlock); + } + } + } + // update new blocks in blockToPhiStmts + if (!old2UpdatedBlock.isEmpty()) { + for (BasicBlock oldB : old2UpdatedBlock.keySet()) { + Set phis = blockToPhiStmts.get(oldB); + blockToPhiStmts.remove(oldB); + blockToPhiStmts.put(old2UpdatedBlock.get(oldB), phis); } } } @@ -326,7 +336,7 @@ private boolean containsAllChildren(Set> blockSet, List iterator = stmt.getUses().iterator(); iterator.hasNext(); ) { Value use = iterator.next(); diff --git a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/AssertUtils.java b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/AssertUtils.java index 515b6ad188e..31c9d19159d 100644 --- a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/AssertUtils.java +++ b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/AssertUtils.java @@ -29,7 +29,7 @@ public static void assertLocalsEquiv(Body expected, Body actual) { } } - // assert whether two bodys have the same stmtGraphs + // assert whether two bodies have the same stmtGraphs public static void assertStmtGraphEquiv(Body expected, Body actual) { StmtGraph expected_SG = expected.getStmtGraph(); StmtGraph actual_SG = actual.getStmtGraph(); diff --git a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/StaticSingleAssignmentFormerTest.java b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/StaticSingleAssignmentFormerTest.java index 57f76ad6f2d..151909015fb 100644 --- a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/StaticSingleAssignmentFormerTest.java +++ b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/StaticSingleAssignmentFormerTest.java @@ -3,8 +3,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import categories.TestCategories; +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.*; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import sootup.core.graph.MutableBlockStmtGraph; @@ -12,16 +14,15 @@ import sootup.core.jimple.basic.StmtPositionInfo; import sootup.core.jimple.common.constant.IntConstant; import sootup.core.jimple.common.ref.IdentityRef; -import sootup.core.jimple.common.stmt.BranchingStmt; -import sootup.core.jimple.common.stmt.FallsThroughStmt; -import sootup.core.jimple.common.stmt.JGotoStmt; -import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.common.stmt.*; import sootup.core.model.Body; +import sootup.core.model.SourceType; import sootup.core.signatures.MethodSignature; import sootup.core.types.ClassType; import sootup.core.types.VoidType; import sootup.core.util.ImmutableUtils; import sootup.interceptors.StaticSingleAssignmentFormer; +import sootup.java.bytecode.frontend.inputlocation.PathBasedAnalysisInputLocation; import sootup.java.core.JavaIdentifierFactory; import sootup.java.core.language.JavaJimple; import sootup.java.core.types.JavaClassType; @@ -29,13 +30,16 @@ /** @author Zun Wang */ @Tag(TestCategories.JAVA_8_CATEGORY) -@Disabled("ms: FIX IT") public class StaticSingleAssignmentFormerTest { // Preparation JavaIdentifierFactory factory = JavaIdentifierFactory.getInstance(); StmtPositionInfo noStmtPositionInfo = StmtPositionInfo.getNoStmtPositionInfo(); JavaJimple javaJimple = JavaJimple.getInstance(); + final String location = + Paths.get(System.getProperty("user.dir")).getParent() + + File.separator + + "shared-test-resources/bugfixes/"; JavaClassType intType = factory.getClassType("int"); JavaClassType classType = factory.getClassType("Test"); @@ -43,152 +47,225 @@ public class StaticSingleAssignmentFormerTest { MethodSignature methodSignature = new MethodSignature(classType, "test", Collections.emptyList(), VoidType.getInstance()); IdentityRef identityRef = JavaJimple.newThisRef(classType); - ClassType exception = factory.getClassType("Exception"); + ClassType exceptionType = factory.getClassType("Exception"); IdentityRef caughtExceptionRef = javaJimple.newCaughtExceptionRef(); // build locals - Local l0 = JavaJimple.newLocal("l0", intType); + Local l0 = JavaJimple.newLocal("l0", classType); Local l1 = JavaJimple.newLocal("l1", intType); Local l2 = JavaJimple.newLocal("l2", intType); Local l3 = JavaJimple.newLocal("l3", intType); Local stack4 = JavaJimple.newLocal("stack4", refType); - Stmt startingStmt = JavaJimple.newIdentityStmt(l0, identityRef, noStmtPositionInfo); - Stmt assign1tol1 = JavaJimple.newAssignStmt(l1, IntConstant.getInstance(1), noStmtPositionInfo); - Stmt assign1tol2 = JavaJimple.newAssignStmt(l2, IntConstant.getInstance(1), noStmtPositionInfo); - Stmt assign0tol3 = JavaJimple.newAssignStmt(l3, IntConstant.getInstance(0), noStmtPositionInfo); + JIdentityStmt startingStmt = JavaJimple.newIdentityStmt(l0, identityRef, noStmtPositionInfo); + JAssignStmt assign1tol1 = + JavaJimple.newAssignStmt(l1, IntConstant.getInstance(1), noStmtPositionInfo); + JAssignStmt assign1tol2 = + JavaJimple.newAssignStmt(l2, IntConstant.getInstance(2), noStmtPositionInfo); + JAssignStmt assign0tol3 = + JavaJimple.newAssignStmt(l3, IntConstant.getInstance(0), noStmtPositionInfo); BranchingStmt ifStmt = JavaJimple.newIfStmt( JavaJimple.newLtExpr(l3, IntConstant.getInstance(100)), noStmtPositionInfo); BranchingStmt ifStmt2 = JavaJimple.newIfStmt( JavaJimple.newLtExpr(l2, IntConstant.getInstance(20)), noStmtPositionInfo); - Stmt returnStmt = JavaJimple.newReturnStmt(l2, noStmtPositionInfo); - Stmt assignl1tol2 = JavaJimple.newAssignStmt(l2, l1, noStmtPositionInfo); - Stmt assignl3plus1tol3 = + JReturnStmt returnStmt = JavaJimple.newReturnStmt(l2, noStmtPositionInfo); + JAssignStmt assignl1tol2 = JavaJimple.newAssignStmt(l2, l1, noStmtPositionInfo); + JAssignStmt assignl3plus1tol3 = JavaJimple.newAssignStmt( l3, JavaJimple.newAddExpr(l3, IntConstant.getInstance(1)), noStmtPositionInfo); - Stmt assignl3tol2 = JavaJimple.newAssignStmt(l2, l3, noStmtPositionInfo); - Stmt assignl3plus2tol3 = + JAssignStmt assignl3tol2 = JavaJimple.newAssignStmt(l2, l3, noStmtPositionInfo); + JAssignStmt assignl3plus2tol3 = JavaJimple.newAssignStmt( l3, JavaJimple.newAddExpr(l3, IntConstant.getInstance(2)), noStmtPositionInfo); - BranchingStmt gotoStmt = JavaJimple.newGotoStmt(noStmtPositionInfo); + JGotoStmt gotoStmt1 = JavaJimple.newGotoStmt(noStmtPositionInfo); + JGotoStmt gotoStmt2 = JavaJimple.newGotoStmt(noStmtPositionInfo); + JGotoStmt gotoStmt3 = JavaJimple.newGotoStmt(noStmtPositionInfo); FallsThroughStmt handlerStmt = JavaJimple.newIdentityStmt(stack4, caughtExceptionRef, noStmtPositionInfo); - FallsThroughStmt l2eq0 = - JavaJimple.newAssignStmt(l2, IntConstant.getInstance(0), noStmtPositionInfo); - BranchingStmt goTo = JavaJimple.newGotoStmt(noStmtPositionInfo); + JAssignStmt l2eq2 = JavaJimple.newAssignStmt(l2, IntConstant.getInstance(2), noStmtPositionInfo); + JGotoStmt gotoStmt = JavaJimple.newGotoStmt(noStmtPositionInfo); @Test public void testSSA() { StaticSingleAssignmentFormer ssa = new StaticSingleAssignmentFormer(); Body.BodyBuilder builder = createBody(); ssa.interceptBody(builder, new JavaView(Collections.emptyList())); - String expectedBodyString = "{\n" - + " int l0, l1, l2, l3, l0#0, l1#1, l2#2, l3#3, l3#4, l2#5, l2#6, l3#7, l2#8, l3#9, l3#10, l2#11;\n" + + " Test l0, l0#0;\n" + + " int l1, l1#1, l2, l2#10, l2#2, l2#4, l2#6, l2#8, l3, l3#11, l3#3, l3#5, l3#7, l3#9;\n" + "\n" + "\n" + " l0#0 := @this: Test;\n" - + "\n" + " l1#1 = 1;\n" - + "\n" - + " l2#2 = 1;\n" - + "\n" + + " l2#2 = 2;\n" + " l3#3 = 0;\n" + "\n" + " label1:\n" - + " l3#4 = phi(l3#3, l3#10);\n" + + " l2#4 = phi(l2#2, l2#10);\n" + + " l3#5 = phi(l3#3, l3#11);\n" + "\n" - + " l2#5 = phi(l2#2, l2#11);\n" + + " if l3#5 < 100 goto label2;\n" + "\n" - + " if l3#4 < 100 goto label3;\n" + + " return l2#4;\n" + "\n" - + " if l2#5 < 20 goto label2;\n" + + " label2:\n" + + " if l2#4 < 20 goto label3;\n" + + " l2#8 = l3#5;\n" + + " l3#9 = l3#5 + 2;\n" + "\n" - + " l2#8 = l1#1;\n" + + " goto label4;\n" + "\n" - + " l3#9 = l3#4 + 1;\n" + + " label3:\n" + + " l2#6 = l1#1;\n" + + " l3#7 = l3#5 + 1;\n" + "\n" - + " l3#10 = phi(l3#7, l3#9);\n" + + " goto label4;\n" + "\n" - + " l2#11 = phi(l2#6, l2#8);\n" + + " label4:\n" + + " l2#10 = phi(l2#6, l2#8);\n" + + " l3#11 = phi(l3#7, l3#9);\n" + "\n" + " goto label1;\n" + + "}\n"; + + assertEquals(expectedBodyString, builder.build().toString()); + } + + @Test + // todo: delete after correction, some blocks has redundant predecessors + // todo: [$stack14 := @caughtexception ... throw $l14] + public void testSSA2() { + ClassType clazzType = factory.getClassType("TrapSSA"); + MethodSignature methodSignature = + factory.getMethodSignature( + clazzType, "main", "void", Collections.singletonList("java.lang.String[]")); + final Path path = Paths.get(location + "TrapSSA.class"); + PathBasedAnalysisInputLocation inputLocation = + new PathBasedAnalysisInputLocation.ClassFileBasedAnalysisInputLocation( + path, "", SourceType.Application); + PathBasedAnalysisInputLocation inputLocationWithSSA = + new PathBasedAnalysisInputLocation.ClassFileBasedAnalysisInputLocation( + path, + "", + SourceType.Application, + Collections.singletonList(new StaticSingleAssignmentFormer())); + JavaView view = new JavaView(inputLocation); + JavaView viewSSA = new JavaView(inputLocationWithSSA); + Body body = view.getMethod(methodSignature).get().getBody(); + Body bodyAfterSSA = viewSSA.getMethod(methodSignature).get().getBody(); + // System.out.println(bodyAfterSSA); + } + + @Test + public void testSSA3() { + ClassType clazzType = factory.getClassType("ForLoopSSA"); + MethodSignature methodSignature = + factory.getMethodSignature( + clazzType, "main", "void", Collections.singletonList("java.lang.String[]")); + final Path path = Paths.get(location + "ForLoopSSA.class"); + PathBasedAnalysisInputLocation inputLocationWithSSA = + new PathBasedAnalysisInputLocation.ClassFileBasedAnalysisInputLocation( + path, + "", + SourceType.Application, + Collections.singletonList(new StaticSingleAssignmentFormer())); + JavaView viewSSA = new JavaView(inputLocationWithSSA); + Body bodyAfterSSA = viewSSA.getMethod(methodSignature).get().getBody(); + + String expectedBodyString = + "{\n" + + " java.lang.String[] l0, l0#0;\n" + + " unknown $stack3, $stack3#5, $stack4, $stack4#6, $stack5, $stack5#7, $stack6, $stack6#9, $stack7, $stack7#8, $stack8, $stack8#10, l1, l1#1, l1#11, l1#3, l2, l2#12, l2#2, l2#4;\n" + "\n" - + " label2:\n" - + " l2#6 = l3#4;\n" + "\n" - + " l3#7 = l3#4 + 2;\n" + + " l0#0 := @parameter0: java.lang.String[];\n" + + " l1#1 = \"\";\n" + + " l2#2 = 0;\n" + "\n" - + " label3:\n" - + " return l2#5;\n" + + " label1:\n" + + " l1#3 = phi(l1#1, l1#11);\n" + + " l2#4 = phi(l2#2, l2#12);\n" + + " $stack3#5 = lengthof l0#0;\n" + + "\n" + + " if l2#4 >= $stack3#5 goto label2;\n" + + " $stack5#7 = new java.lang.StringBuilder;\n" + + " specialinvoke $stack5#7.()>();\n" + + " $stack7#8 = virtualinvoke $stack5#7.(l1#3);\n" + + " $stack6#9 = l0#0[l2#4];\n" + + " $stack8#10 = virtualinvoke $stack7#8.($stack6#9);\n" + + " l1#11 = virtualinvoke $stack8#10.();\n" + + " l2#12 = l2#4 + 1;\n" + + "\n" + + " goto label1;\n" + + "\n" + + " label2:\n" + + " $stack4#6 = ;\n" + + " virtualinvoke $stack4#6.(l1#3);\n" + + "\n" + + " return;\n" + "}\n"; - assertEquals(expectedBodyString, builder.build().toString()); + assertEquals(expectedBodyString, bodyAfterSSA.toString()); } @Test - public void testTrapedSSA() { + public void testTrappedSSA() { StaticSingleAssignmentFormer ssa = new StaticSingleAssignmentFormer(); Body.BodyBuilder builder = createTrapBody(); ssa.interceptBody(builder, new JavaView(Collections.emptyList())); - String expectedBodyString = "{\n" - + " int l0, l1, l2, l3, l0#0, l1#1, l2#2, l3#3, l3#4, l2#5, l2#6, l3#7, l2#8, l2#10, l2#11, l3#12, l3#13, l2#14;\n" - + " ref stack4, stack4#9;\n" + + " Test l0, l0#0;\n" + + " int l1, l1#1, l2, l2#11, l2#13, l2#2, l2#4, l2#6, l2#8, l2#9, l3, l3#10, l3#12, l3#14, l3#3, l3#5;\n" + + " ref stack4, stack4#7;\n" + "\n" + "\n" + " l0#0 := @this: Test;\n" - + "\n" + " l1#1 = 1;\n" - + "\n" - + " l2#2 = 1;\n" - + "\n" + + " l2#2 = 2;\n" + " l3#3 = 0;\n" + "\n" + " label1:\n" - + " l3#4 = phi(l3#3, l3#13);\n" - + "\n" - + " l2#5 = phi(l2#2, l2#14);\n" + + " l2#4 = phi(l2#2, l2#13);\n" + + " l3#5 = phi(l3#3, l3#14);\n" + "\n" - + " if l3#4 < 100 goto label6;\n" + + " if l3#5 < 100 goto label2;\n" + "\n" - + " if l2#5 < 20 goto label5;\n" + + " return l2#4;\n" + "\n" + " label2:\n" - + " l2#8 = l1#1;\n" - + "\n" - + " label3:\n" - + " l2#11 = phi(l2#8, l2#10);\n" - + "\n" - + " l3#12 = l3#4 + 1;\n" + + " if l2#4 < 20 goto label3;\n" + + " l2#11 = l3#5;\n" + + " l3#12 = l3#5 + 2;\n" + "\n" - + " l3#13 = phi(l3#7, l3#12);\n" + + " goto label6;\n" + "\n" - + " l2#14 = phi(l2#6, l2#11);\n" - + "\n" - + " goto label1;\n" + + " label3:\n" + + " l2#6 = l1#1;\n" + "\n" + " label4:\n" - + " stack4#9 := @caughtexception;\n" + + " l2#9 = phi(l2#6, l2#8);\n" + + " l3#10 = l3#5 + 1;\n" + "\n" - + " l2#10 = 0;\n" - + "\n" - + " goto label3;\n" + + " goto label6;\n" + "\n" + " label5:\n" - + " l2#6 = l3#4;\n" + + " stack4#7 := @caughtexception;\n" + + " l2#8 = 2;\n" + "\n" - + " l3#7 = l3#4 + 2;\n" + + " goto label4;\n" + "\n" + " label6:\n" - + " return l2#5;\n" + + " l2#13 = phi(l2#9, l2#11);\n" + + " l3#14 = phi(l3#10, l3#12);\n" + "\n" - + " catch Exception from label2 to label3 with label4;\n" + + " goto label1;\n" + + "\n" + + " catch Exception from label3 to label4 with label5;\n" + "}\n"; assertEquals(expectedBodyString, builder.build().toString()); @@ -200,23 +277,57 @@ public void testTrapedSSA() { *
    *    l0 := @this Test
    *    l1 = 1
-   *    l2 = 1
+   *    l2 = 2
    *    l3 = 0
    * label1:
-   *    if l3 < 100 goto label3
-   *    if l2 < 20 goto label 2
-   *    l2 = l1
-   *    l3 = l3 + 1
-   *    goto label1;
+   *    if l3 < 100 goto label2
+   *    return l2
    * label2:
+   *    if l2 < 20 goto label3
    *    l2 = l3
    *    l3 = l3 + 2
+   *    goto label4;
    * label3:
-   *    return l2
+   *    l2 = l1
+   *    l3 = l3 + 1
+   *    goto label4
+   * label3:
+   *    goto label1
    * 
*/ private Body.BodyBuilder createBody() { MutableBlockStmtGraph graph = new MutableBlockStmtGraph(); + + // Block 0 + graph.setStartingStmt(startingStmt); + graph.putEdge(startingStmt, assign1tol1); + graph.putEdge(assign1tol1, assign1tol2); + graph.putEdge(assign1tol2, assign0tol3); + + // block 1 + graph.putEdge(assign0tol3, ifStmt); + + // block 2 + graph.putEdge(ifStmt, JIfStmt.TRUE_BRANCH_IDX, ifStmt2); + + // block 3 + graph.putEdge(ifStmt, JIfStmt.FALSE_BRANCH_IDX, returnStmt); + + // block 4 + graph.putEdge(ifStmt2, JIfStmt.TRUE_BRANCH_IDX, assignl1tol2); + graph.putEdge(assignl1tol2, assignl3plus1tol3); + graph.putEdge(assignl3plus1tol3, gotoStmt1); + + // block 5 + graph.putEdge(ifStmt2, JIfStmt.FALSE_BRANCH_IDX, assignl3tol2); + graph.putEdge(assignl3tol2, assignl3plus2tol3); + graph.putEdge(assignl3plus2tol3, gotoStmt2); + + // block 6 + graph.putEdge(gotoStmt1, JGotoStmt.BRANCH_IDX, gotoStmt); + graph.putEdge(gotoStmt2, JGotoStmt.BRANCH_IDX, gotoStmt); + graph.putEdge(gotoStmt, JGotoStmt.BRANCH_IDX, ifStmt); + Body.BodyBuilder builder = Body.builder(graph); builder.setMethodSignature(methodSignature); @@ -224,20 +335,6 @@ private Body.BodyBuilder createBody() { Set locals = ImmutableUtils.immutableSet(l0, l1, l2, l3); builder.setLocals(locals); - Map> successorMap = new HashMap<>(); - successorMap.put(ifStmt, Collections.singletonList(returnStmt)); - successorMap.put(ifStmt2, Collections.singletonList(assignl1tol2)); - successorMap.put(gotoStmt, Collections.singletonList(ifStmt)); - - graph.initializeWith( - Arrays.asList( - Arrays.asList(startingStmt, assign1tol1, assign1tol2, assign0tol3, ifStmt), - Collections.singletonList(ifStmt2), - Arrays.asList(assignl1tol2, assignl3plus1tol3, gotoStmt), - Arrays.asList(assignl3tol2, assignl3plus2tol3, returnStmt)), - successorMap, - Collections.emptyList()); - return builder; } @@ -247,57 +344,78 @@ private Body.BodyBuilder createBody() { *
    *    l0 := @this Test
    *    l1 = 1
-   *    l2 = 1
+   *    l2 = 2
    *    l3 = 0
    * label1:
-   *    if l3 < 100 goto label6
-   *    if l2 < 20 goto label5
+   *    if l3 < 100 goto label2
+   *    return l2
    * label2:
-   *    l2 = l1
+   *    if l2 < 20 goto label3
+   *    l2 = l3
+   *    l3 = l3 + 2
+   *    goto label6
    * label3:
-   *    l3 = l3 + 1
-   *    goto label1;
+   *    l2 = l1
    * label4:
-   *    stack4 := @caughtexception
-   *    l2 = 0;
-   *    goto label3;
+   *    l3 = l3 + 1
+   *    goto label6
    * label5:
-   *    l2 = l3
-   *    l3 = l3 + 2
+   *    stack4 := @caughtexception
+   *    l2 = 2
+   *    goto label4
    * label6:
-   *    return l2
+   *    goto label1
    *
-   * catch Exception from label2 to label3 with label4;
+   * catch Exception from label3 to label5 with label5
    * 
*/ private Body.BodyBuilder createTrapBody() { MutableBlockStmtGraph graph = new MutableBlockStmtGraph(); + + // Block 0 + graph.setStartingStmt(startingStmt); + graph.putEdge(startingStmt, assign1tol1); + graph.putEdge(assign1tol1, assign1tol2); + graph.putEdge(assign1tol2, assign0tol3); + + // block 1 + graph.putEdge(assign0tol3, ifStmt); + + // block 2 + graph.putEdge(ifStmt, JIfStmt.TRUE_BRANCH_IDX, ifStmt2); + + // block 3 + graph.putEdge(ifStmt, JIfStmt.FALSE_BRANCH_IDX, returnStmt); + + // block 4 + graph.putEdge(ifStmt2, JIfStmt.TRUE_BRANCH_IDX, assignl1tol2); + + // block 5 exceptional block + graph.addNode(assignl1tol2, Collections.singletonMap(exceptionType, handlerStmt)); + graph.putEdge(handlerStmt, l2eq2); + graph.putEdge(l2eq2, gotoStmt3); + + // block 6 + graph.putEdge(gotoStmt3, JGotoStmt.BRANCH_IDX, assignl3plus1tol3); + graph.putEdge(assignl1tol2, assignl3plus1tol3); + graph.putEdge(assignl3plus1tol3, gotoStmt1); + + // block 7 + graph.putEdge(ifStmt2, JIfStmt.FALSE_BRANCH_IDX, assignl3tol2); + graph.putEdge(assignl3tol2, assignl3plus2tol3); + graph.putEdge(assignl3plus2tol3, gotoStmt2); + + // block 8 + graph.putEdge(gotoStmt1, JGotoStmt.BRANCH_IDX, gotoStmt); + graph.putEdge(gotoStmt2, JGotoStmt.BRANCH_IDX, gotoStmt); + graph.putEdge(gotoStmt, JGotoStmt.BRANCH_IDX, ifStmt); + Body.BodyBuilder builder = Body.builder(graph); builder.setMethodSignature(methodSignature); // build set locals Set locals = ImmutableUtils.immutableSet(l0, l1, l2, l3, stack4); builder.setLocals(locals); - Map> successorMap = new HashMap<>(); - successorMap.put(ifStmt, Collections.singletonList(returnStmt)); - successorMap.put(ifStmt2, Collections.singletonList(assignl1tol2)); - successorMap.put(gotoStmt, Collections.singletonList(ifStmt)); - - graph.initializeWith( - Arrays.asList( - Arrays.asList(startingStmt, assign1tol1, assign1tol2, assign0tol3, ifStmt), - Collections.singletonList(ifStmt2), - Arrays.asList(assignl1tol2, assignl3plus1tol3, gotoStmt), - Arrays.asList(assignl3tol2, assignl3plus2tol3, returnStmt)), - successorMap, - Collections.emptyList()); - - // add exception - graph.addNode(assignl1tol2, Collections.singletonMap(exception, handlerStmt)); - - graph.putEdge(handlerStmt, l2eq0); - graph.putEdge(l2eq0, goTo); - graph.putEdge(goTo, JGotoStmt.BRANCH_IDX, assignl3plus1tol3); return builder; }