From 9024d31a72ec14526570877deb45a21dd1c9e7d7 Mon Sep 17 00:00:00 2001 From: sahilagichani Date: Sun, 8 Dec 2024 23:05:25 +0100 Subject: [PATCH] BlockGraphIterator, BlockGraphIteratorAndTrapAggregator separate classes, buildtraps to LabeledStmtPrinter --- .../sootup/core/graph/BlockGraphIterator.java | 194 ++++++++++++ .../java/sootup/core/graph/StmtGraph.java | 293 +----------------- .../src/main/java/sootup/core/model/Body.java | 3 +- .../BlockGraphIteratorAndTrapAggregator.java | 105 +++++++ .../core/util/printer/JimplePrinter.java | 40 +-- .../core/util/printer/LabeledStmtPrinter.java | 89 +++++- .../core/graph/MutableBlockStmtGraphTest.java | 22 +- .../interceptors/TrapTightenerTest.java | 6 +- .../UnreachableCodeEliminatorTest.java | 6 +- .../sootup/tests/JimpleSerializationTest.java | 1 + .../repository/git/TrapSerialization.java | 8 +- 11 files changed, 417 insertions(+), 350 deletions(-) create mode 100644 sootup.core/src/main/java/sootup/core/graph/BlockGraphIterator.java create mode 100644 sootup.core/src/main/java/sootup/core/util/printer/BlockGraphIteratorAndTrapAggregator.java diff --git a/sootup.core/src/main/java/sootup/core/graph/BlockGraphIterator.java b/sootup.core/src/main/java/sootup/core/graph/BlockGraphIterator.java new file mode 100644 index 0000000000..ffcb1a6d82 --- /dev/null +++ b/sootup.core/src/main/java/sootup/core/graph/BlockGraphIterator.java @@ -0,0 +1,194 @@ +package sootup.core.graph; + +import sootup.core.jimple.common.stmt.JGotoStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.types.ClassType; +import sootup.core.util.DotExporter; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Iterates over the blocks + */ +public class BlockGraphIterator implements Iterator> { + + private final StmtGraph stmtGraph; + @Nonnull + private final ArrayDeque> trapHandlerBlocks = new ArrayDeque<>(); + + @Nonnull + private final ArrayDeque> nestedBlocks = new ArrayDeque<>(); + @Nonnull + private final ArrayDeque> otherBlocks = new ArrayDeque<>(); + @Nonnull + private final Set> iteratedBlocks; + + public BlockGraphIterator(StmtGraph stmtGraph) { + this.stmtGraph = stmtGraph; + final Collection> blocks = stmtGraph.getBlocks(); + iteratedBlocks = new LinkedHashSet<>(blocks.size(), 1); + Stmt startingStmt = stmtGraph.getStartingStmt(); + if (startingStmt != null) { + final BasicBlock startingBlock = stmtGraph.getStartingStmtBlock(); + updateFollowingBlocks(startingBlock); + nestedBlocks.addFirst(startingBlock); + } + } + + @Nullable + private BasicBlock retrieveNextBlock() { + BasicBlock nextBlock; + do { + if (!nestedBlocks.isEmpty()) { + nextBlock = nestedBlocks.pollFirst(); + } else if (!trapHandlerBlocks.isEmpty()) { + nextBlock = trapHandlerBlocks.pollFirst(); + } else if (!otherBlocks.isEmpty()) { + nextBlock = otherBlocks.pollFirst(); + } else { + Collection> blocks = stmtGraph.getBlocks(); + if (iteratedBlocks.size() < blocks.size()) { + // graph is not connected! iterate/append all not connected blocks at the end in no + // particular order. + for (BasicBlock block : blocks) { + if (!iteratedBlocks.contains(block)) { + nestedBlocks.addLast(block); + } + } + if (!nestedBlocks.isEmpty()) { + return nestedBlocks.pollFirst(); + } + } + + return null; + } + + // skip retrieved nextBlock if its already returned + } while (iteratedBlocks.contains(nextBlock)); + return nextBlock; + } + + @Override + @Nonnull + public BasicBlock next() { + BasicBlock currentBlock = retrieveNextBlock(); + if (currentBlock == null) { + throw new NoSuchElementException("Iterator has no more Blocks."); + } + updateFollowingBlocks(currentBlock); + iteratedBlocks.add(currentBlock); + return currentBlock; + } + + private void updateFollowingBlocks(BasicBlock currentBlock) { + // collect traps + final Stmt tailStmt = currentBlock.getTail(); + for (Map.Entry> entry : + currentBlock.getExceptionalSuccessors().entrySet()) { + BasicBlock trapHandlerBlock = entry.getValue(); + trapHandlerBlocks.addLast(trapHandlerBlock); + nestedBlocks.addFirst(trapHandlerBlock); + } + + final List> successors = currentBlock.getSuccessors(); + + for (int i = successors.size() - 1; i >= 0; i--) { + if (i == 0 && tailStmt.fallsThrough()) { + // non-branching successors i.e. not a BranchingStmt or is the first successor (i.e. its + // false successor) of + // JIfStmt + nestedBlocks.addFirst(successors.get(0)); + } else { + + // create the longest FallsThroughStmt sequence possible + final BasicBlock successorBlock = successors.get(i); + BasicBlock leaderOfFallsthroughBlocks = successorBlock; + while (true) { + final List> itPreds = + leaderOfFallsthroughBlocks.getPredecessors(); + + BasicBlock finalLeaderOfFallsthroughBlocks = leaderOfFallsthroughBlocks; + final Optional> fallsthroughPredOpt = + itPreds.stream() + .filter( + b -> + b.getTail().fallsThrough() + && b.getSuccessors().get(0) == finalLeaderOfFallsthroughBlocks) + .findAny(); + if (!fallsthroughPredOpt.isPresent()) { + break; + } + BasicBlock predecessorBlock = fallsthroughPredOpt.get(); + if (predecessorBlock.getTail().fallsThrough() + && predecessorBlock.getSuccessors().get(0) == leaderOfFallsthroughBlocks) { + leaderOfFallsthroughBlocks = predecessorBlock; + } else { + break; + } + } + + // find a return Stmt inside the current Block + Stmt succTailStmt = successorBlock.getTail(); + boolean hasNoSuccessorStmts = succTailStmt.getExpectedSuccessorCount() == 0; + boolean isExceptionFree = successorBlock.getExceptionalSuccessors().isEmpty(); + + boolean isLastStmtCandidate = hasNoSuccessorStmts && isExceptionFree; + // remember branching successors + if (tailStmt instanceof JGotoStmt) { + if (isLastStmtCandidate) { + nestedBlocks.removeFirstOccurrence(currentBlock); + otherBlocks.addLast(leaderOfFallsthroughBlocks); + } else { + otherBlocks.addFirst(leaderOfFallsthroughBlocks); + } + } else if (!nestedBlocks.contains(leaderOfFallsthroughBlocks)) { + // JSwitchStmt, JIfStmt + if (isLastStmtCandidate) { + nestedBlocks.addLast(leaderOfFallsthroughBlocks); + } else { + nestedBlocks.addFirst(leaderOfFallsthroughBlocks); + } + } + } + } + } + + @Override + public boolean hasNext() { + final boolean hasIteratorMoreElements; + BasicBlock b = retrieveNextBlock(); + if (b != null) { + // reinsert at FIRST position -> not great for performance - but easier handling in + // next() + nestedBlocks.addFirst(b); + hasIteratorMoreElements = true; + } else { + hasIteratorMoreElements = false; + } + + // "assertion" that all elements are iterated + if (!hasIteratorMoreElements) { + final int returnedSize = iteratedBlocks.size(); + final Collection> blocks = stmtGraph.getBlocks(); + final int actualSize = blocks.size(); + if (returnedSize != actualSize) { + String info = + blocks.stream() + .filter(n -> !iteratedBlocks.contains(n)) + .map(BasicBlock::getStmts) + .collect(Collectors.toList()) + .toString(); + throw new IllegalStateException( + "There are " + + (actualSize - returnedSize) + + " Blocks that are not iterated! i.e. the StmtGraph is not connected from its startingStmt!" + + info + + DotExporter.createUrlToWebeditor(stmtGraph)); + } + } + return hasIteratorMoreElements; + } +} 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 15df3c0158..bb3b919e27 100644 --- a/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/StmtGraph.java @@ -37,6 +37,7 @@ import sootup.core.types.ClassType; import sootup.core.util.DotExporter; import sootup.core.util.EscapedWriter; +import sootup.core.util.printer.BriefStmtPrinter; import sootup.core.util.printer.JimplePrinter; /** @@ -66,6 +67,7 @@ public abstract class StmtGraph> implements Iterable { public final JimplePrinter jimplePrinter = new JimplePrinter(); + public final BriefStmtPrinter briefStmtPrinter = new BriefStmtPrinter(); public abstract Stmt getStartingStmt(); @@ -91,7 +93,7 @@ public List getStmts() { public abstract List> getBlocksSorted(); public Iterator> getBlockIterator() { - return new BlockGraphIterator(); + return new BlockGraphIterator(this); } public abstract BasicBlock getBlockOf(@Nonnull Stmt stmt); @@ -356,7 +358,7 @@ public boolean equals(Object o) { return false; } - if (!jimplePrinter.buildTraps(this).equals(jimplePrinter.buildTraps(otherGraph))) { + if (!briefStmtPrinter.buildTraps(this).equals(briefStmtPrinter.buildTraps(otherGraph))) { return false; } @@ -418,7 +420,7 @@ private class BlockStmtGraphIterator implements Iterator { @Nonnull private Iterator currentBlockIt = Collections.emptyIterator(); public BlockStmtGraphIterator() { - this(new BlockGraphIterator()); + this(new BlockGraphIterator(StmtGraph.this)); } public BlockStmtGraphIterator(@Nonnull BlockGraphIterator blockIterator) { @@ -444,291 +446,6 @@ public Stmt next() { } } - /** Iterates over the Blocks and collects/aggregates Trap information */ - public class BlockGraphIteratorAndTrapAggregator extends BlockGraphIterator { - - @Nonnull private final List collectedTraps = new ArrayList<>(); - - Map activeTraps = new HashMap<>(); - BasicBlock lastIteratedBlock; // dummy value to remove n-1 unnecessary null-checks - - /* - * @param dummyBlock is just an empty instantiation of type V - as neither BasicBlock nor V instantiable we need a concrete object from the using subclass itclass. - * */ - public BlockGraphIteratorAndTrapAggregator(V dummyBlock) { - super(); - lastIteratedBlock = dummyBlock; - } - - @Nonnull - @Override - public BasicBlock next() { - final BasicBlock block = super.next(); - - final Map> currentBlocksExceptions = - block.getExceptionalSuccessors(); - final Map> lastBlocksExceptions = - lastIteratedBlock.getExceptionalSuccessors(); - - // former trap info is not in the current blocks info -> add it to the trap collection - lastBlocksExceptions.forEach( - (type, trapHandlerBlock) -> { - if (trapHandlerBlock != block.getExceptionalSuccessors().get(type)) { - final Stmt trapBeginStmt = activeTraps.remove(type); - if (trapBeginStmt == null) { - throw new IllegalStateException("Trap start for '" + type + "' is not in the Map!"); - } - // trapend is exclusive! - collectedTraps.add( - new Trap(type, trapBeginStmt, block.getHead(), trapHandlerBlock.getHead())); - } - }); - - // is there a new trap in the current block -> add it to currentTraps - block - .getExceptionalSuccessors() - .forEach( - (type, trapHandlerBlock) -> { - if (trapHandlerBlock != lastBlocksExceptions.get(type)) { - activeTraps.put(type, block.getHead()); - } - }); - - lastIteratedBlock = block; - return block; - } - - /** - * for jimple serialization - this info contains only valid/useful information if all stmts are - * iterated i.e. hasNext() == false! - * - * @return List of Traps - */ - public List getTraps() { - - if (hasNext()) { - throw new IllegalStateException("Iterator needs to be iterated completely!"); - } - - // check for dangling traps that are not collected as the endStmt was not visited. - if (!activeTraps.isEmpty()) { - throw new IllegalArgumentException( - "Invalid StmtGraph. A Trap is not created as a traps endStmt was not visited during the iteration of all Stmts."); - } - return collectedTraps; - } - } - - /** Iterates over the blocks */ - public class BlockGraphIterator implements Iterator> { - - @Nonnull private final ArrayDeque> trapHandlerBlocks = new ArrayDeque<>(); - - @Nonnull private final ArrayDeque> nestedBlocks = new ArrayDeque<>(); - @Nonnull private final ArrayDeque> otherBlocks = new ArrayDeque<>(); - @Nonnull private final Set> iteratedBlocks; - - public BlockGraphIterator() { - final Collection> blocks = getBlocks(); - iteratedBlocks = new LinkedHashSet<>(blocks.size(), 1); - Stmt startingStmt = getStartingStmt(); - if (startingStmt != null) { - final BasicBlock startingBlock = getStartingStmtBlock(); - updateFollowingBlocks(startingBlock); - nestedBlocks.addFirst(startingBlock); - } - } - - @Nullable - private BasicBlock retrieveNextBlock() { - BasicBlock nextBlock; - do { - if (!nestedBlocks.isEmpty()) { - nextBlock = nestedBlocks.pollFirst(); - } else if (!trapHandlerBlocks.isEmpty()) { - nextBlock = trapHandlerBlocks.pollFirst(); - } else if (!otherBlocks.isEmpty()) { - nextBlock = otherBlocks.pollFirst(); - } else { - Collection> blocks = getBlocks(); - if (iteratedBlocks.size() < blocks.size()) { - // graph is not connected! iterate/append all not connected blocks at the end in no - // particular order. - for (BasicBlock block : blocks) { - if (!iteratedBlocks.contains(block)) { - nestedBlocks.addLast(block); - } - } - if (!nestedBlocks.isEmpty()) { - return nestedBlocks.pollFirst(); - } - } - - return null; - } - - // skip retrieved nextBlock if its already returned - } while (iteratedBlocks.contains(nextBlock)); - return nextBlock; - } - - @Override - @Nonnull - public BasicBlock next() { - BasicBlock currentBlock = retrieveNextBlock(); - if (currentBlock == null) { - throw new NoSuchElementException("Iterator has no more Blocks."); - } - updateFollowingBlocks(currentBlock); - iteratedBlocks.add(currentBlock); - return currentBlock; - } - - private void updateFollowingBlocks(BasicBlock currentBlock) { - // collect traps - final Stmt tailStmt = currentBlock.getTail(); - for (Map.Entry> entry : - currentBlock.getExceptionalSuccessors().entrySet()) { - BasicBlock trapHandlerBlock = entry.getValue(); - trapHandlerBlocks.addLast(trapHandlerBlock); - nestedBlocks.addFirst(trapHandlerBlock); - } - - final List> successors = currentBlock.getSuccessors(); - - for (int i = successors.size() - 1; i >= 0; i--) { - if (i == 0 && tailStmt.fallsThrough()) { - // non-branching successors i.e. not a BranchingStmt or is the first successor (i.e. its - // false successor) of - // JIfStmt - nestedBlocks.addFirst(successors.get(0)); - } else { - - // create the longest FallsThroughStmt sequence possible - final BasicBlock successorBlock = successors.get(i); - BasicBlock leaderOfFallsthroughBlocks = successorBlock; - while (true) { - final List> itPreds = - leaderOfFallsthroughBlocks.getPredecessors(); - - BasicBlock finalLeaderOfFallsthroughBlocks = leaderOfFallsthroughBlocks; - final Optional> fallsthroughPredOpt = - itPreds.stream() - .filter( - b -> - b.getTail().fallsThrough() - && b.getSuccessors().get(0) == finalLeaderOfFallsthroughBlocks) - .findAny(); - if (!fallsthroughPredOpt.isPresent()) { - break; - } - BasicBlock predecessorBlock = fallsthroughPredOpt.get(); - if (predecessorBlock.getTail().fallsThrough() - && predecessorBlock.getSuccessors().get(0) == leaderOfFallsthroughBlocks) { - leaderOfFallsthroughBlocks = predecessorBlock; - } else { - break; - } - } - - // find a return Stmt inside the current Block - Stmt succTailStmt = successorBlock.getTail(); - boolean hasNoSuccessorStmts = succTailStmt.getExpectedSuccessorCount() == 0; - boolean isExceptionFree = successorBlock.getExceptionalSuccessors().isEmpty(); - - boolean isLastStmtCandidate = hasNoSuccessorStmts && isExceptionFree; - // remember branching successors - if (tailStmt instanceof JGotoStmt) { - if (isLastStmtCandidate) { - nestedBlocks.removeFirstOccurrence(currentBlock); - otherBlocks.addLast(leaderOfFallsthroughBlocks); - } else { - otherBlocks.addFirst(leaderOfFallsthroughBlocks); - } - } else if (!nestedBlocks.contains(leaderOfFallsthroughBlocks)) { - // JSwitchStmt, JIfStmt - if (isLastStmtCandidate) { - nestedBlocks.addLast(leaderOfFallsthroughBlocks); - } else { - nestedBlocks.addFirst(leaderOfFallsthroughBlocks); - } - } - } - } - } - - @Override - public boolean hasNext() { - final boolean hasIteratorMoreElements; - BasicBlock b = retrieveNextBlock(); - if (b != null) { - // reinsert at FIRST position -> not great for performance - but easier handling in - // next() - nestedBlocks.addFirst(b); - hasIteratorMoreElements = true; - } else { - hasIteratorMoreElements = false; - } - - // "assertion" that all elements are iterated - if (!hasIteratorMoreElements) { - final int returnedSize = iteratedBlocks.size(); - final Collection> blocks = getBlocks(); - final int actualSize = blocks.size(); - if (returnedSize != actualSize) { - String info = - blocks.stream() - .filter(n -> !iteratedBlocks.contains(n)) - .map(BasicBlock::getStmts) - .collect(Collectors.toList()) - .toString(); - throw new IllegalStateException( - "There are " - + (actualSize - returnedSize) - + " Blocks that are not iterated! i.e. the StmtGraph is not connected from its startingStmt!" - + info - + DotExporter.createUrlToWebeditor(StmtGraph.this)); - } - } - return hasIteratorMoreElements; - } - } - - /** - * Returns the result of iterating through all Stmts in this body. All Stmts thus found are - * returned. Branching Stmts and statements which use PhiExpr will have Stmts; a Stmt contains a - * Stmt that is either a target of a branch or is being used as a pointer to the end of a CFG - * block. - * - *

This method was typically used for pointer patching, e.g. when the unit chain is cloned. - * - * @return A collection of all the Stmts that are targets of a BranchingStmt - */ - @Nonnull - public Collection getLabeledStmts() { - Set stmtList = new HashSet<>(); - for (Stmt stmt : getNodes()) { - if (stmt instanceof BranchingStmt) { - if (stmt instanceof JIfStmt) { - stmtList.add(getBranchTargetsOf((JIfStmt) stmt).get(JIfStmt.FALSE_BRANCH_IDX)); - } else if (stmt instanceof JGotoStmt) { - // [ms] bounds are validated in Body if its a valid StmtGraph - stmtList.add(getBranchTargetsOf((JGotoStmt) stmt).get(JGotoStmt.BRANCH_IDX)); - } else if (stmt instanceof JSwitchStmt) { - stmtList.addAll(getBranchTargetsOf((BranchingStmt) stmt)); - } - } - } - - for (Trap trap : jimplePrinter.buildTraps(this)) { - stmtList.add(trap.getBeginStmt()); - stmtList.add(trap.getEndStmt()); - stmtList.add(trap.getHandlerStmt()); - } - - return stmtList; - } - @Override public String toString() { StringWriter writer = new StringWriter(); 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 1c161f05aa..22838f71ae 100644 --- a/sootup.core/src/main/java/sootup/core/model/Body.java +++ b/sootup.core/src/main/java/sootup/core/model/Body.java @@ -38,6 +38,7 @@ import sootup.core.jimple.common.stmt.*; import sootup.core.signatures.MethodSignature; import sootup.core.util.EscapedWriter; +import sootup.core.util.printer.BriefStmtPrinter; import sootup.core.util.printer.JimplePrinter; /** @@ -117,7 +118,7 @@ public Set getLocals() { @Nonnull @Deprecated() public List getTraps() { - return new JimplePrinter().buildTraps(graph); + return new BriefStmtPrinter().buildTraps(graph); } /** Return unit containing the \@this-assignment * */ diff --git a/sootup.core/src/main/java/sootup/core/util/printer/BlockGraphIteratorAndTrapAggregator.java b/sootup.core/src/main/java/sootup/core/util/printer/BlockGraphIteratorAndTrapAggregator.java new file mode 100644 index 0000000000..3401074610 --- /dev/null +++ b/sootup.core/src/main/java/sootup/core/util/printer/BlockGraphIteratorAndTrapAggregator.java @@ -0,0 +1,105 @@ +package sootup.core.util.printer; + +import sootup.core.graph.BasicBlock; +import sootup.core.graph.BlockGraphIterator; +import sootup.core.graph.MutableBasicBlockImpl; +import sootup.core.graph.StmtGraph; +import sootup.core.jimple.basic.StmtPositionInfo; +import sootup.core.jimple.basic.Trap; +import sootup.core.jimple.common.stmt.JNopStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.types.ClassType; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Iterates over the Blocks and collects/aggregates Trap information + * It is used to collect and aggregate traps for serializing Jimple in the JimplePrinter + */ +class BlockGraphIteratorAndTrapAggregator extends BlockGraphIterator { + + @Nonnull + private final List collectedTraps = new ArrayList<>(); + + Map activeTraps = new HashMap<>(); + BasicBlock lastIteratedBlock; // dummy value to remove n-1 unnecessary null-checks + JNopStmt lastStmt = null; + + /* + * @param dummyBlock is just an empty instantiation of type V - as neither BasicBlock nor V instantiable we need a concrete object from the using subclass itclass. + * */ + public BlockGraphIteratorAndTrapAggregator(StmtGraph stmtGraph) { + super(stmtGraph); + lastIteratedBlock = new MutableBasicBlockImpl(); + } + + @Nullable + public JNopStmt getLastStmt() { + return lastStmt; + } + + @Nonnull + @Override + public BasicBlock next() { + final BasicBlock block = super.next(); + + final Map> currentBlocksExceptions = + block.getExceptionalSuccessors(); + final Map> lastBlocksExceptions = + lastIteratedBlock.getExceptionalSuccessors(); + + // former trap info is not in the current blocks info -> add it to the trap collection + lastBlocksExceptions.forEach( + (type, trapHandlerBlock) -> { + if (trapHandlerBlock != block.getExceptionalSuccessors().get(type)) { + final Stmt trapBeginStmt = activeTraps.remove(type); + if (trapBeginStmt == null) { + throw new IllegalStateException("Trap start for '" + type + "' is not in the Map!"); + } + // trapend is exclusive! + collectedTraps.add( + new Trap(type, trapBeginStmt, block.getHead(), trapHandlerBlock.getHead())); + } + }); + + // is there a new trap in the current block -> add it to currentTraps + block + .getExceptionalSuccessors() + .forEach( + (type, trapHandlerBlock) -> { + if (trapHandlerBlock != lastBlocksExceptions.get(type)) { + activeTraps.put(type, block.getHead()); + } + }); + + lastIteratedBlock = block; + return block; + } + + /** + * for jimple serialization - this info contains only valid/useful information if all stmts are + * iterated i.e. hasNext() == false! + * + * @return List of Traps + */ + public List getTraps() { + + if (hasNext()) { + throw new IllegalStateException("Iterator needs to be iterated completely!"); + } + + // check for dangling traps that are not collected as the endStmt was not visited. + if (!activeTraps.isEmpty()) { + lastStmt = new JNopStmt(StmtPositionInfo.getNoStmtPositionInfo()); +// throw new IllegalArgumentException( +// "Invalid StmtGraph. A Trap is not created as a traps endStmt was not visited during the iteration of all Stmts."); + activeTraps.forEach((type, beginning) -> collectedTraps.add(new Trap(type, beginning, lastStmt, lastIteratedBlock.getExceptionalSuccessors().get(type).getHead()))); + } + return collectedTraps; + } +} diff --git a/sootup.core/src/main/java/sootup/core/util/printer/JimplePrinter.java b/sootup.core/src/main/java/sootup/core/util/printer/JimplePrinter.java index a5cd90c5f5..d05ff99b19 100644 --- a/sootup.core/src/main/java/sootup/core/util/printer/JimplePrinter.java +++ b/sootup.core/src/main/java/sootup/core/util/printer/JimplePrinter.java @@ -300,44 +300,6 @@ public void printTo(StmtGraph graph, PrintWriter out, LabeledStmtPrinter prin out.print(printer); } - /** - * returns a (reconstructed) list of traps like the traptable in the bytecode - * - *

Note: if you need exceptionional flow information in more augmented with the affected - * blocks/stmts and not just a (reconstructed, possibly more verbose) traptable - have a look at - * BasicBlock.getExceptionalSuccessor() - */ - /** hint: little expensive getter - its more of a build/create - currently no overlaps */ - public List buildTraps(StmtGraph stmtGraph) { - // [ms] try to incorporate it into the serialisation of jimple printing so the other half of - // iteration information is not wasted.. - StmtGraph.BlockGraphIteratorAndTrapAggregator it = stmtGraph.new BlockGraphIteratorAndTrapAggregator(new MutableBasicBlockImpl()); - // it.getTraps() is valid/completely build when the iterator is done. - Map stmtsBlockIdx = new IdentityHashMap<>(); - int i = 0; - // collect BlockIdx positions to sort the traps according to the numbering - while (it.hasNext()) { - final BasicBlock nextBlock = it.next(); - stmtsBlockIdx.put(nextBlock.getHead(), i); - stmtsBlockIdx.put(nextBlock.getTail(), i); - i++; - } - final List traps = it.getTraps(); - traps.sort(getTrapComparator(stmtsBlockIdx)); - return traps; - } - - /** Comparator which sorts the trap output in getTraps() */ - public Comparator getTrapComparator(@Nonnull Map stmtsBlockIdx) { - return (a, b) -> - ComparisonChain.start() - .compare(stmtsBlockIdx.get(a.getBeginStmt()), stmtsBlockIdx.get(b.getBeginStmt())) - .compare(stmtsBlockIdx.get(a.getEndStmt()), stmtsBlockIdx.get(b.getEndStmt())) - // [ms] would be nice to have the traps ordered by exception hierarchy as well - .compare(a.getExceptionType().toString(), b.getExceptionType().toString()) - .result(); - } - /** * Prints out the method corresponding to b Body, (declaration and body), in the textual format * corresponding to the IR used to encode b body. @@ -421,7 +383,7 @@ private void printStmts(StmtGraph stmtGraph, LabeledStmtPrinter printer) { // Print out exceptions { - Iterator trapIt = buildTraps(stmtGraph).iterator(); + Iterator trapIt = printer.buildTraps(stmtGraph).iterator(); if (trapIt.hasNext()) { printer.newline(); diff --git a/sootup.core/src/main/java/sootup/core/util/printer/LabeledStmtPrinter.java b/sootup.core/src/main/java/sootup/core/util/printer/LabeledStmtPrinter.java index f49d463cb6..abaf19a05b 100644 --- a/sootup.core/src/main/java/sootup/core/util/printer/LabeledStmtPrinter.java +++ b/sootup.core/src/main/java/sootup/core/util/printer/LabeledStmtPrinter.java @@ -24,11 +24,15 @@ import java.util.*; import javax.annotation.Nonnull; + +import com.google.common.collect.ComparisonChain; +import sootup.core.graph.BasicBlock; import sootup.core.graph.StmtGraph; import sootup.core.jimple.Jimple; import sootup.core.jimple.basic.Trap; import sootup.core.jimple.common.ref.IdentityRef; -import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.jimple.common.stmt.*; +import sootup.core.jimple.javabytecode.stmt.JSwitchStmt; import sootup.core.model.SootField; import sootup.core.model.SootMethod; import sootup.core.signatures.FieldSignature; @@ -113,8 +117,8 @@ public Iterable initializeSootMethod(@Nonnull StmtGraph stmtGraph) { @Nonnull public List getStmts(@Nonnull StmtGraph stmtGraph) { - final Collection targetStmtsOfBranches = stmtGraph.getLabeledStmts(); - final List traps = new JimplePrinter().buildTraps(stmtGraph); + final Collection targetStmtsOfBranches = getLabeledStmts(stmtGraph); + final List traps = buildTraps(stmtGraph); final int maxEstimatedSize = targetStmtsOfBranches.size() + traps.size() * 3; labels = new HashMap<>(maxEstimatedSize, 1); @@ -136,7 +140,7 @@ public List getStmts(@Nonnull StmtGraph stmtGraph) { // or is the begin of a trap-range) or does it mark the end of a trap range // does it need a label for (Stmt stmt : targetStmtsOfBranches) { - if (stmtGraph.isStmtBranchTarget(stmt) || trapStmts.contains(stmt)) { + if ((stmt instanceof JNopStmt) || stmtGraph.isStmtBranchTarget(stmt) || trapStmts.contains(stmt)) { labelStmts.add(stmt); } else { refStmts.add(stmt); @@ -195,4 +199,81 @@ public void fieldSignature(FieldSignature fieldSig) { typeSignature(subSignature.getType()); output.append(' ').append(Jimple.escape(subSignature.getName())).append('>'); } + + /** + * returns a (reconstructed) list of traps like the traptable in the bytecode + * + *

Note: if you need exceptionional flow information in more augmented with the affected + * blocks/stmts and not just a (reconstructed, possibly more verbose) traptable - have a look at + * BasicBlock.getExceptionalSuccessor() + */ + /** hint: little expensive getter - its more of a build/create - currently no overlaps */ + public List buildTraps(StmtGraph stmtGraph) { + // [ms] try to incorporate it into the serialisation of jimple printing so the other half of + // iteration information is not wasted.. + BlockGraphIteratorAndTrapAggregator it = new BlockGraphIteratorAndTrapAggregator(stmtGraph); + // it.getTraps() is valid/completely build when the iterator is done. + Map stmtsBlockIdx = new IdentityHashMap<>(); + int i = 0; + // collect BlockIdx positions to sort the traps according to the numbering + while (it.hasNext()) { + final BasicBlock nextBlock = it.next(); + stmtsBlockIdx.put(nextBlock.getHead(), i); + stmtsBlockIdx.put(nextBlock.getTail(), i); + i++; + } + final List traps = it.getTraps(); + if (it.getLastStmt() != null) { + stmtsBlockIdx.put(it.getLastStmt(), i); + } + traps.sort(getTrapComparator(stmtsBlockIdx)); + return traps; + } + + /** Comparator which sorts the trap output in getTraps() */ + public Comparator getTrapComparator(@Nonnull Map stmtsBlockIdx) { + return (a, b) -> + ComparisonChain.start() + .compare(stmtsBlockIdx.get(a.getBeginStmt()), stmtsBlockIdx.get(b.getBeginStmt())) + .compare(stmtsBlockIdx.get(a.getEndStmt()), stmtsBlockIdx.get(b.getEndStmt())) + // [ms] would be nice to have the traps ordered by exception hierarchy as well + .compare(a.getExceptionType().toString(), b.getExceptionType().toString()) + .result(); + } + + /** + * Returns the result of iterating through all Stmts in this body. All Stmts thus found are + * returned. Branching Stmts and statements which use PhiExpr will have Stmts; a Stmt contains a + * Stmt that is either a target of a branch or is being used as a pointer to the end of a CFG + * block. + * + *

This method was typically used for pointer patching, e.g. when the unit chain is cloned. + * + * @return A collection of all the Stmts that are targets of a BranchingStmt + */ + @Nonnull + public Collection getLabeledStmts(StmtGraph stmtGraph) { + Set stmtList = new HashSet<>(); + Collection stmtGraphNodes = stmtGraph.getNodes(); + for (Stmt stmt : stmtGraphNodes) { + if (stmt instanceof BranchingStmt) { + if (stmt instanceof JIfStmt) { + stmtList.add((Stmt) stmtGraph.getBranchTargetsOf((JIfStmt) stmt).get(JIfStmt.FALSE_BRANCH_IDX)); + } else if (stmt instanceof JGotoStmt) { + // [ms] bounds are validated in Body if its a valid StmtGraph + stmtList.add((Stmt) stmtGraph.getBranchTargetsOf((JGotoStmt) stmt).get(JGotoStmt.BRANCH_IDX)); + } else if (stmt instanceof JSwitchStmt) { + stmtList.addAll(stmtGraph.getBranchTargetsOf((BranchingStmt) stmt)); + } + } + } + + for (Trap trap : buildTraps(stmtGraph)) { + stmtList.add(trap.getBeginStmt()); + stmtList.add(trap.getEndStmt()); + stmtList.add(trap.getHandlerStmt()); + } + + return stmtList; + } } diff --git a/sootup.core/src/test/java/sootup/core/graph/MutableBlockStmtGraphTest.java b/sootup.core/src/test/java/sootup/core/graph/MutableBlockStmtGraphTest.java index 2b88a74afd..92faac8bba 100644 --- a/sootup.core/src/test/java/sootup/core/graph/MutableBlockStmtGraphTest.java +++ b/sootup.core/src/test/java/sootup/core/graph/MutableBlockStmtGraphTest.java @@ -19,12 +19,14 @@ import sootup.core.types.ClassType; import sootup.core.types.PrimitiveType; import sootup.core.types.UnknownType; +import sootup.core.util.printer.BriefStmtPrinter; import sootup.core.util.printer.JimplePrinter; @Tag("Java8") public class MutableBlockStmtGraphTest { public JimplePrinter jimplePrinter = new JimplePrinter(); + public final BriefStmtPrinter briefStmtPrinter = new BriefStmtPrinter(); BranchingStmt firstGoto = new JGotoStmt(StmtPositionInfo.getNoStmtPositionInfo()); JNopStmt firstNop = new JNopStmt(StmtPositionInfo.getNoStmtPositionInfo()); @@ -624,7 +626,7 @@ public PackageName getPackageName() { graph0.putEdge(stmt2, 0, returnStmt); { - final List traps = jimplePrinter.buildTraps(graph0); + final List traps = briefStmtPrinter.buildTraps(graph0); assertEquals(2, traps.size()); // as @caughtexception gets currently in their way. assertEquals(stmt2, traps.get(1).getBeginStmt()); assertEquals(returnStmt, traps.get(1).getEndStmt()); @@ -662,7 +664,7 @@ public PackageName getPackageName() { graph2.putEdge(stmt2, JGotoStmt.BRANCH_IDX, returnStmt); { assertEquals(5, graph2.getBlocks().size()); - final List traps = jimplePrinter.buildTraps(graph2); + final List traps = briefStmtPrinter.buildTraps(graph2); assertEquals(2, traps.size()); } @@ -678,7 +680,7 @@ public PackageName getPackageName() { graph3.putEdge(stmt3, JGotoStmt.BRANCH_IDX, returnStmt); { - final List traps = jimplePrinter.buildTraps(graph3); + final List traps = briefStmtPrinter.buildTraps(graph3); assertEquals(5, graph2.getBlocks().size()); assertEquals(2, traps.size()); } @@ -701,7 +703,7 @@ public PackageName getPackageName() { graph4.putEdge(stmt2, JGotoStmt.BRANCH_IDX, stmt3); graph4.putEdge(stmt3, JGotoStmt.BRANCH_IDX, returnStmt); - assertEquals(3, jimplePrinter.buildTraps(graph4).size()); + assertEquals(3, briefStmtPrinter.buildTraps(graph4).size()); // mixed 2 MutableBlockStmtGraph graph5 = new MutableBlockStmtGraph(); @@ -736,7 +738,7 @@ public PackageName getPackageName() { graph5.putEdge(stmt3, JGotoStmt.BRANCH_IDX, returnStmt); { - final List traps = jimplePrinter.buildTraps(graph5); + final List traps = briefStmtPrinter.buildTraps(graph5); assertEquals(6, traps.size()); assertEquals(6, graph5.getBlocks().size()); } @@ -772,7 +774,7 @@ public PackageName getPackageName() { graph6.putEdge(stmt2, JGotoStmt.BRANCH_IDX, stmt3); graph6.putEdge(stmt3, JGotoStmt.BRANCH_IDX, returnStmt); { - final List traps = jimplePrinter.buildTraps(graph6); + final List traps = briefStmtPrinter.buildTraps(graph6); assertEquals(5, traps.size()); assertEquals(6, graph6.getBlocks().size()); assertEquals( @@ -1056,7 +1058,7 @@ public void testRemoveSingleTrap() { graph.addExceptionalEdge(stmt1, throwableSig, handlerStmt); // Verify the trap is present - List traps = jimplePrinter.buildTraps(graph); + List traps = briefStmtPrinter.buildTraps(graph); assertEquals(1, traps.size()); assertEquals(stmt1, traps.get(0).getBeginStmt()); assertEquals(handlerStmt, traps.get(0).getHandlerStmt()); @@ -1065,7 +1067,7 @@ public void testRemoveSingleTrap() { Trap trapToRemove = traps.get(0); graph.removeExceptionalFlowFromAllBlocks( trapToRemove.getExceptionType(), trapToRemove.getHandlerStmt()); - traps = jimplePrinter.buildTraps(graph); + traps = briefStmtPrinter.buildTraps(graph); assertEquals(0, traps.size()); } @@ -1099,7 +1101,7 @@ public void testRemoveMultipleTrapsWithDifferentExceptionTypes() { graph.addExceptionalEdge(stmt2, ioExceptionSig, handlerStmt2); // Verify both traps are present - List traps = jimplePrinter.buildTraps(graph); + List traps = briefStmtPrinter.buildTraps(graph); assertEquals(2, traps.size()); // Remove one trap and verify the remaining @@ -1108,7 +1110,7 @@ public void testRemoveMultipleTrapsWithDifferentExceptionTypes() { graph.removeExceptionalFlowFromAllBlocks( trapToRemove.getExceptionType(), trapToRemove.getHandlerStmt()); - traps = jimplePrinter.buildTraps(graph); + traps = briefStmtPrinter.buildTraps(graph); assertEquals(1, traps.size()); assertEquals(stmt2, trapToKeep.getBeginStmt()); assertEquals(handlerStmt2, trapToKeep.getHandlerStmt()); diff --git a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/TrapTightenerTest.java b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/TrapTightenerTest.java index f6d1bcb1f3..b75ceae543 100644 --- a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/TrapTightenerTest.java +++ b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/TrapTightenerTest.java @@ -23,6 +23,7 @@ import sootup.core.types.ClassType; import sootup.core.types.VoidType; import sootup.core.util.ImmutableUtils; +import sootup.core.util.printer.BriefStmtPrinter; import sootup.core.util.printer.JimplePrinter; import sootup.interceptors.TrapTightener; import sootup.java.core.JavaIdentifierFactory; @@ -35,6 +36,7 @@ @Disabled("FIXME: needs .setTraps() adapted to MutableBlockStmtGraph") public class TrapTightenerTest { public JimplePrinter jimplePrinter = new JimplePrinter(); + public final BriefStmtPrinter briefStmtPrinter = new BriefStmtPrinter(); JavaIdentifierFactory factory = JavaIdentifierFactory.getInstance(); StmtPositionInfo noStmtPositionInfo = StmtPositionInfo.getNoStmtPositionInfo(); @@ -134,7 +136,7 @@ public void testSimpleBody() { List excepted = new ArrayList<>(); excepted.add(trap3); - List actual = jimplePrinter.buildTraps(stmtGraph); + List actual = briefStmtPrinter.buildTraps(stmtGraph); AssertUtils.assertTrapsEquiv(excepted, actual); } /** @@ -175,7 +177,7 @@ public void testMonitoredBody() { List excepted = new ArrayList<>(); excepted.add(trap1); - List actual = jimplePrinter.buildTraps(stmtGraph); + List actual = briefStmtPrinter.buildTraps(stmtGraph); AssertUtils.assertTrapsEquiv(excepted, actual); } diff --git a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/UnreachableCodeEliminatorTest.java b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/UnreachableCodeEliminatorTest.java index 77845f7b46..80a45f4dd2 100644 --- a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/UnreachableCodeEliminatorTest.java +++ b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/interceptors/UnreachableCodeEliminatorTest.java @@ -23,6 +23,7 @@ import sootup.core.types.PrimitiveType; import sootup.core.types.VoidType; import sootup.core.util.ImmutableUtils; +import sootup.core.util.printer.BriefStmtPrinter; import sootup.core.util.printer.JimplePrinter; import sootup.interceptors.UnreachableCodeEliminator; import sootup.java.core.JavaIdentifierFactory; @@ -35,6 +36,7 @@ public class UnreachableCodeEliminatorTest { public final JimplePrinter jimplePrinter = new JimplePrinter(); + public final BriefStmtPrinter briefStmtPrinter = new BriefStmtPrinter(); JavaIdentifierFactory factory = JavaIdentifierFactory.getInstance(); JavaJimple javaJimple = JavaJimple.getInstance(); @@ -142,7 +144,7 @@ public void testTrappedBody1() { new UnreachableCodeEliminator().interceptBody(builder, new JavaView(Collections.emptyList())); - assertEquals(0, jimplePrinter.buildTraps(builder.getStmtGraph()).size()); + assertEquals(0, briefStmtPrinter.buildTraps(builder.getStmtGraph()).size()); Set expectedStmtsSet = ImmutableUtils.immutableSet(startingStmt, stmt1, ret1); AssertUtils.assertSetsEquiv(expectedStmtsSet, builder.getStmtGraph().getNodes()); @@ -178,7 +180,7 @@ public void testTrappedBody2() { UnreachableCodeEliminator eliminator = new UnreachableCodeEliminator(); eliminator.interceptBody(builder, new JavaView(Collections.emptyList())); - assertEquals(0, jimplePrinter.buildTraps(builder.getStmtGraph()).size()); + assertEquals(0, briefStmtPrinter.buildTraps(builder.getStmtGraph()).size()); Set expectedStmtsSet = ImmutableUtils.immutableSet(startingStmt, stmt1, ret1); assertEquals(expectedStmtsSet, builder.getStmtGraph().getNodes()); diff --git a/sootup.tests/src/test/java/sootup/tests/JimpleSerializationTest.java b/sootup.tests/src/test/java/sootup/tests/JimpleSerializationTest.java index 1b131a1622..b8d9e2812d 100644 --- a/sootup.tests/src/test/java/sootup/tests/JimpleSerializationTest.java +++ b/sootup.tests/src/test/java/sootup/tests/JimpleSerializationTest.java @@ -49,6 +49,7 @@ public void testBasicTrapSerialization() { assertTrue(nestedTrap.isPresent()); JavaSootMethod nestedTrapMethod = nestedTrap.get(); System.out.println(nestedTrapMethod.getBody()); + nestedTrapMethod.getBody().getStmtGraph().toString(); } @Test diff --git a/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/TrapSerialization.java b/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/TrapSerialization.java index fde6aef70e..dae76bf937 100644 --- a/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/TrapSerialization.java +++ b/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/TrapSerialization.java @@ -9,16 +9,16 @@ public Integer processWithExplicitCasting(String var2, String var4) throws Excep Object var19; try { try { - var19 = 10; + var19 = 10; // Label1 throw new Exception(); } catch (Exception e) { - var19 = 20; + var19 = 20; // Label2 throw new Exception(e); } finally { - var19 = 30; + var19 = 30; // Label5 } } catch (Exception ex) { - var19 = 40; + var19 = 40; // Label3 } return (Integer) var19; }