diff --git a/src/main/java/soot/SootMethod.java b/src/main/java/soot/SootMethod.java index d45132e0882..658fb115b62 100644 --- a/src/main/java/soot/SootMethod.java +++ b/src/main/java/soot/SootMethod.java @@ -30,6 +30,7 @@ import java.util.List; import java.util.Objects; import java.util.StringTokenizer; +import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -415,6 +416,21 @@ public synchronized void setActiveBody(Body body) { * get retrieve its active body. Please call {@link SootClass#setApplicationClass()} on the relevant class. */ public Body retrieveActiveBody() { + return retrieveActiveBody((b) -> { }); + } + + /** + * Returns the active body if present, else constructs an active body, calls the consumer and returns the body afterward. + * + * If you called Scene.v().loadClassAndSupport() for a class yourself, it will not be an application class, so you cannot + * get retrieve its active body. Please call {@link SootClass#setApplicationClass()} on the relevant class. + * + * @param consumer Consumer that takes in the body of the method. The consumer is only invoked if the current + * invocation constructs a new body and is guaranteed to terminate before the body is available + * to other threads. + * @return active body of the method + */ + public Body retrieveActiveBody(Consumer
consumer) { // Retrieve the active body so thread changes do not affect the // synchronization between if the body exists and the returned body. // This is a quick check just in case the activeBody exists. @@ -445,6 +461,11 @@ public Body retrieveActiveBody() { // Method sources are not expected to be thread safe activeBody = ms.getBody(this, "jb"); + + // Call the consumer such that clients can update any data structures, caches, etc. + // atomically before the body is available to other threads. + consumer.accept(activeBody); + setActiveBody(activeBody); // If configured, we drop the method source to save memory diff --git a/src/main/java/soot/jimple/toolkits/ide/icfg/AbstractJimpleBasedICFG.java b/src/main/java/soot/jimple/toolkits/ide/icfg/AbstractJimpleBasedICFG.java index 2e13bb535aa..69377aa1160 100644 --- a/src/main/java/soot/jimple/toolkits/ide/icfg/AbstractJimpleBasedICFG.java +++ b/src/main/java/soot/jimple/toolkits/ide/icfg/AbstractJimpleBasedICFG.java @@ -25,18 +25,17 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import heros.DontSynchronize; import heros.SynchronizedBy; import heros.solver.IDESolver; import java.util.Collection; import java.util.Collections; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import soot.Body; import soot.PatchingChain; @@ -53,7 +52,7 @@ public abstract class AbstractJimpleBasedICFG implements BiDiInterproceduralCFG< protected final boolean enableExceptions; - @DontSynchronize("written by single thread; read afterwards") + @SynchronizedBy("thread-safe data structure") private final Map