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 00000000000..59e21430235 --- /dev/null +++ b/sootup.core/src/main/java/sootup/core/graph/BlockGraphIterator.java @@ -0,0 +1,209 @@ +package sootup.core.graph; + +import java.util.*; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import sootup.core.jimple.common.stmt.JGotoStmt; +import sootup.core.jimple.common.stmt.Stmt; +import sootup.core.types.ClassType; +import sootup.core.util.DotExporter; + +/*- + * #%L + * Soot + * %% + * Copyright (C) 2024 Sahil Agichani + * %% + * 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% + */ + +/** 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/ForwardingStmtGraph.java b/sootup.core/src/main/java/sootup/core/graph/ForwardingStmtGraph.java index 80c4393323e..32e161e4757 100644 --- a/sootup.core/src/main/java/sootup/core/graph/ForwardingStmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/ForwardingStmtGraph.java @@ -23,7 +23,6 @@ */ import java.util.*; import javax.annotation.Nonnull; -import sootup.core.jimple.basic.Trap; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.types.ClassType; @@ -128,12 +127,6 @@ public Iterator iterator() { return backingGraph.iterator(); } - @Nonnull - @Override - public List buildTraps() { - return backingGraph.buildTraps(); - } - @Override public void removeExceptionalFlowFromAllBlocks( @Nonnull ClassType exceptionType, @Nonnull Stmt exceptionHandlerStmt) { 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..03ed3d469a1 100644 --- a/sootup.core/src/main/java/sootup/core/graph/ImmutableBlockStmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/ImmutableBlockStmtGraph.java @@ -26,7 +26,6 @@ import java.util.*; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import sootup.core.jimple.basic.Trap; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.types.ClassType; @@ -137,12 +136,6 @@ public boolean hasEdgeConnecting(@Nonnull Stmt source, @Nonnull Stmt target) { throw new UnsupportedOperationException("Not implemented yet!"); } - @Nonnull - @Override - public List buildTraps() { - throw new UnsupportedOperationException("Not implemented yet!"); - } - @Override public void removeExceptionalFlowFromAllBlocks(ClassType classType, Stmt exceptionHandlerStmt) { 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..9cd15096eeb 100644 --- a/sootup.core/src/main/java/sootup/core/graph/MutableBlockStmtGraph.java +++ b/sootup.core/src/main/java/sootup/core/graph/MutableBlockStmtGraph.java @@ -22,7 +22,6 @@ * #L% */ -import com.google.common.collect.ComparisonChain; import com.google.common.collect.Lists; import java.util.*; import javax.annotation.Nonnull; @@ -1596,39 +1595,6 @@ public boolean hasEdgeConnecting(@Nonnull Stmt source, @Nonnull Stmt target) { } } - /** 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(); - } - - /** hint: little expensive getter - its more of a build/create - currently no overlaps */ - @Override - public List buildTraps() { - // [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(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; - } - @Override public void removeExceptionalFlowFromAllBlocks( @Nonnull ClassType exceptionType, @Nonnull Stmt exceptionHandlerStmt) { 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..45833cf8ec9 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; /** @@ -89,7 +90,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); @@ -148,15 +149,6 @@ public int degree(@Nonnull Stmt node) { */ public abstract boolean hasEdgeConnecting(@Nonnull Stmt source, @Nonnull Stmt target); - /** - * 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() - */ - public abstract List buildTraps(); - /** * Removes the specified exceptional flow from all blocks. * @@ -206,16 +198,25 @@ public Collection getEntrypoints() { /** validates whether the each Stmt has the correct amount of outgoing flows. */ public void validateStmtConnectionsInGraph() { try { + List handlerStmts = new ArrayList<>(); + for (Stmt stmt : getNodes()) { + if (stmt instanceof JIdentityStmt) { + // JThrowStmt? + IdentityRef rightOp = ((JIdentityStmt) stmt).getRightOp(); + if (rightOp instanceof JCaughtExceptionRef) { + handlerStmts.add(stmt); + } + } + } for (Stmt stmt : getNodes()) { final List successors = successors(stmt); final int successorCount = successors.size(); if (predecessors(stmt).isEmpty()) { + if (!(stmt == getStartingStmt() - || buildTraps().stream() - .map(Trap::getHandlerStmt) - .anyMatch(handler -> handler == stmt))) { + || handlerStmts.stream().anyMatch(handler -> handler == stmt))) { throw new IllegalStateException( "Stmt '" + stmt @@ -353,7 +354,9 @@ public boolean equals(Object o) { return false; } - if (!buildTraps().equals(otherGraph.buildTraps())) { + List currTraps = new BriefStmtPrinter(this).getTraps(); + List otherGraphTraps = new BriefStmtPrinter(otherGraph).getTraps(); + if (!currTraps.equals(otherGraphTraps)) { return false; } @@ -415,7 +418,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) { @@ -441,291 +444,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 */ - protected 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 : buildTraps()) { - 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 720e9d82ee2..d7cbcc0dcaf 100644 --- a/sootup.core/src/main/java/sootup/core/model/Body.java +++ b/sootup.core/src/main/java/sootup/core/model/Body.java @@ -111,17 +111,6 @@ public Set getLocals() { return locals; } - /** - * Returns an unmodifiable view of the traps found in this Body. @Deprecated the exceptional flow - * information is already integrated into the StmtGraphs BasicBlocks.getExceptionalFlows() - - * exists to make porting tools from Soot easier - */ - @Nonnull - @Deprecated() - public List getTraps() { - return graph.buildTraps(); - } - /** Return unit containing the \@this-assignment * */ @Nullable public Stmt getThisStmt() { 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 00000000000..4ce08c8695c --- /dev/null +++ b/sootup.core/src/main/java/sootup/core/util/printer/BlockGraphIteratorAndTrapAggregator.java @@ -0,0 +1,130 @@ +package sootup.core.util.printer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +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; + +/*- + * #%L + * Soot + * %% + * Copyright (C) 2024 Sahil Agichani + * %% + * 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% + */ + +/** + * 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<>(); + + @Nonnull protected final Map activeTraps = new HashMap<>(); + @Nonnull protected BasicBlock lastIteratedBlock; + @Nullable protected 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()); + 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/BriefStmtPrinter.java b/sootup.core/src/main/java/sootup/core/util/printer/BriefStmtPrinter.java index 12f7f250366..ce1f0d19367 100644 --- a/sootup.core/src/main/java/sootup/core/util/printer/BriefStmtPrinter.java +++ b/sootup.core/src/main/java/sootup/core/util/printer/BriefStmtPrinter.java @@ -22,6 +22,7 @@ * #L% */ +import sootup.core.graph.StmtGraph; import sootup.core.jimple.Jimple; import sootup.core.jimple.common.ref.IdentityRef; import sootup.core.jimple.common.ref.JCaughtExceptionRef; @@ -37,6 +38,10 @@ public BriefStmtPrinter() { super(); } + public BriefStmtPrinter(StmtGraph stmtGraph) { + buildTraps(stmtGraph); + } + @Override public void method(SootMethod m) { handleIndent(); 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 daf83dc9794..c02555c048c 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 @@ -26,7 +26,7 @@ import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; -import sootup.core.graph.StmtGraph; +import sootup.core.graph.*; import sootup.core.jimple.Jimple; import sootup.core.jimple.basic.Local; import sootup.core.jimple.basic.Trap; @@ -354,10 +354,10 @@ private void printStmts(StmtGraph stmtGraph, LabeledStmtPrinter printer) { // a trap) final boolean currentStmtHasLabel = labels.get(currentStmt) != null; - if (previousStmt.branches() + if (currentStmtHasLabel + || previousStmt.branches() || stmtGraph.predecessors(currentStmt).size() != 1 - || previousStmt.getExpectedSuccessorCount() == 0 - || currentStmtHasLabel) { + || previousStmt.getExpectedSuccessorCount() == 0) { printer.newline(); } @@ -380,7 +380,7 @@ private void printStmts(StmtGraph stmtGraph, LabeledStmtPrinter printer) { // Print out exceptions { - Iterator trapIt = stmtGraph.buildTraps().iterator(); + Iterator trapIt = printer.getTraps().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 01f4eb5eb75..dfaf73543ff 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 @@ -22,13 +22,15 @@ * #L% */ +import com.google.common.collect.ComparisonChain; import java.util.*; import javax.annotation.Nonnull; -import sootup.core.graph.StmtGraph; +import sootup.core.graph.*; 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; @@ -45,6 +47,8 @@ public abstract class LabeledStmtPrinter extends AbstractStmtPrinter { */ protected Map references; + private List traps; + public LabeledStmtPrinter() {} public Map getLabels() { @@ -107,16 +111,10 @@ public void stmtRef(Stmt stmt, boolean branchTarget) { */ public Iterable initializeSootMethod(@Nonnull StmtGraph stmtGraph) { this.graph = stmtGraph; - final List linearizedStmtGraph = getStmts(stmtGraph); - return linearizedStmtGraph; - } - - @Nonnull - public List getStmts(@Nonnull StmtGraph stmtGraph) { - final Collection targetStmtsOfBranches = stmtGraph.getLabeledStmts(); - final List traps = stmtGraph.buildTraps(); + JNopStmt needsNopAtEnd = buildTraps(stmtGraph); + final Collection labeledStmts = getLabeledStmts(stmtGraph, this.traps); - final int maxEstimatedSize = targetStmtsOfBranches.size() + traps.size() * 3; + final int maxEstimatedSize = labeledStmts.size() + traps.size() * 3; labels = new HashMap<>(maxEstimatedSize, 1); references = new HashMap<>(maxEstimatedSize, 1); @@ -135,8 +133,8 @@ public List getStmts(@Nonnull StmtGraph stmtGraph) { // Build labelStmts and refStmts -> is stmt head of a block (as its a branch target/trapHandler // 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)) { + for (Stmt stmt : labeledStmts) { + if (trapStmts.contains(stmt) || stmtGraph.isStmtBranchTarget(stmt)) { labelStmts.add(stmt); } else { refStmts.add(stmt); @@ -163,6 +161,12 @@ public List getStmts(@Nonnull StmtGraph stmtGraph) { references.put(s, Integer.toString(refCount++)); } } + + if (needsNopAtEnd != null) { + linearizedStmtGraph.add(needsNopAtEnd); + labels.put(needsNopAtEnd, String.format(formatString, ++labelCount)); + } + return linearizedStmtGraph; } @@ -195,4 +199,89 @@ 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 JNopStmt 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(); + boolean b = it.getLastStmt() != null; + if (b) { + stmtsBlockIdx.put(it.getLastStmt(), i); + } + traps.sort(getTrapComparator(stmtsBlockIdx)); + this.traps = traps; + return it.getLastStmt(); + } + + /** 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, List traps) { + 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 : traps) { + stmtList.add(trap.getBeginStmt()); + stmtList.add(trap.getEndStmt()); + stmtList.add(trap.getHandlerStmt()); + } + + return stmtList; + } + + public List getTraps() { + return traps; + } } diff --git a/sootup.core/src/main/java/sootup/core/validation/JimpleTrapValidator.java b/sootup.core/src/main/java/sootup/core/validation/JimpleTrapValidator.java index 3b211381dd2..7f4301dd250 100644 --- a/sootup.core/src/main/java/sootup/core/validation/JimpleTrapValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/JimpleTrapValidator.java @@ -31,6 +31,7 @@ import sootup.core.jimple.common.stmt.JIdentityStmt; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; +import sootup.core.util.printer.BriefStmtPrinter; import sootup.core.views.View; /** @@ -53,7 +54,10 @@ public List validate(Body body, View view) { List exceptions = new ArrayList<>(); Set caughtStmts = new HashSet(); - for (Trap trap : body.getTraps()) { + BriefStmtPrinter stmtPrinter = new BriefStmtPrinter(); + stmtPrinter.buildTraps(body.getStmtGraph()); + Iterable traps = stmtPrinter.getTraps(); + for (Trap trap : traps) { caughtStmts.add(trap.getHandlerStmt()); if (!(trap.getHandlerStmt() instanceof JIdentityStmt)) { exceptions.add( diff --git a/sootup.core/src/main/java/sootup/core/validation/TrapsValidator.java b/sootup.core/src/main/java/sootup/core/validation/TrapsValidator.java index e9019e475b0..cec0b87428a 100644 --- a/sootup.core/src/main/java/sootup/core/validation/TrapsValidator.java +++ b/sootup.core/src/main/java/sootup/core/validation/TrapsValidator.java @@ -27,6 +27,7 @@ import sootup.core.jimple.basic.Trap; import sootup.core.jimple.common.stmt.Stmt; import sootup.core.model.Body; +import sootup.core.util.printer.BriefStmtPrinter; import sootup.core.views.View; public class TrapsValidator implements BodyValidator { @@ -39,9 +40,12 @@ public class TrapsValidator implements BodyValidator { @Override public List validate(Body body, View view) { List exceptions = new ArrayList<>(); + BriefStmtPrinter stmtPrinter = new BriefStmtPrinter(); + stmtPrinter.buildTraps(body.getStmtGraph()); + Iterable traps = stmtPrinter.getTraps(); List stmts = body.getStmts(); - for (Trap t : body.getTraps()) { + for (Trap t : traps) { if (!stmts.contains(t.getBeginStmt())) exceptions.add( new ValidationException( 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 e64299713f9..6143763a4d4 100644 --- a/sootup.core/src/test/java/sootup/core/graph/MutableBlockStmtGraphTest.java +++ b/sootup.core/src/test/java/sootup/core/graph/MutableBlockStmtGraphTest.java @@ -19,10 +19,13 @@ import sootup.core.types.ClassType; import sootup.core.types.PrimitiveType; import sootup.core.types.UnknownType; +import sootup.core.util.printer.BriefStmtPrinter; @Tag("Java8") public class MutableBlockStmtGraphTest { + public final BriefStmtPrinter briefStmtPrinter = new BriefStmtPrinter(); + BranchingStmt firstGoto = new JGotoStmt(StmtPositionInfo.getNoStmtPositionInfo()); JNopStmt firstNop = new JNopStmt(StmtPositionInfo.getNoStmtPositionInfo()); JNopStmt secondNop = new JNopStmt(StmtPositionInfo.getNoStmtPositionInfo()); @@ -621,7 +624,8 @@ public PackageName getPackageName() { graph0.putEdge(stmt2, 0, returnStmt); { - final List traps = graph0.buildTraps(); + briefStmtPrinter.buildTraps(graph0); + List traps = briefStmtPrinter.getTraps(); assertEquals(2, traps.size()); // as @caughtexception gets currently in their way. assertEquals(stmt2, traps.get(1).getBeginStmt()); assertEquals(returnStmt, traps.get(1).getEndStmt()); @@ -659,7 +663,8 @@ public PackageName getPackageName() { graph2.putEdge(stmt2, JGotoStmt.BRANCH_IDX, returnStmt); { assertEquals(5, graph2.getBlocks().size()); - final List traps = graph2.buildTraps(); + briefStmtPrinter.buildTraps(graph2); + List traps = briefStmtPrinter.getTraps(); assertEquals(2, traps.size()); } @@ -675,7 +680,8 @@ public PackageName getPackageName() { graph3.putEdge(stmt3, JGotoStmt.BRANCH_IDX, returnStmt); { - final List traps = graph3.buildTraps(); + briefStmtPrinter.buildTraps(graph3); + List traps = briefStmtPrinter.getTraps(); assertEquals(5, graph2.getBlocks().size()); assertEquals(2, traps.size()); } @@ -698,7 +704,8 @@ public PackageName getPackageName() { graph4.putEdge(stmt2, JGotoStmt.BRANCH_IDX, stmt3); graph4.putEdge(stmt3, JGotoStmt.BRANCH_IDX, returnStmt); - assertEquals(3, graph4.buildTraps().size()); + briefStmtPrinter.buildTraps(graph4); + assertEquals(3, briefStmtPrinter.getTraps().size()); // mixed 2 MutableBlockStmtGraph graph5 = new MutableBlockStmtGraph(); @@ -733,8 +740,8 @@ public PackageName getPackageName() { graph5.putEdge(stmt3, JGotoStmt.BRANCH_IDX, returnStmt); { - final List traps = graph5.buildTraps(); - assertEquals(6, traps.size()); + briefStmtPrinter.buildTraps(graph5); + assertEquals(6, briefStmtPrinter.getTraps().size()); assertEquals(6, graph5.getBlocks().size()); } @@ -769,7 +776,8 @@ public PackageName getPackageName() { graph6.putEdge(stmt2, JGotoStmt.BRANCH_IDX, stmt3); graph6.putEdge(stmt3, JGotoStmt.BRANCH_IDX, returnStmt); { - final List traps = graph6.buildTraps(); + briefStmtPrinter.buildTraps(graph6); + List traps = briefStmtPrinter.getTraps(); assertEquals(5, traps.size()); assertEquals(6, graph6.getBlocks().size()); assertEquals( @@ -1030,6 +1038,7 @@ public void simpleInsertion() { assertTrue(graph.successors(stmt1).contains(stmt2)); } + // It is an invalid graph, just for the test @Test public void testRemoveSingleTrap() { MutableBlockStmtGraph graph = new MutableBlockStmtGraph(); @@ -1052,7 +1061,8 @@ public void testRemoveSingleTrap() { graph.addExceptionalEdge(stmt1, throwableSig, handlerStmt); // Verify the trap is present - List traps = graph.buildTraps(); + briefStmtPrinter.buildTraps(graph); + List traps = briefStmtPrinter.getTraps(); assertEquals(1, traps.size()); assertEquals(stmt1, traps.get(0).getBeginStmt()); assertEquals(handlerStmt, traps.get(0).getHandlerStmt()); @@ -1061,7 +1071,8 @@ public void testRemoveSingleTrap() { Trap trapToRemove = traps.get(0); graph.removeExceptionalFlowFromAllBlocks( trapToRemove.getExceptionType(), trapToRemove.getHandlerStmt()); - traps = graph.buildTraps(); + briefStmtPrinter.buildTraps(graph); + traps = briefStmtPrinter.getTraps(); assertEquals(0, traps.size()); } @@ -1095,7 +1106,8 @@ public void testRemoveMultipleTrapsWithDifferentExceptionTypes() { graph.addExceptionalEdge(stmt2, ioExceptionSig, handlerStmt2); // Verify both traps are present - List traps = graph.buildTraps(); + briefStmtPrinter.buildTraps(graph); + List traps = briefStmtPrinter.getTraps(); assertEquals(2, traps.size()); // Remove one trap and verify the remaining @@ -1104,7 +1116,8 @@ public void testRemoveMultipleTrapsWithDifferentExceptionTypes() { graph.removeExceptionalFlowFromAllBlocks( trapToRemove.getExceptionType(), trapToRemove.getHandlerStmt()); - traps = graph.buildTraps(); + briefStmtPrinter.buildTraps(graph); + traps = briefStmtPrinter.getTraps(); 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/TryWithResourcesFinallyTests.java b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/TryWithResourcesFinallyTests.java index b78111e421d..3153479079c 100644 --- a/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/TryWithResourcesFinallyTests.java +++ b/sootup.java.bytecode.frontend/src/test/java/sootup/java/bytecode/frontend/TryWithResourcesFinallyTests.java @@ -4,13 +4,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; -import java.util.List; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import sootup.core.inputlocation.AnalysisInputLocation; -import sootup.core.jimple.basic.Trap; import sootup.core.model.SourceType; import sootup.core.signatures.MethodSignature; +import sootup.core.util.printer.BriefStmtPrinter; import sootup.java.bytecode.frontend.inputlocation.PathBasedAnalysisInputLocation; import sootup.java.core.views.JavaView; @@ -29,6 +28,8 @@ public void test() { MethodSignature methodSignature = view.getIdentifierFactory() .parseMethodSignature(""); - List traps = view.getMethod(methodSignature).get().getBody().getTraps(); + BriefStmtPrinter stmtPrinter = new BriefStmtPrinter(); + stmtPrinter.buildTraps(view.getMethod(methodSignature).get().getBody().getStmtGraph()); + stmtPrinter.getTraps(); } } 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 9097545d073..59e9b2fa051 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.interceptors.TrapTightener; import sootup.java.core.JavaIdentifierFactory; import sootup.java.core.language.JavaJimple; @@ -33,6 +34,8 @@ @Tag(TestCategories.JAVA_8_CATEGORY) @Disabled("FIXME: needs .setTraps() adapted to MutableBlockStmtGraph") public class TrapTightenerTest { + public final BriefStmtPrinter briefStmtPrinter = new BriefStmtPrinter(); + JavaIdentifierFactory factory = JavaIdentifierFactory.getInstance(); StmtPositionInfo noStmtPositionInfo = StmtPositionInfo.getNoStmtPositionInfo(); @@ -131,7 +134,8 @@ public void testSimpleBody() { List excepted = new ArrayList<>(); excepted.add(trap3); - List actual = stmtGraph.buildTraps(); + briefStmtPrinter.buildTraps(stmtGraph); + List actual = briefStmtPrinter.getTraps(); AssertUtils.assertTrapsEquiv(excepted, actual); } /** @@ -172,7 +176,8 @@ public void testMonitoredBody() { List excepted = new ArrayList<>(); excepted.add(trap1); - List actual = stmtGraph.buildTraps(); + briefStmtPrinter.buildTraps(stmtGraph); + List actual = briefStmtPrinter.getTraps(); 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 cc114b2fb14..a3109f6fc15 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 @@ -13,16 +13,14 @@ 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.signatures.MethodSignature; import sootup.core.types.ClassType; import sootup.core.types.PrimitiveType; import sootup.core.types.VoidType; import sootup.core.util.ImmutableUtils; +import sootup.core.util.printer.BriefStmtPrinter; import sootup.interceptors.UnreachableCodeEliminator; import sootup.java.core.JavaIdentifierFactory; import sootup.java.core.language.JavaJimple; @@ -33,6 +31,8 @@ @Tag(TestCategories.JAVA_8_CATEGORY) public class UnreachableCodeEliminatorTest { + public final BriefStmtPrinter briefStmtPrinter = new BriefStmtPrinter(); + JavaIdentifierFactory factory = JavaIdentifierFactory.getInstance(); JavaJimple javaJimple = JavaJimple.getInstance(); StmtPositionInfo noStmtPositionInfo = StmtPositionInfo.getNoStmtPositionInfo(); @@ -139,7 +139,8 @@ public void testTrappedBody1() { new UnreachableCodeEliminator().interceptBody(builder, new JavaView(Collections.emptyList())); - assertEquals(0, builder.getStmtGraph().buildTraps().size()); + briefStmtPrinter.buildTraps(builder.getStmtGraph()); + assertEquals(0, briefStmtPrinter.getTraps().size()); Set expectedStmtsSet = ImmutableUtils.immutableSet(startingStmt, stmt1, ret1); AssertUtils.assertSetsEquiv(expectedStmtsSet, builder.getStmtGraph().getNodes()); @@ -175,7 +176,8 @@ public void testTrappedBody2() { UnreachableCodeEliminator eliminator = new UnreachableCodeEliminator(); eliminator.interceptBody(builder, new JavaView(Collections.emptyList())); - assertEquals(0, builder.getStmtGraph().buildTraps().size()); + briefStmtPrinter.buildTraps(builder.getStmtGraph()); + assertEquals(0, briefStmtPrinter.getTraps().size()); Set expectedStmtsSet = ImmutableUtils.immutableSet(startingStmt, stmt1, ret1); assertEquals(expectedStmtsSet, builder.getStmtGraph().getNodes()); diff --git a/sootup.jimple.frontend/src/test/java/sootup/jimple/frontend/JimpleAnalysisInputLocationTest.java b/sootup.jimple.frontend/src/test/java/sootup/jimple/frontend/JimpleAnalysisInputLocationTest.java index 1f37a6f673f..ccad1d000ee 100644 --- a/sootup.jimple.frontend/src/test/java/sootup/jimple/frontend/JimpleAnalysisInputLocationTest.java +++ b/sootup.jimple.frontend/src/test/java/sootup/jimple/frontend/JimpleAnalysisInputLocationTest.java @@ -120,7 +120,6 @@ public void testIfBodyInterceptorsApplied() { m -> { if (m.getSignature().getName().contains("tc1")) { String s = m.getBody().toString(); - System.out.println(s); } }); }); diff --git a/sootup.jimple.frontend/src/test/java/sootup/jimple/frontend/JimpleConverterTest.java b/sootup.jimple.frontend/src/test/java/sootup/jimple/frontend/JimpleConverterTest.java index 2c10cad184e..88fc0ba6bf9 100644 --- a/sootup.jimple.frontend/src/test/java/sootup/jimple/frontend/JimpleConverterTest.java +++ b/sootup.jimple.frontend/src/test/java/sootup/jimple/frontend/JimpleConverterTest.java @@ -22,6 +22,7 @@ import sootup.core.types.PrimitiveType; import sootup.core.types.VoidType; import sootup.core.util.StringTools; +import sootup.core.util.printer.BriefStmtPrinter; import sootup.jimple.JimpleLexer; import sootup.jimple.JimpleParser; @@ -830,7 +831,10 @@ public void testRedundantTrapHandler() throws IOException { CharStreams.fromFileName("src/test/java/resources/jimple/RedundantTrapHandler.jimple")); Set methods = clazz.getMethods(); SootMethod method = methods.iterator().next(); - List traps = method.getBody().getTraps(); + + BriefStmtPrinter stmtPrinter = new BriefStmtPrinter(); + stmtPrinter.buildTraps(method.getBody().getStmtGraph()); + List traps = stmtPrinter.getTraps(); assertEquals(0, traps.size()); } } diff --git a/sootup.tests/pom.xml b/sootup.tests/pom.xml index d2c9e05b0bd..ff609544283 100644 --- a/sootup.tests/pom.xml +++ b/sootup.tests/pom.xml @@ -44,7 +44,6 @@ slf4j-simple test - diff --git a/sootup.tests/src/test/java/sootup/tests/JimpleSerializationTest.java b/sootup.tests/src/test/java/sootup/tests/JimpleSerializationTest.java new file mode 100644 index 00000000000..0fdc463796e --- /dev/null +++ b/sootup.tests/src/test/java/sootup/tests/JimpleSerializationTest.java @@ -0,0 +1,55 @@ +package sootup.tests; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.*; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import sootup.core.inputlocation.AnalysisInputLocation; +import sootup.core.model.SourceType; +import sootup.java.bytecode.frontend.inputlocation.JavaClassPathAnalysisInputLocation; +import sootup.java.core.JavaSootMethod; +import sootup.java.core.views.JavaView; + +@Tag("Java8") +public class JimpleSerializationTest { + + @Test + public void testTrapSerialization() { + AnalysisInputLocation inputLocation = + new JavaClassPathAnalysisInputLocation( + "src/test/resources/bugs/1119_trap-serialization", + SourceType.Application, + Collections.emptyList()); + JavaView view = new JavaView(inputLocation); + + Optional methodOpt = + view.getMethod( + view.getIdentifierFactory() + .parseMethodSignature( + "")); + assertTrue(methodOpt.isPresent()); + JavaSootMethod method = methodOpt.get(); + method.getBody().toString(); + } + + @Test + public void testBasicTrapSerialization() { + AnalysisInputLocation inputLocation = + new JavaClassPathAnalysisInputLocation( + "src/test/resources/bugs/1119_trap-serialization", + SourceType.Application, + Collections.emptyList()); + JavaView javaView = new JavaView(inputLocation); + Optional nestedTrap = + javaView.getMethod( + javaView + .getIdentifierFactory() + .parseMethodSignature( + "")); + + assertTrue(nestedTrap.isPresent()); + JavaSootMethod nestedTrapMethod = nestedTrap.get(); + nestedTrapMethod.getBody().getStmtGraph().toString(); + } +} diff --git a/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/GitRepository.class b/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/GitRepository.class new file mode 100644 index 00000000000..c148dac3cc2 Binary files /dev/null and b/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/GitRepository.class differ diff --git a/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/TrapSerialization.class b/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/TrapSerialization.class new file mode 100644 index 00000000000..33ff6c5999c Binary files /dev/null and b/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/TrapSerialization.class differ 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 new file mode 100644 index 00000000000..dae76bf9379 --- /dev/null +++ b/sootup.tests/src/test/resources/bugs/1119_trap-serialization/com/linecorp/centraldogma/server/internal/storage/repository/git/TrapSerialization.java @@ -0,0 +1,26 @@ +package com.linecorp.centraldogma.server.internal.storage.repository.git; + +import java.util.*; +import java.io.*; + +public class TrapSerialization { + + public Integer processWithExplicitCasting(String var2, String var4) throws Exception{ + Object var19; + try { + try { + var19 = 10; // Label1 + throw new Exception(); + } catch (Exception e) { + var19 = 20; // Label2 + throw new Exception(e); + } finally { + var19 = 30; // Label5 + } + } catch (Exception ex) { + var19 = 40; // Label3 + } + return (Integer) var19; + } + +} \ No newline at end of file