-
-
Notifications
You must be signed in to change notification settings - Fork 91
Fix TrapTightener #1251
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
base: develop
Are you sure you want to change the base?
Fix TrapTightener #1251
Changes from all commits
5371dce
dd26e89
1bc5a43
03cae0c
80e3ff8
ca4a8b4
4c0b82d
717ee7c
550b4cc
24f8866
2175ff6
969d992
8828b8b
0f0ded3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
public class TrapTightenerExamples { | ||
|
||
void example1() { | ||
int a = 1; | ||
int c = 0; | ||
try{ | ||
int b = a; | ||
c = b/a; | ||
}catch (ArithmeticException e){ | ||
throw e; | ||
} | ||
} | ||
|
||
void example2() { | ||
int a = 1; | ||
int b = 0; | ||
try{ | ||
int c = 2; | ||
int d = b/a; | ||
int e = 0; | ||
e = a/b; | ||
}catch (ArithmeticException e){ | ||
throw e; | ||
} | ||
} | ||
|
||
void example3() { | ||
int a = 1; | ||
int b = 0; | ||
try{ | ||
int c = b/a; | ||
a = b; | ||
}catch (ArithmeticException e){ | ||
throw e; | ||
} | ||
} | ||
|
||
void example4() { | ||
int a = 1; | ||
int b = 0; | ||
try{ | ||
a = b; | ||
}catch (ArithmeticException e){ | ||
throw e; | ||
} | ||
} | ||
|
||
void example5() { | ||
int a = 1; | ||
int b = 0; | ||
int[] arr = {1, 2, 3}; | ||
try{ | ||
int c = 2; | ||
int d = c/b; | ||
arr[3] = d; | ||
int e = 5; | ||
}catch (ArithmeticException e1){ | ||
throw e1; | ||
}catch (NullPointerException e2){ | ||
throw e2; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,7 +4,7 @@ | |
* #%L | ||
* Soot - a J*va Optimization Framework | ||
* %% | ||
* Copyright (C) 1997-2020 John Jorgensen, Zun Wang | ||
* Copyright (C) 1997-2025 John Jorgensen, Zun Wang | ||
* %% | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Lesser General Public License as | ||
|
@@ -24,17 +24,17 @@ | |
|
||
import java.util.*; | ||
import org.jspecify.annotations.NonNull; | ||
import sootup.core.graph.BasicBlock; | ||
import sootup.core.graph.MutableStmtGraph; | ||
import sootup.core.graph.StmtGraph; | ||
import sootup.core.jimple.basic.Trap; | ||
import sootup.core.graph.*; | ||
import sootup.core.jimple.basic.Value; | ||
import sootup.core.jimple.common.stmt.Stmt; | ||
import sootup.core.jimple.javabytecode.stmt.JEnterMonitorStmt; | ||
import sootup.core.jimple.javabytecode.stmt.JExitMonitorStmt; | ||
import sootup.core.model.Body; | ||
import sootup.core.transform.BodyInterceptor; | ||
import sootup.core.typehierarchy.TypeHierarchy; | ||
import sootup.core.types.ClassType; | ||
import sootup.core.views.View; | ||
import sootup.java.core.exceptions.StmtExceptionAnalyzer; | ||
|
||
/** | ||
* @author Zun Wang | ||
|
@@ -43,128 +43,126 @@ | |
* exception caught by the Trap and ends just after the last Unit which might throw an exception | ||
* caught by the Trap. In the case where none of the Units protected by a Trap can throw the | ||
* exception it catches, the Trap's protected area is left completely empty, which will likely | ||
* cause the UnreachableCodeEliminator to remove the Trap(handler?) completely (if the | ||
* traphandler does not cover another range). The TrapTightener is used to reduce the risk of | ||
* unverifiable code which can result from the use of ExceptionalUnitGraphs from which | ||
* unrealizable exceptional control flow edges have been removed. | ||
* cause the UnreachableCodeEliminator to remove the Trap completely. The TrapTightener is used | ||
* to reduce the risk of unverifiable code which can result from the use of | ||
* ExceptionalUnitGraphs from which unrealizable exceptional control flow edges have been | ||
* removed. | ||
*/ | ||
public class TrapTightener implements BodyInterceptor { | ||
|
||
TypeHierarchy hierarchy; | ||
StmtExceptionAnalyzer exceptionAnalyzer; | ||
|
||
@Override | ||
public void interceptBody(Body.@NonNull BodyBuilder builder, @NonNull View view) { | ||
|
||
// FIXME: [ms] ThrowAnalysis is missing and in result mightThrow (...) makes no sense. Issue | ||
// #486 | ||
if (true) { | ||
throw new UnsupportedOperationException("TrapTightener is not yet implemented."); | ||
} | ||
this.hierarchy = view.getTypeHierarchy(); | ||
this.exceptionAnalyzer = new StmtExceptionAnalyzer(hierarchy); | ||
MutableBlockStmtGraph blockGraph = (MutableBlockStmtGraph) builder.getStmtGraph(); | ||
|
||
MutableStmtGraph graph = builder.getStmtGraph(); | ||
List<Stmt> stmtsInPrintOrder = builder.getStmts(); | ||
Set<Stmt> monitoredStmts = monitoredStmts(blockGraph); | ||
|
||
// collect stmts | ||
Set<Stmt> monitoredStmts = monitoredStmts(graph); | ||
Map<Stmt, Collection<ClassType>> toRemove = new HashMap<>(); | ||
for (BasicBlock<?> block : graph.getBlocks()) { | ||
for (Stmt stmt : block.getStmts()) { | ||
|
||
Collection<ClassType> removeForStmt = new ArrayList<>(); | ||
for (Map.Entry<? extends ClassType, ?> exception : | ||
block.getExceptionalSuccessors().entrySet()) { | ||
|
||
// FIXME: check for java9 modules signature, too! | ||
boolean isCatchAll = | ||
exception.getKey().getFullyQualifiedName().equals("java.lang.Throwable"); | ||
|
||
if ( | ||
/* mightThrow(graph, stmt, trap) || */ (isCatchAll && monitoredStmts.contains(stmt))) { | ||
// if it might throw or if trap is a catch-all block and the current stmt has an active | ||
// monitor, we need to keep the block | ||
removeForStmt.add(exception.getKey()); | ||
break; | ||
for (Stmt stmt : builder.getStmts()) { | ||
Map<ClassType, Stmt> exceptionalMap = blockGraph.exceptionalSuccessors(stmt); | ||
for (ClassType exceptionType : exceptionalMap.keySet()) { | ||
if (!isCatchAll(exceptionType) || !monitoredStmts.contains(stmt)) { | ||
if (!canThrowExceptionInGraph(blockGraph, stmt, exceptionType)) { | ||
if (!toRemove.containsKey(stmt)) { | ||
toRemove.put(stmt, new HashSet<>()); | ||
} | ||
toRemove.get(stmt).add(exceptionType); | ||
} | ||
} | ||
if (!removeForStmt.isEmpty()) { | ||
toRemove.put(stmt, removeForStmt); | ||
} | ||
} | ||
} | ||
|
||
// remove exceptions for stmts | ||
Set<MutableBasicBlock> mutatedBlocks = new HashSet<>(); | ||
for (Map.Entry<Stmt, Collection<ClassType>> entry : toRemove.entrySet()) { | ||
Stmt stmt = entry.getKey(); | ||
MutableBasicBlock block = (MutableBasicBlock) blockGraph.getBlockOf(stmt); | ||
blockGraph.splitAndExcludeStmtFromBlock(stmt, block); | ||
for (ClassType classType : entry.getValue()) { | ||
graph.removeExceptionalEdge(entry.getKey(), classType); | ||
MutableBasicBlock blockWithUnthrowableException = | ||
(MutableBasicBlock) blockGraph.getBlockOf(entry.getKey()); | ||
blockWithUnthrowableException.removeExceptionalSuccessorBlock(classType); | ||
mutatedBlocks.add(blockWithUnthrowableException); | ||
Comment on lines
+85
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. seems like functionality that should only be done in mutablestmtgraph |
||
} | ||
} | ||
mutatedBlocks.stream().forEach(block -> blockGraph.tryMergeIntoSurroundingBlocks(block)); | ||
|
||
// FIXME: check if there are traphandlers that have no predecessor | ||
|
||
// delete the unused traps | ||
UnreachableCodeEliminator codeEliminator = new UnreachableCodeEliminator(); | ||
codeEliminator.interceptBody(builder, view); | ||
Comment on lines
+93
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets refactor this into a method / simplify |
||
} | ||
|
||
/** | ||
* Find out all monitored stmts from a given exceptional graph, collect them into a list | ||
* Find out all monitored stmts from a given StmtGraph, collect them into a list | ||
* | ||
* @param graph a given exceptionalStmtGraph | ||
* @return a list of monitored stmts | ||
* @param graph a given StmtGraph | ||
* @return a set of monitored stmts | ||
*/ | ||
private Set<Stmt> monitoredStmts(@NonNull StmtGraph<?> graph) { | ||
Set<Stmt> monitoredStmts = new HashSet<>(); | ||
public Set<Stmt> monitoredStmts(@NonNull StmtGraph<?> graph) { | ||
Map<Stmt, Set<Value>> monitored = new HashMap<>(); | ||
|
||
Deque<Stmt> queue = new ArrayDeque<>(); | ||
queue.add(graph.getStartingStmt()); | ||
Set<Stmt> visitedStmts = new HashSet<>(); | ||
Value exitValue = null; | ||
|
||
while (!queue.isEmpty()) { | ||
Stmt stmt = queue.removeFirst(); | ||
visitedStmts.add(stmt); | ||
// enter a monitored block | ||
if (stmt instanceof JEnterMonitorStmt) { | ||
Deque<Stmt> monitoredQueue = new ArrayDeque<>(); | ||
monitoredQueue.add(stmt); | ||
while (!monitoredQueue.isEmpty()) { | ||
Stmt monitoredStmt = monitoredQueue.removeFirst(); | ||
monitoredStmts.add(monitoredStmt); | ||
visitedStmts.add(monitoredStmt); | ||
if (monitoredStmt instanceof JExitMonitorStmt) { | ||
queue.addAll(graph.getAllSuccessors(monitoredStmt)); | ||
} else { | ||
for (Stmt succ : graph.getAllSuccessors(monitoredStmt)) { | ||
if (!visitedStmts.contains(succ)) { | ||
monitoredQueue.add(succ); | ||
boolean hasChanged = false; | ||
Stmt currStmt = queue.removeFirst(); | ||
if (currStmt instanceof JEnterMonitorStmt) { | ||
Value monitoredValue = ((JEnterMonitorStmt) currStmt).getOp(); | ||
if (!monitored.containsKey(currStmt)) { | ||
monitored.put(currStmt, new HashSet<>()); | ||
} | ||
hasChanged = monitored.get(currStmt).add(monitoredValue); | ||
Comment on lines
+119
to
+122
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. performance: reuse get() ---> != null |
||
} else if (currStmt instanceof JExitMonitorStmt) { | ||
exitValue = ((JExitMonitorStmt) currStmt).getOp(); | ||
} | ||
for (Stmt pred : graph.predecessors(currStmt)) { | ||
if (monitored.containsKey(pred)) { | ||
for (Value value : monitored.get(pred)) { | ||
Comment on lines
+127
to
+128
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. . |
||
if (value != exitValue) { | ||
if (!monitored.containsKey(currStmt)) { | ||
monitored.put(currStmt, new HashSet<>()); | ||
} | ||
hasChanged = monitored.get(currStmt).add(value); | ||
Comment on lines
+131
to
+133
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. . |
||
} | ||
} | ||
} | ||
} else { | ||
queue.addAll(graph.getAllSuccessors(stmt)); | ||
} | ||
if (visitedStmts.add(currStmt) || hasChanged) { | ||
queue.addAll(graph.getAllSuccessors(currStmt)); | ||
} | ||
} | ||
return monitoredStmts; | ||
return monitored.keySet(); | ||
} | ||
|
||
/** | ||
* Check whether trap-destinations of the given stmt contain the given trap. | ||
* Check whether the given stmt could throw the exception interpreted by a given stmtGraph | ||
* | ||
* @param graph is a exceptional StmtGraph | ||
* @param graph is a StmtGraph | ||
* @param stmt is a stmt in the given graph | ||
* @param trap is a given trap | ||
* @return If trap-destinations of the given stmt contain the given trap, return true, otherwise | ||
* return false | ||
* @return true, if the given stmt can throw the exception stored in the given stmtGraph | ||
*/ | ||
private boolean canThrowExceptionInGraph( | ||
@NonNull StmtGraph<?> graph, @NonNull Stmt stmt, @NonNull ClassType exceptionType) { | ||
Set<ClassType> inferredExceptions = exceptionAnalyzer.mightThrow(stmt, graph).getExceptions(); | ||
boolean isThrowable = | ||
inferredExceptions.stream() | ||
.anyMatch( | ||
inferredException -> | ||
(inferredException.equals(exceptionType) | ||
|| hierarchy.isSubtype(inferredException, exceptionType))); | ||
return isThrowable; | ||
} | ||
|
||
// FIXME: [ms] makes no sense in that Implementation! StmtGraph is not the legacy | ||
// ExceptionalUnitGraph | ||
private boolean mightThrow(@NonNull StmtGraph<?> graph, @NonNull Stmt stmt, @NonNull Trap trap) { | ||
final BasicBlock<?> block = graph.getBlockOf(stmt); | ||
|
||
for (Map.Entry<? extends ClassType, ? extends BasicBlock<?>> dest : | ||
block.getExceptionalSuccessors().entrySet()) { | ||
final ClassType exceptionType = dest.getKey(); | ||
final BasicBlock<?> traphandlerBlock = dest.getValue(); | ||
if (exceptionType.equals(trap.getExceptionType()) | ||
&& traphandlerBlock.getHead().equals(trap.getHandlerStmt())) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
private boolean isCatchAll(ClassType exceptionType) { | ||
return exceptionType.getFullyQualifiedName().equals("java.lang.Throwable") | ||
|| exceptionType.getFullyQualifiedName().equals("java.base/java.lang.Throwable"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is slower than before