Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add failing test case #1125

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 209 additions & 0 deletions sootup.core/src/main/java/sootup/core/graph/BlockGraphIterator.java
Original file line number Diff line number Diff line change
@@ -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
* <http://www.gnu.org/licenses/lgpl-2.1.html>.
* #L%
*/

/** Iterates over the blocks */
public class BlockGraphIterator implements Iterator<BasicBlock<?>> {

private final StmtGraph stmtGraph;
@Nonnull private final ArrayDeque<BasicBlock<?>> trapHandlerBlocks = new ArrayDeque<>();

@Nonnull private final ArrayDeque<BasicBlock<?>> nestedBlocks = new ArrayDeque<>();
@Nonnull private final ArrayDeque<BasicBlock<?>> otherBlocks = new ArrayDeque<>();
@Nonnull private final Set<BasicBlock<?>> iteratedBlocks;

public BlockGraphIterator(StmtGraph stmtGraph) {
this.stmtGraph = stmtGraph;
final Collection<? extends BasicBlock<?>> 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<? extends BasicBlock<?>> 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<? extends ClassType, ? extends BasicBlock<?>> entry :
currentBlock.getExceptionalSuccessors().entrySet()) {
BasicBlock<?> trapHandlerBlock = entry.getValue();
trapHandlerBlocks.addLast(trapHandlerBlock);
nestedBlocks.addFirst(trapHandlerBlock);
}

final List<? extends BasicBlock<?>> 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<? extends BasicBlock<?>> itPreds =
leaderOfFallsthroughBlocks.getPredecessors();

BasicBlock<?> finalLeaderOfFallsthroughBlocks = leaderOfFallsthroughBlocks;
final Optional<? extends BasicBlock<?>> 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 -&gt; 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<? extends BasicBlock<?>> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -128,12 +127,6 @@ public Iterator<Stmt> iterator() {
return backingGraph.iterator();
}

@Nonnull
@Override
public List<Trap> buildTraps() {
return backingGraph.buildTraps();
}

@Override
public void removeExceptionalFlowFromAllBlocks(
@Nonnull ClassType exceptionType, @Nonnull Stmt exceptionHandlerStmt) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -137,12 +136,6 @@ public boolean hasEdgeConnecting(@Nonnull Stmt source, @Nonnull Stmt target) {
throw new UnsupportedOperationException("Not implemented yet!");
}

@Nonnull
@Override
public List<Trap> buildTraps() {
throw new UnsupportedOperationException("Not implemented yet!");
}

@Override
public void removeExceptionalFlowFromAllBlocks(ClassType classType, Stmt exceptionHandlerStmt) {
throw new UnsupportedOperationException("Not implemented yet!");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
* #L%
*/

import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Lists;
import java.util.*;
import javax.annotation.Nonnull;
Expand Down Expand Up @@ -1596,39 +1595,6 @@ public boolean hasEdgeConnecting(@Nonnull Stmt source, @Nonnull Stmt target) {
}
}

/** Comparator which sorts the trap output in getTraps() */
public Comparator<Trap> getTrapComparator(@Nonnull Map<Stmt, Integer> 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<Trap> 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<Stmt, Integer> 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<Trap> traps = it.getTraps();
traps.sort(getTrapComparator(stmtsBlockIdx));
return traps;
}

@Override
public void removeExceptionalFlowFromAllBlocks(
@Nonnull ClassType exceptionType, @Nonnull Stmt exceptionHandlerStmt) {
Expand Down
Loading
Loading