diff --git a/.gitignore b/.gitignore index 7c68ca731..e3de82ef4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ */bin/* *~ .metadata/ +com.ibm.wala.cast/lib/ com.ibm.wala.cast.java.jdt.test/ com.ibm.wala.cast.java.polyglot/lib/ com.ibm.wala.cast.java.test.data/src/JLex/ diff --git a/com.ibm.wala.cast/build.xml b/com.ibm.wala.cast/build.xml index 027fce457..bf79ce9bd 100644 --- a/com.ibm.wala.cast/build.xml +++ b/com.ibm.wala.cast/build.xml @@ -59,7 +59,7 @@ - + diff --git a/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/ExceptionPruningAnalysis.java b/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/ExceptionPruningAnalysis.java index 290ac49d8..17856b740 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/ExceptionPruningAnalysis.java +++ b/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/ExceptionPruningAnalysis.java @@ -59,9 +59,10 @@ public interface ExceptionPruningAnalysis> { /** * Returns true if the corresponding method contains instructions that may - * throw an exception. Run compute(IPrograssMonitor) first. + * throw an exception which is not caught in the same method. + * Run compute(IPrograssMonitor) first. * @return true if the corresponding method contains instructions that may - * throw an exception. + * throw an exception which is not caught in the same method */ boolean hasExceptions(); diff --git a/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/NullPointerAnalysis.java b/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/NullPointerAnalysis.java index c47d68a85..65eadb79d 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/NullPointerAnalysis.java +++ b/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/NullPointerAnalysis.java @@ -58,7 +58,12 @@ public final class NullPointerAnalysis { public static ExceptionPruningAnalysis createIntraproceduralExplodedCFGAnalysis(TypeReference[] ignoredExceptions, IR ir, ParameterState paramState, MethodState mState) { - return new ExplodedCFGNullPointerAnalysis(ignoredExceptions, ir, paramState, mState); + return new ExplodedCFGNullPointerAnalysis(ignoredExceptions, ir, paramState, mState, false); + } + + public static ExceptionPruningAnalysis + createIntraproceduralExplodedCFGAnalysis(TypeReference[] ignoredExceptions, IR ir, ParameterState paramState, MethodState mState, boolean optHasException) { + return new ExplodedCFGNullPointerAnalysis(ignoredExceptions, ir, paramState, mState, optHasException); } public static ExceptionPruningAnalysis @@ -86,10 +91,16 @@ public final class NullPointerAnalysis { computeInterprocAnalysis(final TypeReference[] ignoredExceptions, final CallGraph cg, final MethodState defaultExceptionMethodState, final IProgressMonitor progress) throws WalaException, UnsoundGraphException, CancelException { + return computeInterprocAnalysis(ignoredExceptions, cg, defaultExceptionMethodState, progress, false); + } + + public static InterprocAnalysisResult + computeInterprocAnalysis(final TypeReference[] ignoredExceptions, final CallGraph cg, + final MethodState defaultExceptionMethodState, final IProgressMonitor progress, boolean optHasExceptions) + throws WalaException, UnsoundGraphException, CancelException { final InterprocNullPointerAnalysis inpa = InterprocNullPointerAnalysis.compute(ignoredExceptions, cg, - defaultExceptionMethodState, progress); + defaultExceptionMethodState, progress, optHasExceptions); return inpa.getResult(); } - } diff --git a/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/inter/InterprocNullPointerAnalysis.java b/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/inter/InterprocNullPointerAnalysis.java index 4182e6050..489e795f5 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/inter/InterprocNullPointerAnalysis.java +++ b/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/inter/InterprocNullPointerAnalysis.java @@ -64,20 +64,22 @@ public final class InterprocNullPointerAnalysis { private final TypeReference[] ignoredExceptions; private final MethodState defaultMethodState; private final Map states; + private final boolean optHasExceptions; public static InterprocNullPointerAnalysis compute(final TypeReference[] ignoredExceptions, final CallGraph cg, - final MethodState defaultMethodState, final IProgressMonitor progress) + final MethodState defaultMethodState, final IProgressMonitor progress, boolean optHasExceptions) throws WalaException, UnsoundGraphException, CancelException { - final InterprocNullPointerAnalysis inpa = new InterprocNullPointerAnalysis(ignoredExceptions, defaultMethodState); + final InterprocNullPointerAnalysis inpa = new InterprocNullPointerAnalysis(ignoredExceptions, defaultMethodState, optHasExceptions); inpa.run(cg, progress); return inpa; } - private InterprocNullPointerAnalysis(final TypeReference[] ignoredExceptions, final MethodState defaultMethodState) { + private InterprocNullPointerAnalysis(final TypeReference[] ignoredExceptions, final MethodState defaultMethodState, boolean optHasExceptions) { this.ignoredExceptions = ignoredExceptions; this.defaultMethodState = defaultMethodState; this.states = new HashMap(); + this.optHasExceptions = optHasExceptions; } private void run(final CallGraph cg, final IProgressMonitor progress) throws WalaException, UnsoundGraphException, CancelException { @@ -147,7 +149,7 @@ public final class InterprocNullPointerAnalysis { // run intraprocedural part again with invoke exception info final ExceptionPruningAnalysis intra2 = - NullPointerAnalysis.createIntraproceduralExplodedCFGAnalysis(ignoredExceptions, ir, paramState, mState); + NullPointerAnalysis.createIntraproceduralExplodedCFGAnalysis(ignoredExceptions, ir, paramState, mState, optHasExceptions); final int deletedEdges2 = intra2.compute(progress); final ControlFlowGraph cfg2 = intra2.getCFG(); final IntraprocAnalysisState singleState1 = states.get(startNode); @@ -174,7 +176,7 @@ public final class InterprocNullPointerAnalysis { states.put(startNode, new IntraprocAnalysisState()); } else { final ExceptionPruningAnalysis intra = - NullPointerAnalysis.createIntraproceduralExplodedCFGAnalysis(ignoredExceptions, ir, paramState, defaultMethodState); + NullPointerAnalysis.createIntraproceduralExplodedCFGAnalysis(ignoredExceptions, ir, paramState, defaultMethodState, optHasExceptions); final int deletedEdges = intra.compute(progress); // Analyze the method with intraprocedural scope final ControlFlowGraph cfg = intra.getCFG(); diff --git a/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/intra/ExplodedCFGNullPointerAnalysis.java b/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/intra/ExplodedCFGNullPointerAnalysis.java index e8c2b976f..963c20695 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/intra/ExplodedCFGNullPointerAnalysis.java +++ b/com.ibm.wala.core/src/com/ibm/wala/cfg/exc/intra/ExplodedCFGNullPointerAnalysis.java @@ -37,12 +37,14 @@ public class ExplodedCFGNullPointerAnalysis implements ExceptionPruningAnalysis< private final IR ir; private final ParameterState initialState; private final MethodState mState; + private final boolean optHasExceptions; - public ExplodedCFGNullPointerAnalysis(TypeReference[] ignoredExceptions, IR ir, ParameterState paramState, MethodState mState) { + public ExplodedCFGNullPointerAnalysis(TypeReference[] ignoredExceptions, IR ir, ParameterState paramState, MethodState mState, boolean optHasExceptions) { this.ignoredExceptions = (ignoredExceptions != null ? ignoredExceptions.clone() : null); this.ir = ir; this.initialState = (paramState == null ? ParameterState.createDefault(ir.getMethod()) : paramState); this.mState = (mState == null ? MethodState.DEFAULT : mState); + this.optHasExceptions = optHasExceptions; } /* @@ -85,7 +87,7 @@ public class ExplodedCFGNullPointerAnalysis implements ExceptionPruningAnalysis< for (IExplodedBasicBlock bb : cfg) { if (bb.getInstruction() == null) continue; List succ = cfg.getExceptionalSuccessors(bb); - if (succ != null && !succ.isEmpty()) { + if (succ != null && !succ.isEmpty() && (!optHasExceptions || succ.contains(cfg.exit()))) { hasException = true; break; } diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationSystem.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationSystem.java index 8ba89ccce..b2c75f57f 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationSystem.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/callgraph/propagation/PropagationSystem.java @@ -634,10 +634,12 @@ public class PropagationSystem extends DefaultFixedPointSolver + + + diff --git a/com.ibm.wala.dalvik/META-INF/MANIFEST.MF b/com.ibm.wala.dalvik/META-INF/MANIFEST.MF index b57ef0242..cf2821ff8 100644 --- a/com.ibm.wala.dalvik/META-INF/MANIFEST.MF +++ b/com.ibm.wala.dalvik/META-INF/MANIFEST.MF @@ -53,5 +53,9 @@ Bundle-ClassPath: dalvik.jar, lib/commons-io-2.4.jar, lib/commons-lang3-3.1.jar, lib/dexlib-1.3.4.jar, - lib/guava-13.0.1.jar + lib/guava-13.0.1.jar, + lib/logback-classic-1.0.9.jar, + lib/logback-core-1.0.9.jar, + lib/slf4j-api-1.7.2.jar + diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/AndroidModel.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/AndroidModel.java index ec0bd45fb..3a2574742 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/AndroidModel.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/AndroidModel.java @@ -345,7 +345,7 @@ public class AndroidModel /* makes SummarizedMethod */ final TypeSafeInstructionFactory tsif = new TypeSafeInstructionFactory(this.cha); final Instantiator instantiator = new Instantiator (this.body, tsif, this.paramManager, this.cha, this.mRef, this.scope); - + boolean enteredASection = false; // // Add preparing code to the model // @@ -410,6 +410,7 @@ public class AndroidModel /* makes SummarizedMethod */ if (this.labelSpecial.hadSectionSwitch(ep.order)) { this.labelSpecial.enter(ep.getSection(), body.getNextProgramCounter()); + enteredASection = true; } // @@ -554,8 +555,9 @@ public class AndroidModel /* makes SummarizedMethod */ // Close all sections by "jumping over" the remaining labels - labelSpecial.finish(body.getNextProgramCounter()); - + if (enteredASection) { + labelSpecial.finish(body.getNextProgramCounter()); + } this.monitor.done(); } @@ -736,10 +738,11 @@ public class AndroidModel /* makes SummarizedMethod */ // SHORTCUT: redirect.addStatement(invokation); - - final int returnPC = redirect.getNextProgramCounter(); - final SSAInstruction returnInstruction = instructionFactory.ReturnInstruction(returnPC, svc); - redirect.addStatement(returnInstruction); + if (instructionFactory.isAssignableFrom(svc.getType(), svc.getValidIn().getReturnType())) { + final int returnPC = redirect.getNextProgramCounter(); + final SSAInstruction returnInstruction = instructionFactory.ReturnInstruction(returnPC, svc); + redirect.addStatement(returnInstruction); + } final IClass declaringClass = this.cha.lookupClass(asMethod.getDeclaringClass()); if (declaringClass == null) { diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/AndroidModelClass.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/AndroidModelClass.java index fcb5748ab..b397c6f0a 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/AndroidModelClass.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/AndroidModelClass.java @@ -1,12 +1,3 @@ -/* - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html. - * - * This file is a derivative of code released under the terms listed below. - * - */ /* * Copyright (c) 2013, * Tobias Blaschke @@ -47,7 +38,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.logging.Logger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.classLoader.FieldImpl; @@ -88,36 +81,30 @@ import com.ibm.wala.util.strings.Atom; * @todo Move this class into an other loader? Currently: Primordial */ public final /* singleton */ class AndroidModelClass extends SyntheticClass { + private static Logger logger = LoggerFactory.getLogger(AndroidModelClass.class); + public static final TypeReference ANDROID_MODEL_CLASS = TypeReference.findOrCreate( ClassLoaderReference.Primordial, TypeName.string2TypeName("Lcom/ibm/wala/AndroidModelClass")); - private static IClassHierarchy cha; - - private static class AndroidModelClassHolder { - private static final AndroidModelClass INSTANCE = new AndroidModelClass(AndroidModelClass.cha); - } + private IClassHierarchy cha; public static AndroidModelClass getInstance(IClassHierarchy cha) { - if (AndroidModelClass.cha == null) { - if (cha == null) { - throw new IllegalArgumentException("Cha may not be null if there had not been an Instance AndroidModelClass before!"); - } else { - AndroidModelClass.cha = cha; - } + IClass android = cha.lookupClass(ANDROID_MODEL_CLASS); + AndroidModelClass mClass; + if (android == null) { + mClass = new AndroidModelClass(cha); + } else if (!(android instanceof AndroidModelClass)) { + throw new IllegalArgumentException(String.format("android model class does not have expected type %s, but %s!", AndroidModelClass.class, android.getClass().toString())); } else { - if (! cha.equals(AndroidModelClass.cha)) { - throw new IllegalArgumentException("Cha differs!"); - } + mClass = (AndroidModelClass) android; } - return AndroidModelClassHolder.INSTANCE; + return mClass; } private AndroidModelClass(IClassHierarchy cha) { super(ANDROID_MODEL_CLASS, cha); this.addMethod(this.clinit()); - - if (AndroidModelClassHolder.INSTANCE != null) { // May be caused when using reflection - throw new IllegalStateException("AndroidModelClass is a singleton and already instantiated!"); - } + this.cha = cha; + this.cha.addClass(this); } /** @@ -193,7 +180,7 @@ public final /* singleton */ class AndroidModelClass extends SyntheticClass { return methods.get(selector); } if (selector.equals(MethodReference.initSelector)) { - + logger.warn("AndroidModelClass is not intended to be initialized"); return null; } throw new IllegalArgumentException("Could not resolve " + selector); diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/FlatInstantiator.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/FlatInstantiator.java index 295275c3e..00ebcccf6 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/FlatInstantiator.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/FlatInstantiator.java @@ -1,12 +1,3 @@ -/* - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html. - * - * This file is a derivative of code released under the terms listed below. - * - */ /* * Copyright (c) 2013, * Tobias Blaschke @@ -46,7 +37,9 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.logging.Logger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.ibm.wala.analysis.typeInference.ConeType; import com.ibm.wala.analysis.typeInference.PrimitiveType; @@ -81,6 +74,7 @@ import com.ibm.wala.util.ssa.TypeSafeInstructionFactory; * @author Tobias Blaschke */ public class FlatInstantiator implements IInstantiator { + private static final Logger logger = LoggerFactory.getLogger(FlatInstantiator.class); final IClassHierarchy cha; final VolatileMethodSummary body; @@ -115,8 +109,8 @@ public class FlatInstantiator implements IInstantiator { private boolean isExcluded(IClass cls) { - if (this.analysisScope.getExclusions().contains(cls.getName().toString())) { // XXX FUUUUU - + if (this.analysisScope.getExclusions() != null && this.analysisScope.getExclusions().contains(cls.getName().toString())) { // XXX FUUUUU + logger.info("Hit exclusions with {}", cls); return true; } else { return false; @@ -145,7 +139,7 @@ public class FlatInstantiator implements IInstantiator { throw new IllegalArgumentException("Can't create an instance of null"); } if (seen == null) { - + logger.debug("Empty seen"); seen = new HashSet(); } @@ -189,9 +183,9 @@ public class FlatInstantiator implements IInstantiator { return instance; } else if (klass == null) { if (! T.getName().toString().startsWith("Landroid/")) { - + logger.error("The Type {} is not in the ClassHierarchy! Returning null as instance", T); } else { - + logger.debug("The Type {} is not in the ClassHierarchy! Returning null as instance", T); } this.body.addConstant(instance.getNumber(), new ConstantValue(null)); instance.setAssigned(); @@ -204,7 +198,7 @@ public class FlatInstantiator implements IInstantiator { final Set types = getTypes(T); - + logger.info("Creating instance of {} is {}", T, types); if (types.isEmpty()) { throw new IllegalStateException("Types of " + T + " are empty"); } @@ -220,7 +214,7 @@ public class FlatInstantiator implements IInstantiator { assert(newInst.getDef() == instance.getNumber()); return instance; } else if (klass.isArrayClass()) { - + logger.info("Creating Array-Class {}", klass.toString()); final TypeReference payloadType = T.getArrayElementType(); SSAValue payload = null; @@ -228,7 +222,7 @@ public class FlatInstantiator implements IInstantiator { for (final SSAValue see : seen) { if (ParameterAccessor.isAssignable(see.getType(), payloadType, this.cha)) { // Happens on Array of interfaces - + logger.trace("Reusing {} for array payload {}", see, payload); payload = see; } } @@ -267,7 +261,7 @@ public class FlatInstantiator implements IInstantiator { return instance; } else { // Abstract, Interface or array - + logger.debug("Not a regular class {}", T); final Set subInstances = new HashSet(); for (final TypeReference type : types) { final IClass subKlass = this.cha.lookupClass(type); @@ -330,7 +324,7 @@ public class FlatInstantiator implements IInstantiator { this.pm.setPhi(instance, phi); } } else { - + logger.warn("No sub-instances for: {} - setting to null", instance); this.body.addConstant(instance.getNumber(), new ConstantValue(null)); instance.setAssigned(); } @@ -399,7 +393,7 @@ public class FlatInstantiator implements IInstantiator { final IMethod cTor = lookupConstructor(val.getType()); final ParameterAccessor ctorAcc = new ParameterAccessor(cTor); assert (ctorAcc.hasImplicitThis()) : "CTor detected as not having implicit this pointer"; - + logger.debug("Acc for: %", this.scope); final ParameterAccessor acc = new ParameterAccessor(this.scope, false); // TODO pm needs a connectThrough too! // TODO false is false // TODO: The overrides may lead to use before definition @@ -412,8 +406,8 @@ public class FlatInstantiator implements IInstantiator { seen.add(nullSelf); seen.addAll(overrides); - - + logger.debug("Recursing for: {}", cTor); + logger.debug("With seen: {}", seen); final List ctorParams = acc.connectThrough(ctorAcc, overrides, /* defaults */ null, this.cha, this, /* managed */ false, /* key */ null, seen, currentDepth + 1); // XXX This starts the recursion! addCallCtor(val, cTor.getReference(), ctorParams); @@ -439,18 +433,19 @@ public class FlatInstantiator implements IInstantiator { * Used internally to avoid endless recursion on getTypes(). */ private Set getTypes(final TypeReference T, final Set seen) { + logger.debug("getTypes({}, {})", T, seen); final Set ret = new HashSet(); ret.add(T); if (T.isPrimitiveType()) { - + logger.warn("getTypes called on a primitive"); return ret; //throw new IllegalArgumentException("Not you that call primitive type on :P"); } final IClass cls = this.cha.lookupClass(T); if (cls == null) { - + logger.error("The type {} is not in the ClassHierarchy - try continuing anyway", T); return ret; //throw new IllegalArgumentException("The type " + T + " is not in the ClassHierarchy"); } else if (isExcluded(cls)) { @@ -464,9 +459,9 @@ public class FlatInstantiator implements IInstantiator { if (impls.isEmpty()) { //throw new IllegalStateException("The interface " + T + " has no known implementors"); if (! T.getName().toString().startsWith("Landroid/")) { - + logger.error("The interface {} has no known implementors - skipping over it", T); } else { - + logger.debug("The interface {} has no known implementors - skipping over it", T); } return ret; // XXX: This is a bad idea? } else { @@ -486,7 +481,7 @@ public class FlatInstantiator implements IInstantiator { } else { for (final IClass sub: subs) { if (seen.contains(sub.getReference())) { - + logger.debug("Seen: {}", sub); continue; } if (sub.isAbstract()) { @@ -571,7 +566,7 @@ public class FlatInstantiator implements IInstantiator { final IMethod method = methods.iterator().next(); assert (method.isInit()); final SSAInstruction firstInstruction = this.cache.getIR(method).iterateAllInstructions().next(); - + logger.debug("First instruction of ctor is: " + firstInstruction); if (firstInstruction instanceof SSAAbstractInvokeInstruction) { final SSAAbstractInvokeInstruction invokation = (SSAAbstractInvokeInstruction) firstInstruction; return invokation.isSpecial(); // Always? @@ -643,12 +638,12 @@ public class FlatInstantiator implements IInstantiator { score = candidScore; } - + logger.debug("CTor {} got score {}", im, candidScore); } if (ctor == null) { - + logger.warn("Still found no CTor for {}", T); return cha.resolveMethod(klass, MethodReference.initSelector); } else { return ctor; diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/Instantiator.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/Instantiator.java index a866cc740..3a2ed49c7 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/Instantiator.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/Instantiator.java @@ -1,12 +1,3 @@ -/* - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html. - * - * This file is a derivative of code released under the terms listed below. - * - */ /* * Copyright (c) 2013, * Tobias Blaschke @@ -46,7 +37,9 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.logging.Logger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.ibm.wala.analysis.typeInference.ConeType; import com.ibm.wala.analysis.typeInference.PrimitiveType; @@ -85,6 +78,7 @@ import com.ibm.wala.util.strings.Atom; * @author Tobias Blaschke */ public class Instantiator implements IInstantiator { + private static final Logger logger = LoggerFactory.getLogger(Instantiator.class); final IClassHierarchy cha; final VolatileMethodSummary body; @@ -104,8 +98,8 @@ public class Instantiator implements IInstantiator { } private boolean isExcluded(IClass cls) { - if (this.analysisScope.getExclusions().contains(cls.getName().toString())) { // XXX FUUUUU - + if (this.analysisScope.getExclusions() != null && this.analysisScope.getExclusions().contains(cls.getName().toString())) { // XXX FUUUUU + logger.info("Hit exclusions with {}", cls); return true; } else { return false; @@ -130,14 +124,14 @@ public class Instantiator implements IInstantiator { throw new IllegalArgumentException("Can't create an instance of null"); } if (seen == null) { - + logger.debug("Empty seen"); seen = new HashSet(); } { // Special type? - final SpecializedInstantiator sInst = new SpecializedInstantiator(body, instructionFactory, pm, - cha, scope, analysisScope, this); - if (sInst.understands(T)) { + if (SpecializedInstantiator.understands(T)) { + final SpecializedInstantiator sInst = new SpecializedInstantiator(body, instructionFactory, pm, + cha, scope, analysisScope, this); return sInst.createInstance(T, asManaged, key, seen); } } @@ -177,10 +171,10 @@ public class Instantiator implements IInstantiator { pm.setAllocation(instance, getInst); return instance; } else { - + logger.info("NEW Component {} \n\tbreadCrumb: {}", instance, pm.breadCrumb); } } else { - + logger.info("NEW Component {} \n\tbreadCrumb: {}", instance, pm.breadCrumb); } } } // */ @@ -190,9 +184,9 @@ public class Instantiator implements IInstantiator { return instance; } else if (klass == null) { if (! T.getName().toString().startsWith("Landroid/")) { - + logger.error("The Type {} is not in the ClassHierarchy! Returning null as instance", T); } else { - + logger.debug("The Type {} is not in the ClassHierarchy! Returning null as instance", T); } this.body.addConstant(instance.getNumber(), new ConstantValue(null)); instance.setAssigned(); @@ -205,7 +199,7 @@ public class Instantiator implements IInstantiator { final Set types = getTypes(T); - + logger.info("Creating instance of {} is {}", T, types); if (types.isEmpty()) { throw new IllegalStateException("Types of " + T + " are empty"); } @@ -221,7 +215,7 @@ public class Instantiator implements IInstantiator { assert(newInst.getDef() == instance.getNumber()); return instance; } else if (klass.isArrayClass()) { - + logger.info("Creating Array-Class {}", klass.toString()); final TypeReference payloadType = T.getArrayElementType(); SSAValue payload = null; @@ -229,7 +223,7 @@ public class Instantiator implements IInstantiator { for (final SSAValue see : seen) { if (ParameterAccessor.isAssignable(see.getType(), payloadType, this.cha)) { // Happens on Array of interfaces - + logger.trace("Reusing {} for array payload {}", see, payload); payload = see; } } @@ -268,7 +262,7 @@ public class Instantiator implements IInstantiator { return instance; } else { // Abstract, Interface or array - + logger.debug("Not a regular class {}", T); final Set subInstances = new HashSet(); for (final TypeReference type : types) { final IClass subKlass = this.cha.lookupClass(type); @@ -331,7 +325,7 @@ public class Instantiator implements IInstantiator { this.pm.setPhi(instance, phi); } } else { - + logger.warn("No sub-instances for: {} - setting to null", instance); this.body.addConstant(instance.getNumber(), new ConstantValue(null)); instance.setAssigned(); } @@ -400,7 +394,7 @@ public class Instantiator implements IInstantiator { final IMethod cTor = lookupConstructor(val.getType()); final ParameterAccessor ctorAcc = new ParameterAccessor(cTor); assert (ctorAcc.hasImplicitThis()) : "CTor detected as not having implicit this pointer"; - + logger.debug("Acc for: %", this.scope); final ParameterAccessor acc = new ParameterAccessor(this.scope, false); // TODO pm needs a connectThrough too! // TODO false is false // TODO: The overrides may lead to use before definition @@ -413,8 +407,8 @@ public class Instantiator implements IInstantiator { seen.add(nullSelf); seen.addAll(overrides); - - + logger.debug("Recursing for: {}", cTor); + logger.debug("With seen: {}", seen); final List ctorParams = acc.connectThrough(ctorAcc, overrides, /* defaults */ null, this.cha, this, /* managed */ false, /* key */ null, seen); // XXX This starts the recursion! addCallCtor(val, cTor.getReference(), ctorParams); @@ -440,18 +434,19 @@ public class Instantiator implements IInstantiator { * Used internally to avoid endless recursion on getTypes(). */ private Set getTypes(final TypeReference T, final Set seen) { + logger.debug("getTypes({}, {})", T, seen); final Set ret = new HashSet(); ret.add(T); if (T.isPrimitiveType()) { - + logger.warn("getTypes called on a primitive"); return ret; //throw new IllegalArgumentException("Not you that call primitive type on :P"); } final IClass cls = this.cha.lookupClass(T); if (cls == null) { - + logger.error("The type {} is not in the ClassHierarchy - try continuing anyway", T); return ret; //throw new IllegalArgumentException("The type " + T + " is not in the ClassHierarchy"); } else if (isExcluded(cls)) { @@ -465,9 +460,9 @@ public class Instantiator implements IInstantiator { if (impls.isEmpty()) { //throw new IllegalStateException("The interface " + T + " has no known implementors"); if (! T.getName().toString().startsWith("Landroid/")) { - + logger.error("The interface {} has no known implementors - skipping over it", T); } else { - + logger.debug("The interface {} has no known implementors - skipping over it", T); } return ret; // XXX: This is a bad idea? } else { @@ -487,7 +482,7 @@ public class Instantiator implements IInstantiator { } else { for (final IClass sub: subs) { if (seen.contains(sub.getReference())) { - + logger.debug("Seen: {}", sub); continue; } if (sub.isAbstract()) { @@ -572,7 +567,7 @@ public class Instantiator implements IInstantiator { final IMethod method = methods.iterator().next(); assert (method.isInit()); final SSAInstruction firstInstruction = this.cache.getIR(method).iterateAllInstructions().next(); - + logger.debug("First instruction of ctor is: " + firstInstruction); if (firstInstruction instanceof SSAAbstractInvokeInstruction) { final SSAAbstractInvokeInstruction invokation = (SSAAbstractInvokeInstruction) firstInstruction; return invokation.isSpecial(); // Always? @@ -644,12 +639,12 @@ public class Instantiator implements IInstantiator { score = candidScore; } - + logger.debug("CTor {} got score {}", im, candidScore); } if (ctor == null) { - + logger.warn("Still found no CTor for {}", T); return cha.resolveMethod(klass, MethodReference.initSelector); } else { return ctor; diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/SpecializedInstantiator.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/SpecializedInstantiator.java index 6adc5e780..860c2087e 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/SpecializedInstantiator.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/parameters/SpecializedInstantiator.java @@ -150,7 +150,7 @@ public class SpecializedInstantiator extends FlatInstantiator { understandTypes.add(AndroidTypes.ContextWrapper); } - public boolean understands(TypeReference T) { + public static boolean understands(TypeReference T) { return understandTypes.contains(T); } diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/AbstractAndroidModel.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/AbstractAndroidModel.java index 776f403d5..93132ab08 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/AbstractAndroidModel.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/AbstractAndroidModel.java @@ -1,12 +1,3 @@ -/* - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html. - * - * This file is a derivative of code released under the terms listed below. - * - */ /* * Copyright (c) 2013, * Tobias Blaschke @@ -43,6 +34,9 @@ package com.ibm.wala.dalvik.ipa.callgraph.androidModel.structure; import java.util.ArrayList; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.ibm.wala.dalvik.ipa.callgraph.impl.AndroidEntryPoint; import com.ibm.wala.dalvik.ipa.callgraph.impl.AndroidEntryPoint.ExecutionOrder; import com.ibm.wala.dalvik.ipa.callgraph.impl.AndroidEntryPoint.IExecutionOrder; @@ -73,6 +67,8 @@ import com.ibm.wala.util.ssa.TypeSafeInstructionFactory; * @since 2013-09-07 */ public abstract class AbstractAndroidModel { + private static final Logger logger = LoggerFactory.getLogger(AbstractAndroidModel.class); + private ExecutionOrder currentSection = null; protected VolatileMethodSummary body = null; protected TypeSafeInstructionFactory insts = null; @@ -84,26 +80,6 @@ public abstract class AbstractAndroidModel { // Helper functions // - /** - * Breaks a basic block. - * - * When inserting loops into the model you have to assure a new basic block starts at the - * target of the jump. - *

- * An endless loop is a really silly way to do that, but it worked for static analysis. - * - * @return The PC after insertion. - * @deprecated The GoTo instruction breaks basic blocks by itself now, no need to call this - * function any longer - */ - @Deprecated - protected int makeBrakingNOP(int PC) { - - body.addStatement(insts.GotoInstruction(PC, PC)); - PC++; - return PC; - } - /** * Return a List of all Types returned by functions between start (inclusive) and end (exclusive). * @@ -350,7 +326,7 @@ public abstract class AbstractAndroidModel { if ((this.currentSection != null) && (this.currentSection.compareTo(section) >= 0)) { if (this.currentSection.compareTo(section) == 0) { - + logger.error("You entered {} twice! Ignoring second atempt.", section); } else { throw new IllegalArgumentException("Sections must be in ascending order! When trying to " + "enter " + this.currentSection.toString() + " from " + section.toString()); @@ -369,28 +345,28 @@ public abstract class AbstractAndroidModel { } if ((this.currentSection == null) && (section.compareTo(AndroidEntryPoint.ExecutionOrder.AT_FIRST) >= 0)) { - + logger.info("ENTER: AT_FIRST"); PC = enterAT_FIRST(PC); this.currentSection = AndroidEntryPoint.ExecutionOrder.AT_FIRST; } if ((this.currentSection.compareTo(AndroidEntryPoint.ExecutionOrder.AT_FIRST) <= 0) && (section.compareTo(AndroidEntryPoint.ExecutionOrder.BEFORE_LOOP) >= 0)) { - + logger.info("ENTER: BEFORE_LOOP"); PC = enterBEFORE_LOOP(PC); this.currentSection = AndroidEntryPoint.ExecutionOrder.BEFORE_LOOP; } if ((this.currentSection.compareTo(AndroidEntryPoint.ExecutionOrder.BEFORE_LOOP) <= 0) && (section.compareTo(AndroidEntryPoint.ExecutionOrder.START_OF_LOOP) >= 0)) { - + logger.info("ENTER: START_OF_LOOP"); PC = enterSTART_OF_LOOP(PC); this.currentSection = AndroidEntryPoint.ExecutionOrder.START_OF_LOOP; } if ((this.currentSection.compareTo(AndroidEntryPoint.ExecutionOrder.START_OF_LOOP) <= 0) && (section.compareTo(AndroidEntryPoint.ExecutionOrder.MIDDLE_OF_LOOP) >= 0)) { - + logger.info("ENTER: MIDDLE_OF_LOOP"); PC = enterMIDDLE_OF_LOOP(PC); this.currentSection = AndroidEntryPoint.ExecutionOrder.MIDDLE_OF_LOOP; } @@ -398,27 +374,27 @@ public abstract class AbstractAndroidModel { if ((this.currentSection.compareTo(AndroidEntryPoint.ExecutionOrder.MIDDLE_OF_LOOP) <= 0) && (section.compareTo(AndroidEntryPoint.ExecutionOrder.MULTIPLE_TIMES_IN_LOOP) >= 0)) { PC = enterMULTIPLE_TIMES_IN_LOOP(PC); - + logger.info("ENTER: MULTIPLE_TIMES_IN_LOOP"); this.currentSection = AndroidEntryPoint.ExecutionOrder.MULTIPLE_TIMES_IN_LOOP; } if ((this.currentSection.compareTo(AndroidEntryPoint.ExecutionOrder.MULTIPLE_TIMES_IN_LOOP) <= 0) && (section.compareTo(AndroidEntryPoint.ExecutionOrder.END_OF_LOOP) >= 0)) { - + logger.info("ENTER: END_OF_LOOP"); PC = enterEND_OF_LOOP(PC); this.currentSection = AndroidEntryPoint.ExecutionOrder.END_OF_LOOP; } if ((this.currentSection.compareTo(AndroidEntryPoint.ExecutionOrder.END_OF_LOOP) <= 0) && (section.compareTo(AndroidEntryPoint.ExecutionOrder.AFTER_LOOP) >= 0)) { - + logger.info("ENTER: AFTER_LOOP"); PC = enterAFTER_LOOP(PC); this.currentSection = AndroidEntryPoint.ExecutionOrder.AFTER_LOOP; } if ((this.currentSection.compareTo(AndroidEntryPoint.ExecutionOrder.AFTER_LOOP) <= 0) && (section.compareTo(AndroidEntryPoint.ExecutionOrder.AT_LAST) >= 0)) { - + logger.info("ENTER: AT_LAST"); PC = enterAT_LAST(PC); this.currentSection = AndroidEntryPoint.ExecutionOrder.AT_LAST; } diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/LoopAndroidModel.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/LoopAndroidModel.java index 4959895d4..15677f15a 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/LoopAndroidModel.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/LoopAndroidModel.java @@ -1,12 +1,3 @@ -/* - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html. - * - * This file is a derivative of code released under the terms listed below. - * - */ /* * Copyright (c) 2013, * Tobias Blaschke @@ -44,14 +35,19 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.logging.Logger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.ibm.wala.dalvik.ipa.callgraph.impl.AndroidEntryPoint.ExecutionOrder; import com.ibm.wala.ipa.callgraph.Entrypoint; import com.ibm.wala.ipa.summaries.VolatileMethodSummary; +import com.ibm.wala.shrikeBT.IConditionalBranchInstruction; +import com.ibm.wala.ssa.ConstantValue; import com.ibm.wala.ssa.SSAPhiInstruction; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.ssa.SSAValue; +import com.ibm.wala.util.ssa.SSAValue.NamedKey; import com.ibm.wala.util.ssa.SSAValue.TypeKey; import com.ibm.wala.util.ssa.SSAValue.VariableKey; import com.ibm.wala.util.ssa.SSAValueManager; @@ -74,6 +70,8 @@ import com.ibm.wala.util.ssa.TypeSafeInstructionFactory; * @author Tobias Blaschke */ public class LoopAndroidModel extends SingleStartAndroidModel { + private static final Logger logger = LoggerFactory.getLogger(LoopAndroidModel.class); + //protected VolatileMethodSummary body; //protected JavaInstructionFactory insts; //protected DexFakeRootMethod.ReuseParameters paramTypes; @@ -98,10 +96,10 @@ public class LoopAndroidModel extends SingleStartAndroidModel { * {@inheritDoc} */ protected int enterSTART_OF_LOOP (int PC) { - + logger.info("PC {} is the jump target of START_OF_LOOP", PC); this.outerLoopPC = PC; - PC = makeBrakingNOP(this.outerLoopPC); + PC = body.getNextProgramCounter(); paramManager.scopeDown(true); // Top-Half of Phi-Handling @@ -137,7 +135,7 @@ public class LoopAndroidModel extends SingleStartAndroidModel { // Insert the Phis at the beginning of the Block int phiPC = outerLoopPC + 1; boolean oldAllowReserved = body.allowReserved(true); - + logger.info("Setting block-inner Phis"); for (TypeReference phiType : outerStartingPhis.keySet()) { final SSAValue oldPhi = outerStartingPhis.get(phiType); final List forPhi = new ArrayList(2); @@ -152,19 +150,23 @@ public class LoopAndroidModel extends SingleStartAndroidModel { body.allowReserved(oldAllowReserved); // Close the Loop - - - body.addStatement(insts.GotoInstruction(PC, outerLoopPC)); + logger.info("Closing Loop"); + logger.info("PC {}: Goto {}", PC, outerLoopPC); + NamedKey trueKey = new SSAValue.NamedKey(TypeReference.BooleanName, "true"); + SSAValue trueVal = paramManager.getFree(TypeReference.Boolean, trueKey); + paramManager.setPhi(trueVal, null); + body.addConstant(trueVal.getNumber(), new ConstantValue(true)); + body.addStatement(insts.ConditionalBranchInstruction(PC, IConditionalBranchInstruction.Operator.EQ, TypeReference.Boolean, trueVal.getNumber(), trueVal.getNumber(), outerLoopPC)); paramManager.scopeUp(); // Add Phi-Statements at the beginning of this block... - + logger.info("Setting outer-block Phis"); for (TypeReference phiType : outerStartingPhis.keySet()) { final VariableKey phiKey = outerStartingPhis.get(phiType).key; PC = body.getNextProgramCounter(); List all = paramManager.getAllForPhi(phiKey); - + logger.debug("Into phi {} for {}", all, phiType.getName()); // Narf ... unpacking... paramManager.invalidate(phiKey); @@ -184,7 +186,7 @@ public class LoopAndroidModel extends SingleStartAndroidModel { * {@inheritDoc} */ protected int leaveAT_LAST (int PC) { - + logger.info("Leaving Model with PC = {}", PC); return PC; } diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/LoopKillAndroidModel.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/LoopKillAndroidModel.java index 1ba0a731f..3ce2c1c72 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/LoopKillAndroidModel.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/LoopKillAndroidModel.java @@ -1,12 +1,3 @@ -/* - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html. - * - * This file is a derivative of code released under the terms listed below. - * - */ /* * Copyright (c) 2013, * Tobias Blaschke @@ -44,14 +35,19 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.logging.Logger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.ibm.wala.dalvik.ipa.callgraph.impl.AndroidEntryPoint.ExecutionOrder; import com.ibm.wala.ipa.callgraph.Entrypoint; import com.ibm.wala.ipa.summaries.VolatileMethodSummary; +import com.ibm.wala.shrikeBT.IConditionalBranchInstruction; +import com.ibm.wala.ssa.ConstantValue; import com.ibm.wala.ssa.SSAPhiInstruction; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.ssa.SSAValue; +import com.ibm.wala.util.ssa.SSAValue.NamedKey; import com.ibm.wala.util.ssa.SSAValue.TypeKey; import com.ibm.wala.util.ssa.SSAValue.VariableKey; import com.ibm.wala.util.ssa.SSAValueManager; @@ -70,6 +66,8 @@ import com.ibm.wala.util.ssa.TypeSafeInstructionFactory; * @author Tobias Blaschke */ public class LoopKillAndroidModel extends LoopAndroidModel { + private static final Logger logger = LoggerFactory.getLogger(LoopKillAndroidModel.class); + //protected VolatileMethodSummary body; //protected JavaInstructionFactory insts; //protected DexFakeRootMethod.ReuseParameters paramTypes; @@ -92,10 +90,10 @@ public class LoopKillAndroidModel extends LoopAndroidModel { * {@inheritDoc} */ protected int enterAT_FIRST(int PC) { - + logger.info("PC {} is the jump target of START_OF_LOOP", PC); this.outerLoopPC = PC; - PC = makeBrakingNOP(this.outerLoopPC); + PC = body.getNextProgramCounter(); paramManager.scopeDown(true); // Top-Half of Phi-Handling @@ -131,7 +129,7 @@ public class LoopKillAndroidModel extends LoopAndroidModel { // Insert the Phis at the beginning of the Block int phiPC = outerLoopPC + 1; boolean oldAllowReserved = body.allowReserved(true); - + logger.info("Setting block-inner Phis"); for (TypeReference phiType : outerStartingPhis.keySet()) { final SSAValue oldPhi = outerStartingPhis.get(phiType); final List forPhi = new ArrayList(2); @@ -146,19 +144,23 @@ public class LoopKillAndroidModel extends LoopAndroidModel { body.allowReserved(oldAllowReserved); // Close the Loop - - - body.addStatement(insts.GotoInstruction(PC, outerLoopPC)); + logger.info("Closing Loop"); + logger.info("PC {}: Goto {}", PC, outerLoopPC); + NamedKey trueKey = new SSAValue.NamedKey(TypeReference.BooleanName, "true"); + SSAValue trueVal = paramManager.getFree(TypeReference.Boolean, trueKey); + paramManager.setPhi(trueVal, null); + body.addConstant(trueVal.getNumber(), new ConstantValue(true)); + body.addStatement(insts.ConditionalBranchInstruction(PC, IConditionalBranchInstruction.Operator.EQ, TypeReference.Boolean, trueVal.getNumber(), trueVal.getNumber(), outerLoopPC)); paramManager.scopeUp(); // Add Phi-Statements at the beginning of this block... - + logger.info("Setting outer-block Phis"); for (TypeReference phiType : outerStartingPhis.keySet()) { final VariableKey phiKey = outerStartingPhis.get(phiType).key; PC = body.getNextProgramCounter(); List all = paramManager.getAllForPhi(phiKey); - + logger.debug("Into phi {} for {}", all, phiType.getName()); // Narf ... unpacking... paramManager.invalidate(phiKey); diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/SingleStartAndroidModel.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/SingleStartAndroidModel.java index 99c459ffc..45c5fe399 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/SingleStartAndroidModel.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/androidModel/structure/SingleStartAndroidModel.java @@ -1,12 +1,3 @@ -/* - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html. - * - * This file is a derivative of code released under the terms listed below. - * - */ /* * Copyright (c) 2013, * Tobias Blaschke @@ -44,14 +35,19 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.logging.Logger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.ibm.wala.dalvik.ipa.callgraph.impl.AndroidEntryPoint.ExecutionOrder; import com.ibm.wala.ipa.callgraph.Entrypoint; import com.ibm.wala.ipa.summaries.VolatileMethodSummary; +import com.ibm.wala.shrikeBT.IConditionalBranchInstruction; +import com.ibm.wala.ssa.ConstantValue; import com.ibm.wala.ssa.SSAPhiInstruction; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.ssa.SSAValue; +import com.ibm.wala.util.ssa.SSAValue.NamedKey; import com.ibm.wala.util.ssa.SSAValue.TypeKey; import com.ibm.wala.util.ssa.SSAValue.VariableKey; import com.ibm.wala.util.ssa.SSAValueManager; @@ -73,6 +69,8 @@ import com.ibm.wala.util.ssa.TypeSafeInstructionFactory; * @author Tobias Blaschke */ public class SingleStartAndroidModel extends AbstractAndroidModel { + private static final Logger logger = LoggerFactory.getLogger(SingleStartAndroidModel.class); + //protected VolatileMethodSummary body; //protected JavaInstructionFactory insts; //protected DexFakeRootMethod.ReuseParameters paramTypes; @@ -98,10 +96,9 @@ public class SingleStartAndroidModel extends AbstractAndroidModel { * {@inheritDoc} */ protected int enterMULTIPLE_TIMES_IN_LOOP (int PC) { - - + logger.info("PC {} is the jump target of START_OF_LOOP", PC); this.outerLoopPC = PC; - PC = makeBrakingNOP(this.outerLoopPC); + PC = body.getNextProgramCounter(); paramManager.scopeDown(true); // Top-Half of Phi-Handling @@ -137,7 +134,7 @@ public class SingleStartAndroidModel extends AbstractAndroidModel { // Insert the Phis at the beginning of the Block int phiPC = outerLoopPC + 1; boolean oldAllowReserved = body.allowReserved(true); - + logger.info("Setting block-inner Phis"); for (TypeReference phiType : outerStartingPhis.keySet()) { final SSAValue oldPhi = outerStartingPhis.get(phiType); final List forPhi = new ArrayList(2); @@ -152,19 +149,25 @@ public class SingleStartAndroidModel extends AbstractAndroidModel { body.allowReserved(oldAllowReserved); // Close the Loop - - - body.addStatement(insts.GotoInstruction(PC, outerLoopPC)); + logger.info("Closing Loop"); + logger.info("PC {}: Goto {}", PC, outerLoopPC); + if (PC != outerLoopPC) { + NamedKey trueKey = new SSAValue.NamedKey(TypeReference.BooleanName, "true"); + SSAValue trueVal = paramManager.getFree(TypeReference.Boolean, trueKey); + paramManager.setPhi(trueVal, null); + body.addConstant(trueVal.getNumber(), new ConstantValue(true)); + body.addStatement(insts.ConditionalBranchInstruction(PC, IConditionalBranchInstruction.Operator.EQ, TypeReference.Boolean, trueVal.getNumber(), trueVal.getNumber(), outerLoopPC)); + } paramManager.scopeUp(); // Add Phi-Statements at the beginning of this block... - + logger.info("Setting outer-block Phis"); for (TypeReference phiType : outerStartingPhis.keySet()) { final VariableKey phiKey = outerStartingPhis.get(phiType).key; PC = body.getNextProgramCounter(); List all = paramManager.getAllForPhi(phiKey); - + logger.debug("Into phi {} for {}", all, phiType.getName()); // Narf ... unpacking... paramManager.invalidate(phiKey); diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/Intent.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/Intent.java index c123e194f..ab0fc02d7 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/Intent.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/Intent.java @@ -260,7 +260,7 @@ public class Intent implements ContextItem, Comparable { private static boolean isSystemService(Intent intent) { assert (intent.action != null); - return ((intent.action.getVal(0) != 'L') && (intent.action.rIndex((byte) '/') < 0) && (intent.action.rIndex((byte) '.') < 0)); + return (intent != null && intent.action != null && (intent.action.getVal(0) != 'L') && (intent.action.rIndex((byte) '/') < 0) && (intent.action.rIndex((byte) '.') < 0)); } /** @@ -311,7 +311,7 @@ public class Intent implements ContextItem, Comparable { // Unknown so not selected as external return false; } - return (! intent.action.toString().startsWith(pack)); + return (! (intent.action.toString().startsWith("L" + pack) || intent.action.toString().startsWith(pack))); } /** diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextSelector.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextSelector.java index 756669046..3f621b720 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextSelector.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ipa/callgraph/propagation/cfa/IntentContextSelector.java @@ -3,8 +3,8 @@ * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html. - * - * This file is a derivative of code released under the terms listed below. + * + * This file is a derivative of code released under the terms listed below. * */ /* @@ -40,32 +40,59 @@ */ package com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa; -import java.util.Map; -import java.util.logging.Logger; +import com.ibm.wala.ipa.callgraph.ContextSelector; + +import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.Intent; +import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentMap; +import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentContext; +import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.AndroidContext; +import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentContextInterpreter; import com.ibm.wala.classLoader.CallSiteReference; +import com.ibm.wala.classLoader.IClass; +import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.classLoader.IMethod; -import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters.StartInfo; -import com.ibm.wala.dalvik.util.AndroidEntryPointManager; -import com.ibm.wala.dalvik.util.AndroidTypes; +import com.ibm.wala.types.Selector; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.Context; -import com.ibm.wala.ipa.callgraph.ContextSelector; import com.ibm.wala.ipa.callgraph.propagation.ConstantKey; import com.ibm.wala.ipa.callgraph.propagation.InstanceKey; -import com.ibm.wala.ipa.cha.IClassHierarchy; + import com.ibm.wala.types.MethodReference; -import com.ibm.wala.types.Selector; -import com.ibm.wala.util.collections.HashMapFactory; +import com.ibm.wala.types.TypeReference; +import com.ibm.wala.ipa.callgraph.propagation.NormalAllocationInNode; +import com.ibm.wala.ipa.callgraph.propagation.AbstractTypeInNode; +import com.ibm.wala.ssa.SSAInstruction; +import com.ibm.wala.ssa.SSAAbstractInvokeInstruction; + +import com.ibm.wala.util.strings.Atom; +import com.ibm.wala.dalvik.util.AndroidTypes; import com.ibm.wala.util.intset.EmptyIntSet; import com.ibm.wala.util.intset.IntSet; +import com.ibm.wala.util.intset.BimodalMutableIntSet; import com.ibm.wala.util.intset.IntSetUtil; +import com.ibm.wala.ssa.SymbolTable; +import com.ibm.wala.util.collections.HashMapFactory; + +import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters; +import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters.StartInfo; + +import com.ibm.wala.dalvik.util.AndroidEntryPointManager; + +import com.ibm.wala.util.strings.StringStuff; +import java.util.Iterator; +import java.util.Map; +import java.util.HashMap; +import com.ibm.wala.util.collections.Pair; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Adds Intents to the Context of functions that start Android-Components. * * This is done by remembering all new-sites where Intent-Objects are built and the parameters to its - * Constructor. When a function managed by this Selector (see IntentStarters) is encountered the stored + * Constructor. When a function managed by this Selector (see IntentStarters) is encountered the stored * information is added to its Context. * * @see com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentContextInterpreter @@ -75,7 +102,9 @@ import com.ibm.wala.util.intset.IntSetUtil; * @since 2013-10-14 */ public class IntentContextSelector implements ContextSelector { - private final IntentMap intents = new IntentMap(); + private static final Logger logger = LoggerFactory.getLogger(IntentContextSelector.class); + + private final IntentMap intents = new IntentMap(); private final ContextSelector parent; private final IntentStarters intentStarters; private final Map seenContext; @@ -95,7 +124,7 @@ public class IntentContextSelector implements ContextSelector { /** * Given a calling node and a call site, returns the Context in which the callee should be evaluated. - * + * * {@inheritDoc} * * @throws IllegalArgumentException if the type of a parameter given as actualParameters does not match an expected one @@ -116,25 +145,25 @@ public class IntentContextSelector implements ContextSelector { /* { final InstanceKey self = actualParameters[0]; assert (self != null) : "This-Pointer was not marked as relevant!"; - + if (seenContext.containsKey(self)) { ctx = new AndroidContext(ctx, seenContext.get(self).getContextType()); } else { - + logger.warn("No Android-Context seen for {}", caller); } } // */ Intent intent = null; - { // Seach intent + { // Seach intent for (int j = 0; j < actualParameters.length; ++j) { final InstanceKey param = actualParameters[j]; if (param == null) { continue; - } else if (param.getConcreteType().getName().equals(AndroidTypes.IntentName)) { + } else if (param.getConcreteType().getName().equals(AndroidTypes.IntentName)) { if (! intents.contains(param) ) { - - + logger.error("Unable to resolve Intent called from {}", caller.getMethod()); + logger.error("Search Key: {} hash: {}", param, param.hashCode()); break; } else { intent = intents.find(param); @@ -151,10 +180,10 @@ public class IntentContextSelector implements ContextSelector { return new IntentContext(ctx, iintent); //return new IntentContext(iintent); } else { - + logger.warn("Encountered unresolvable Intent"); intent = new Intent("Unresolvable"); intent.setImmutable(); - AndroidEntryPointManager.MANAGER.addCallSeen(site, intent); + AndroidEntryPointManager.MANAGER.addCallSeen(site, intent); return new IntentContext(ctx, intent); //return new IntentContext(intent); } @@ -172,21 +201,21 @@ public class IntentContextSelector implements ContextSelector { return Intent.IntentType.SYSTEM_SERVICE; } // TODO override equals and hashCode? - }; + }; } else { intent = null; if (param == null) { - + logger.warn("Got param as 'null'. Obviously can't handle this. Caller was: {}", caller.getMethod()); } else { - + logger.warn("Got param as {}. Can't handle this :(", param.getClass()); } } } - + // Add the context if (intent != null) { - AndroidEntryPointManager.MANAGER.addCallSeen(site, intent); - + AndroidEntryPointManager.MANAGER.addCallSeen(site, intent); + logger.info("SystemService {} in {} by {}", intent, site, caller); final Intent iintent = intents.findOrCreateImmutable(intent); return new IntentContext(ctx, iintent); //return new IntentContext(iintent); @@ -202,57 +231,64 @@ public class IntentContextSelector implements ContextSelector { final InstanceKey uriKey; final InstanceKey actionKey; { // fetch actionKey, uriKey - switch (callee.getNumberOfParameters()) { + switch (callee.getNumberOfParameters()) { case 1: + logger.debug("Handling Intent()"); actionKey = null; uriKey = null; break; - case 2: + case 2: if (calleeSel.equals(Selector.make("(Ljava/lang/String;)V"))) { - actionKey = actualParameters[1]; + logger.debug("Handling Intent(String action)"); + actionKey = actualParameters[1]; } else if (calleeSel.equals(Selector.make("(Landroid/content/Intent;)V"))) { + logger.debug("Handling Intent(Intent other)"); final InstanceKey inIntent = actualParameters[1]; if (intents.contains(inIntent)) { intents.put(self, intents.find(inIntent)); } else { - + logger.warn("In Intent-Copy constructor: Unable to find the original"); } actionKey = null; } else { - + logger.error("No handling implemented for: {}", callee); actionKey = null; } uriKey = null; break; case 3: if (calleeSel.equals(Selector.make("(Ljava/lang/String;Landroid/net/Uri;)V"))) { + logger.debug("Handling Intent(String action, Uri uri)"); // TODO: Use Information of the URI... - actionKey = actualParameters[1]; + actionKey = actualParameters[1]; uriKey = actualParameters[2]; } else if (calleeSel.equals(Selector.make("(Landroid/content/Context;Ljava/lang/Class;)V"))) { + logger.debug("Handling Intent(Context, Class)"); actionKey = actualParameters[2]; uriKey = null; isExplicit = true; } else { - + logger.error("No handling implemented for: {}", callee); actionKey = null; uriKey = null; } break; case 5: if (calleeSel.equals(Selector.make("(Ljava/lang/String;Landroid/net/Uri;Landroid/content/Context;Ljava/lang/Class;)V"))) { + logger.debug("Handling Intent(String action, Uri uri, Context, Class)"); actionKey = actualParameters[4]; uriKey = actualParameters[2]; isExplicit = true; } else { - + logger.error("No handling implemented for: {}", callee); actionKey = null; uriKey = null; } break; default: + logger.error("Can't extract Info from Intent-Constructor: {} (not implemented)", site); actionKey = null; uriKey = null; } @@ -261,9 +297,9 @@ public class IntentContextSelector implements ContextSelector { final Intent intent = intents.findOrCreate(self); // Creates Wala-internal Intent if (actionKey == null) { - + logger.trace("Got action as 'null'. Obviously can't handle this. Caller was {}", caller.getMethod()); if (isExplicit) { - + logger.warn("An Intent with undeteminable target would be explicit - unbinding. Caller was {}", caller.getMethod()); intent.unbind(); } } else { @@ -274,31 +310,31 @@ public class IntentContextSelector implements ContextSelector { // intents.setExplicit(self); //} - + logger.debug("Setting the target of Intent {} in {} by {}", intent, site, caller); // TODO: Evaluate uriKey - } else if (callee.getSelector().equals(Selector.make("setAction(Ljava/lang/String;)Landroid/content/Intent;")) && + } else if (callee.getSelector().equals(Selector.make("setAction(Ljava/lang/String;)Landroid/content/Intent;")) && callee.getDeclaringClass().getName().equals(AndroidTypes.IntentName)) { final InstanceKey self = actualParameters[0]; final InstanceKey actionKey = actualParameters[1]; final Intent intent = intents.find(self); if (AndroidEntryPointManager.MANAGER.isAllowIntentRerouting()) { - + logger.warn("Re-Setting the target of Intent {} in {} by {}", intent, site, caller); intents.setAction(self, actionKey, false); // May unbind internally } else { intents.unbind(self); } - + logger.info("Encountered Intent.setAction - Intent is now: {}", intent); } else if (callee.getSelector().equals(Selector.make("setComponent(Landroid/content/ComponentName;)Landroid/content/Intent;"))) { // TODO: We can't extract from ComponentName yet. final InstanceKey self = actualParameters[0]; final Intent intent = intents.find(self); - - + logger.warn("Re-Setting the target of Intent {} in {} by {}", intent, site, caller); + intent.setExplicit(); intents.unbind(self); - } else if (callee.getSelector().equals(Selector.make("setClass(Landroid/content/Context;Ljava/lang/Class;)Landroid/content/Intent;")) || + } else if (callee.getSelector().equals(Selector.make("setClass(Landroid/content/Context;Ljava/lang/Class;)Landroid/content/Intent;")) || callee.getSelector().equals(Selector.make("setClassName(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;")) || callee.getSelector().equals(Selector.make("setClassName(Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent;"))) { final InstanceKey self = actualParameters[0]; @@ -306,51 +342,51 @@ public class IntentContextSelector implements ContextSelector { final Intent intent = intents.find(self); if (AndroidEntryPointManager.MANAGER.isAllowIntentRerouting()) { - + logger.warn("Re-Setting the target of Intent {} in {} by {}", intent, site, caller); intents.setAction(self, actionKey, true); } else { intents.unbind(self); } - + logger.info("Encountered Intent.setClass - Intent is now: {}", intent); } else if (callee.getSelector().equals(Selector.make("fillIn(Landroid/content/Intent;I)I"))) { // See 'setAction' before... TODO - + logger.warn("Intent.fillIn not implemented - Caller: {}", caller); final InstanceKey self = actualParameters[0]; intents.unbind(self); } else if (callee.isInit() && callee.getDeclaringClass().getName().equals(AndroidTypes.IntentSenderName)) { - // TODO + logger.error("Unable to evaluate IntentSender: Not implemented!"); // TODO } /*else if (site.isSpecial() && callee.getDeclaringClass().getName().equals( AndroidTypes.ContextWrapperName)) { - final InstanceKey baseKey = actualParameters[1]; - final InstanceKey wrapperKey = actualParameters[0]; + final InstanceKey baseKey = actualParameters[1]; + final InstanceKey wrapperKey = actualParameters[0]; logger.debug("Handling ContextWrapper(Context base)"); if (seenContext.containsKey(baseKey)) { seenContext.put(wrapperKey, seenContext.get(baseKey)); } else { if (baseKey == null) { - + logger.trace("Got baseKey as 'null'. Obviously can't handle this. Caller was: {}", caller.getMethod()); } else { - + logger.warn("ContextWrapper: No AndroidContext was seen for baseKey"); } } } else if ((site.isSpecial() && callee.getDeclaringClass().getName().equals( AndroidTypes.ContextImplName))) { final InstanceKey self = actualParameters[0]; - seenContext.put(self, new AndroidContext(ctx, AndroidTypes.AndroidContextType.CONTEXT_IMPL)); + seenContext.put(self, new AndroidContext(ctx, AndroidTypes.AndroidContextType.CONTEXT_IMPL)); } else if (callee.getDeclaringClass().getName().equals(AndroidTypes.ContextWrapperName) && callee.getSelector().equals(Selector.make("attachBaseContext(Landroid/content/Context;)V"))) { - final InstanceKey baseKey = actualParameters[1]; - final InstanceKey wrapperKey = actualParameters[0]; - + final InstanceKey baseKey = actualParameters[1]; + final InstanceKey wrapperKey = actualParameters[0]; + logger.debug("Handling ContextWrapper.attachBaseContext(base)"); if (seenContext.containsKey(baseKey)) { seenContext.put(wrapperKey, seenContext.get(baseKey)); } else { if (baseKey == null) { - + logger.trace("Got baseKey as 'null'. Obviously can't handle this. Caller was: {}", caller.getMethod()); } else { - + logger.warn("ContextWrapper: No AndroidContext was seen for baseKey"); } } } */ @@ -359,8 +395,8 @@ public class IntentContextSelector implements ContextSelector { } /** - * Given a calling node and a call site, return the set of parameters based on which this selector may choose - * to specialize contexts. + * Given a calling node and a call site, return the set of parameters based on which this selector may choose + * to specialize contexts. * * {@inheritDoc} */ @@ -383,10 +419,10 @@ public class IntentContextSelector implements ContextSelector { ret = IntSetUtil.add(ret, relevant[i]); } } - - + + logger.debug("Get relevant for {} is {}", site, ret); } else if (site.isSpecial() && target.getDeclaringClass().getName().equals( - AndroidTypes.IntentName)) { + AndroidTypes.IntentName)) { final MethodReference mRef = site.getDeclaredTarget(); final int numArgs = mRef.getNumberOfParameters(); @@ -405,33 +441,44 @@ public class IntentContextSelector implements ContextSelector { case 1: return IntSetUtil.make(new int[] { 0, 1 }); case 2: + logger.debug("Got Intent Constructor of: {}", site.getDeclaredTarget().getSelector()); return IntSetUtil.make(new int[] { 0, 1, 2 }); case 3: + logger.debug("Got Intent Constructor of: {}", site.getDeclaredTarget().getSelector()); return IntSetUtil.make(new int[] { 0, 1, 2, 3 }); case 4: + logger.debug("Got Intent Constructor of: {}", site.getDeclaredTarget().getSelector()); return IntSetUtil.make(new int[] { 0, 1, 2, 3, 4 }); default: + logger.debug("Got Intent Constructor of: {}", site.getDeclaredTarget().getSelector()); return IntSetUtil.make(new int[] { 0, 1, 2, 3, 4, 5 }); } } else if (site.isSpecial() && target.getDeclaringClass().getName().equals( AndroidTypes.IntentSenderName)) { - // public IntentSender(IIntentSender target) - // public IntentSender(IBinder target) - - return IntSetUtil.make(new int[] { 0, 1 }); + + logger.warn("Encountered an IntentSender-Object: {}", target); + if (target.getNumberOfParameters() == 0) { + // public IntentSender() + return IntSetUtil.make(new int[] { 0 }); + } else { + // public IntentSender(IIntentSender target) + // public IntentSender(IBinder target) + return IntSetUtil.make(new int[] { 0, 1 }); + } } /*else if (site.isSpecial() && target.getDeclaringClass().getName().equals( AndroidTypes.ContextWrapperName)) { - + logger.debug("Fetched ContextWrapper ctor"); return IntSetUtil.make(new int[] { 0, 1 }); } else if ((site.isSpecial() && target.getDeclaringClass().getName().equals( AndroidTypes.ContextImplName))) { - + logger.debug("Fetched Context ctor"); return IntSetUtil.make(new int[] { 0 }); } else if (target.getDeclaringClass().getName().equals(AndroidTypes.ContextWrapperName) && target.getSelector().equals(Selector.make("attachBaseContext(Landroid/content/Context;)V"))) { logger.debug("Encountered ContextWrapper.attachBaseContext()"); return IntSetUtil.make(new int[] { 0, 1 }); }*/ else if (target.getSelector().equals(Selector.make("getSystemService(Ljava/lang/String;)Ljava/lang/Object;"))) { + logger.debug("Encountered Context.getSystemService()"); return IntSetUtil.make(new int[] { 0, 1 }); } else if (target.getSelector().equals(Selector.make("setAction(Ljava/lang/String;)Landroid/content/Intent;"))) { return IntSetUtil.make(new int[] { 0, 1 }); diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/DexSSABuilder.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/DexSSABuilder.java index 4e798f756..b0ca6a3bb 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/DexSSABuilder.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/ssa/DexSSABuilder.java @@ -389,8 +389,9 @@ public class DexSSABuilder extends AbstractIntRegisterMachine { * If we've already created the current instruction, return the value number def'ed by the current instruction. Else, create a * new symbol. */ + private int reuseOrCreateDef() { - if (getCurrentInstruction() == null) { + if (getCurrentInstruction() == null || !getCurrentInstruction().hasDef()) { return symbolTable.newSymbol(); } else { return getCurrentInstruction().getDef(); @@ -766,7 +767,7 @@ public class DexSSABuilder extends AbstractIntRegisterMachine { */ @Override public void visitGoto(Goto instruction) { - emitInstruction(insts.GotoInstruction(getCurrentInstructionIndex(), -1)); + emitInstruction(insts.GotoInstruction(getCurrentInstructionIndex(), instruction.destination)); } /** diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidEntryPointLocator.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidEntryPointLocator.java index fb7944f6f..6ac75e513 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidEntryPointLocator.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidEntryPointLocator.java @@ -1,12 +1,3 @@ -/* - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html. - * - * This file is a derivative of code released under the terms listed below. - * - */ /* * Copyright (c) 2013, * Tobias Blaschke @@ -41,6 +32,7 @@ package com.ibm.wala.dalvik.util; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -50,6 +42,9 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IClassLoader; import com.ibm.wala.classLoader.IMethod; @@ -77,6 +72,7 @@ import com.ibm.wala.util.config.SetOfClasses; * @author Tobias Blaschke */ public final class AndroidEntryPointLocator { + private static final Logger logger = LoggerFactory.getLogger(AndroidEntryPointLocator.class); private final IProgressMonitor mon; /** @@ -176,13 +172,14 @@ nextMethod: if (this.flags.contains(LocatorFlags.INCLUDE_CALLBACKS)) { for (final AndroidComponent compo : AndroidComponent.values()) { if (compo == AndroidComponent.UNKNOWN) continue; - if (compo.toReference() != null) { + if (compo.toReference() == null) { + logger.error("Null-Reference for " + compo); + } else { bases.add(compo.toReference()); } } } else { // Restrict the set - bases.add(AndroidTypes.Handler); bases.add(AndroidTypes.Application); bases.add(AndroidTypes.Activity); /** @todo TODO: add Fragments in getEntryPoints */ @@ -223,6 +220,7 @@ nextMethod: try { candids = cha.computeSubClasses(base); } catch (IllegalArgumentException e) { // Pretty agan :( + logger.error(e.getMessage()); continue; } for (final IClass candid : candids) { @@ -236,13 +234,16 @@ nextMethod: if ((method.isInit() || method.isClinit()) && (! this.flags.contains(LocatorFlags.WITH_CTOR))) { + logger.debug("Skipping constructor of {}", method); continue; } if (baseClass.getMethod(method.getSelector()) != null) { final AndroidEntryPoint ep = makeEntryPointForHeuristic(method, cha); if (! eps.contains(ep)) { // Just to be sure that a previous element stays as-is - eps.add(ep); + if (eps.add(ep)) { + logger.debug("Heuristic 1: selecting {} for base {}", method, base); + } } } } @@ -288,7 +289,9 @@ nextMethod: isAndroidClass = true; break; } + logger.trace("Heuristic: \t {} is {}", appClass.getName().toString(), androidClass.getName().toString()); for (IClass iface : appClass.getAllImplementedInterfaces ()) { + logger.trace("Heuristic: \t implements {}", iface.getName().toString()); if (isAPIComponent(iface)) { isAndroidClass = true; break; @@ -298,16 +301,19 @@ nextMethod: androidClass = androidClass.getSuperclass(); } if (! isAndroidClass) { + logger.trace("Heuristic: Skipping non andoid {}", appClass.getName().toString()); continue; // continue appClass; } } + logger.debug("Heuristic: Scanning methods of {}", appClass.getName().toString()); { // Overridden methods if (isAPIComponent(appClass)) continue; if (isExcluded(appClass)) continue; final Collection methods = appClass.getDeclaredMethods(); for (final IMethod method : methods) { if ((method.isInit() || method.isClinit()) && (! this.flags.contains(LocatorFlags.WITH_CTOR))) { + logger.debug("Skipping constructor of {}", method); continue; } assert (method.getSelector() != null): "Method has no selector: " + method; @@ -316,7 +322,10 @@ nextMethod: final AndroidEntryPoint ep = makeEntryPointForHeuristic(method, cha); if (! eps.contains(ep)) { // Just to be sure that a previous element stays as-is - eps.add(ep); + if (eps.add(ep)) { + logger.debug("Heuristic 2a: selecting {}", method); + }} else { + logger.debug("Heuristic 2a: already selected {}", method); } } } @@ -326,22 +335,22 @@ nextMethod: final Collection iFaces = appClass.getAllImplementedInterfaces(); for (final IClass iFace : iFaces) { if (isAPIComponent(iFace)) { + logger.debug("Skipping iFace: {}", iFace); continue; } if (isExcluded(iFace)) continue; + logger.debug("Searching Interface {}", iFace); final Collection ifMethods = iFace.getDeclaredMethods(); for (final IMethod ifMethod : ifMethods) { final IMethod method = appClass.getMethod(ifMethod.getSelector()); - if (method == null || method.isAbstract()) { - continue; - } - if (method.getDeclaringClass().getClassLoader().getReference().equals(ClassLoaderReference.Application)) { + if (method != null && method.getDeclaringClass().getClassLoader().getReference().equals(ClassLoaderReference.Application)) { // The function is overridden final AndroidEntryPoint ep = new AndroidEntryPoint(selectPositionForHeuristic(method), method, cha); if (! eps.contains(ep)) { // Just to be sure that a previous element stays as-is - eps.add(ep); - } + if (eps.add(ep)) { + logger.debug("Heuristic 2b: selecting {}", method); + }} } else { // The function is taken from the super-class if (this.flags.contains(LocatorFlags.WITH_SUPER)) { @@ -356,6 +365,7 @@ nextMethod: System.arraycopy(oldTypes, 0, newTypes, 0, oldTypes.length); newTypes[oldTypes.length] = appClass.getReference(); eps_ep.setParameterTypes(0, newTypes); + logger.debug("New This-Types for {} are {}", method.getSelector(), Arrays.toString(newTypes)); } } } else { @@ -363,7 +373,10 @@ nextMethod: ep.setParameterTypes(0, new TypeReference[]{appClass.getReference()}); } eps.add(ep); + logger.debug("Heuristic 2b: selecting from super {}", method); } + } else { + logger.debug("Heuristic 2b: Skipping {}", method); } } } @@ -389,9 +402,12 @@ nextMethod: private boolean isExcluded(final IClass cls) { final SetOfClasses set = cls.getClassHierarchy().getScope().getExclusions(); - final String clsName = cls.getReference().getName().toString().substring(1); - - return set.contains(clsName); + if (set == null) { + return false; // exclusions null ==> no exclusions ==> no class is excluded + } else { + final String clsName = cls.getReference().getName().toString().substring(1); + return set.contains(clsName); + } } /** diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidEntryPointManager.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidEntryPointManager.java index c0b96ab1f..1ae239791 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidEntryPointManager.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidEntryPointManager.java @@ -1,12 +1,3 @@ -/* - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html. - * - * This file is a derivative of code released under the terms listed below. - * - */ /* * Copyright (c) 2013, * Tobias Blaschke @@ -49,6 +40,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.DefaultInstantiationBehavior; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.IInstantiationBehavior; @@ -76,7 +70,9 @@ import com.ibm.wala.util.strings.StringStuff; * @author Tobias Blaschke */ public final /* singleton */ class AndroidEntryPointManager implements Serializable { - public static final AndroidEntryPointManager MANAGER = new AndroidEntryPointManager(); + private static final Logger logger = LoggerFactory.getLogger(AndroidEntryPointManager.class); + + public static AndroidEntryPointManager MANAGER = new AndroidEntryPointManager(); public static List ENTRIES = new ArrayList(); /** * This is TRANSIENT! @@ -98,7 +94,12 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa return false; } - private AndroidEntryPointManager() {} + private AndroidEntryPointManager() {} + + public static void reset() { + ENTRIES = new ArrayList(); + MANAGER = new AndroidEntryPointManager(); + } public Set getComponents() { if (ENTRIES.isEmpty()) { @@ -242,7 +243,7 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa return prev; } - private Class abstractAndroidModel = LoopAndroidModel.class; + private Class abstractAndroidModel = LoopAndroidModel.class; /** * What special handling to insert into the model. * @@ -261,7 +262,7 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa return new LoopAndroidModel(body, insts, paramManager, entryPoints); } else { try { - final Constructor ctor = this.abstractAndroidModel.getDeclaredConstructor( + final Constructor ctor = this.abstractAndroidModel.getDeclaredConstructor( VolatileMethodSummary.class, TypeSafeInstructionFactory.class, SSAValueManager.class, Iterable.class); if (ctor == null) { @@ -290,7 +291,7 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa * * @return null or the class set using setModelBehavior */ - public Class getModelBehavior() { + public Class getModelBehavior() { return this.abstractAndroidModel; } @@ -299,7 +300,7 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa * * @throws IllgealArgumentException if the abstractAndroidModel does not subclass AbstractAndroidModel */ - public void setModelBehavior(Class abstractAndroidModel) { + public void setModelBehavior(Class abstractAndroidModel) { if (abstractAndroidModel == null) { throw new IllegalArgumentException("abstractAndroidModel may not be null. Use SequentialAndroidModel " + "if no special handling shall be inserted."); @@ -336,6 +337,7 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa pack = StringStuff.deployment2CanonicalTypeString(pack); } if (this.pack == null) { + logger.info("Setting the package to {}", pack); this.pack = pack; } else if (!(this.pack.equals(pack))) { throw new IllegalArgumentException("The already set package " + this.pack + " and " + pack + @@ -359,6 +361,7 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa */ public String getPackage() { if (this.pack == null) { + logger.warn("Returning null as package"); return null; } else { return this.pack; @@ -379,7 +382,7 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa return this.pack; } else { if (ENTRIES.isEmpty()) { - assert false : "guessPackage() called when no entrypoints had been set"; + logger.error("guessPackage() called when no entrypoints had been set"); return null; } final String first = ENTRIES.get(0).getMethod().getReference().getDeclaringClass().getName().getPackage().toString(); @@ -446,6 +449,7 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa throw new IllegalArgumentException("The given Intent is null"); } + logger.info("Register Intent {}", intent); // Looks a bit weired but works as Intents are only matched based on their action and uri overrideIntents.put(intent, intent); } @@ -559,6 +563,7 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa throw new IllegalArgumentException("The Intent given as 'to' is null"); } + logger.info("Override Intent {} to {}", from, to); overrideIntents.put(from, to); } @@ -581,20 +586,28 @@ public final /* singleton */ class AndroidEntryPointManager implements Serializa while (!(ret.equals(intent))) { // Follow the chain of overrides if (!overrideIntents.containsKey(intent)) { + logger.info("Resolved {} to {}", intent, ret); return ret; } else { + logger.debug("Resolving {} hop over {}", intent, ret); final Intent old = ret; ret = overrideIntents.get(ret); if (ret == old) { // Yes, == // This is an evil hack(tm). I should fix the Intent-Table! + logger.warn("Malformend Intent-Table, staying with " + ret + " for " + intent); return ret; } } } ret = overrideIntents.get(ret); // Once again to get Info set in register + logger.info("Resolved {} to {}", intent, ret); return ret; } else { + logger.info("No information on {} hash: {}", intent, intent.hashCode()); + for (Intent known : overrideIntents.keySet()) { + logger.debug("Known Intents: {} hash: {}", known, known.hashCode()); + } return intent; } } diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidManifestXMLReader.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidManifestXMLReader.java index b962bc7e3..21b7c25cc 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidManifestXMLReader.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidManifestXMLReader.java @@ -55,6 +55,8 @@ import java.util.Stack; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParserFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -93,6 +95,8 @@ public class AndroidManifestXMLReader { * If you only want Information about objects created use the logger in AndroidSettingFactory as this * parser generates all objects using it. */ + private static final Logger logger = LoggerFactory.getLogger(AndroidSettingFactory.class); + public AndroidManifestXMLReader(File xmlFile) throws IOException { if (xmlFile == null) { throw new IllegalArgumentException("xmlFile may not be null"); @@ -417,6 +421,7 @@ public class AndroidManifestXMLReader { } attributesHistory.get(relevant).push(attr); + logger.debug("Pushing '{}' for {} in {}", attr, relevant, self); // if there is no such value in saxAttrs it returns null } } @@ -428,6 +433,7 @@ public class AndroidManifestXMLReader { public void popAttributes() { for (Attr relevant : self.getRelevantAttributes()) { try { + logger.debug("Popping {} of value {} in {}", relevant, attributesHistory.get(relevant).peek(), self); attributesHistory.get(relevant).pop(); } catch (java.util.EmptyStackException e) { System.err.println(self + " failed to pop " + relevant); @@ -461,6 +467,7 @@ public class AndroidManifestXMLReader { } subTag.getHandler().popAttributes(); // hmmm.... + logger.debug("New Stack: {}", parserStack); //parserStack.pop(); } else { throw new IllegalStateException(subTag + " is not allowed as sub-tag of " + self + " in Context:\n\t" + parserStack); @@ -531,20 +538,18 @@ public class AndroidManifestXMLReader { @Override public void leave() { Set allowedTags = EnumSet.copyOf(self.getAllowedSubTags()); - String url = null; - String name = null; - + Set urls = new HashSet(); + Set names = new HashSet(); while (parserStack.peek() != self) { Tag current = parserStack.pop(); if (allowedTags.contains(current)) { - allowedTags.remove(current); // TODO: Does this always fit? if (current == Tag.ACTION) { Object oName = attributesHistory.get(Attr.NAME).peek(); if (oName == null) { throw new IllegalStateException("The currently parsed Action did not leave the required 'name' Attribute" + " on the Stack! Attributes-Stack for name is: " + attributesHistory.get(Attr.NAME)); } else if (oName instanceof String) { - name = (String) oName; + names.add((String) oName); } else { throw new IllegalStateException("Unexpected Attribute type for name: " + oName.getClass().toString()); } @@ -553,7 +558,7 @@ public class AndroidManifestXMLReader { if (oUrl == null) { // TODO } else if (oUrl instanceof String) { - url = (String) oUrl; + urls.add((String) oUrl); } else { throw new IllegalStateException("Unexpected Attribute type for name: " + oUrl.getClass().toString()); } @@ -572,12 +577,19 @@ public class AndroidManifestXMLReader { if ((attributesHistory.get(Attr.PACKAGE) != null ) && (!(attributesHistory.get(Attr.PACKAGE).isEmpty()))) { pack = (String) attributesHistory.get(Attr.PACKAGE).peek(); } else { + logger.warn("Empty Package {}", attributesHistory.get(Attr.PACKAGE).peek()); pack = null; } - if (name != null) { - final Intent intent = AndroidSettingFactory.intent(name, url); - attributesHistory.get(self).push(intent); + if (!names.isEmpty()) { + for (String name : names) { + if (urls.isEmpty()) urls.add(null); + for (String url : urls) { + logger.info("New Intent ({}, {})", name, url); + final Intent intent = AndroidSettingFactory.intent(name, url); + attributesHistory.get(self).push(intent); + } + } } else { throw new IllegalStateException("Error in parser implementation! The required attribute 'name' which should have been " + "defined in ACTION could not be retrieved. This should have been thrown before as it is a required attribute for " + @@ -623,13 +635,16 @@ public class AndroidManifestXMLReader { if ((attributesHistory.get(Attr.PACKAGE) != null ) && (!(attributesHistory.get(Attr.PACKAGE).isEmpty()))) { pack = (String) attributesHistory.get(Attr.PACKAGE).peek(); } else { + logger.warn("Empty Package {}", attributesHistory.get(Attr.PACKAGE).peek()); pack = null; } final String name = (String) attributesHistory.get(Attr.NAME).peek(); // TODO: Verify type! final Intent intent = AndroidSettingFactory.intent(pack, name, null); + logger.info("\tRegister: {}", intent); AndroidEntryPointManager.MANAGER.registerIntent(intent); for (Intent ovr: overrideTargets) { + logger.info("\tOverride: {} --> {}", ovr, intent); AndroidEntryPointManager.MANAGER.setOverride(ovr, intent); } } @@ -649,7 +664,9 @@ public class AndroidManifestXMLReader { if ((tag == Tag.UNIMPORTANT) || (unimportantDepth > 0)) { unimportantDepth++; } else { - final ParserItem handler = tag.getHandler(); + logger.debug("Handling {} made from {}", tag, qName); + + final ParserItem handler = tag.getHandler(); if (handler != null) { handler.enter(attrs); } diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidPreFlightChecks.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidPreFlightChecks.java index 7f3173a17..025c6837b 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidPreFlightChecks.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/util/AndroidPreFlightChecks.java @@ -225,7 +225,7 @@ public class AndroidPreFlightChecks { public boolean checkAllComponentsReuse() { boolean pass = true; - final IInstantiationBehavior behaviour = this.manager.getInstantiationBehavior(null); // XXX: This generates false positives without cha! + final IInstantiationBehavior behaviour = this.manager.getInstantiationBehavior(cha); // XXX: This generates false positives without cha! final List entrypoits = AndroidEntryPointManager.ENTRIES; for (AndroidEntryPoint ep : entrypoits) { diff --git a/com.ibm.wala.util/src/com/ibm/wala/fixedpoint/impl/BasicNullaryStatement.java b/com.ibm.wala.util/src/com/ibm/wala/fixedpoint/impl/BasicNullaryStatement.java index 3ba5bd047..b3740d93f 100644 --- a/com.ibm.wala.util/src/com/ibm/wala/fixedpoint/impl/BasicNullaryStatement.java +++ b/com.ibm.wala.util/src/com/ibm/wala/fixedpoint/impl/BasicNullaryStatement.java @@ -23,7 +23,7 @@ public class BasicNullaryStatement extends NullaryStatement */ private final NullaryOperator operator; - BasicNullaryStatement(T lhs, NullaryOperator operator) { + public BasicNullaryStatement(T lhs, NullaryOperator operator) { super(lhs); this.operator = operator; } @@ -35,4 +35,20 @@ public class BasicNullaryStatement extends NullaryStatement public NullaryOperator getOperator() { return operator; } + + /** + * Return a string representation of this object + * @return a string representation of this object + */ + @Override + public String toString() { + String result; + if (lhs == null) { + result = "null lhs"; + } else { + result = lhs.toString(); + } + result = getOperator() + " " + result; + return result; + } } diff --git a/com.ibm.wala.util/src/com/ibm/wala/util/config/FileOfClasses.java b/com.ibm.wala.util/src/com/ibm/wala/util/config/FileOfClasses.java index 91f7fe4ef..1b41971e7 100644 --- a/com.ibm.wala.util/src/com/ibm/wala/util/config/FileOfClasses.java +++ b/com.ibm.wala.util/src/com/ibm/wala/util/config/FileOfClasses.java @@ -43,6 +43,9 @@ public class FileOfClasses extends SetOfClasses implements Serializable { StringBuffer regex = null; String line; while ((line = is.readLine()) != null) { + + if (line.startsWith("#")) continue; + if (regex == null) { regex = new StringBuffer("(" + line + ")"); } else { diff --git a/com.ibm.wala.util/src/com/ibm/wala/util/graph/Acyclic.java b/com.ibm.wala.util/src/com/ibm/wala/util/graph/Acyclic.java index ab94e706c..f41dff704 100644 --- a/com.ibm.wala.util/src/com/ibm/wala/util/graph/Acyclic.java +++ b/com.ibm.wala.util/src/com/ibm/wala/util/graph/Acyclic.java @@ -11,8 +11,10 @@ package com.ibm.wala.util.graph; import java.util.Collection; +import java.util.HashSet; import java.util.Iterator; import java.util.Set; +import java.util.Stack; import com.ibm.wala.util.collections.HashSetFactory; import com.ibm.wala.util.intset.BasicNaturalRelation; @@ -40,6 +42,8 @@ public class Acyclic { return !it.hasNext(); } + public static final int THRESHOLD_FOR_NONRECURSIVE_DFS = 1000; + /** * Compute a relation R s.t. (i,j) \in R iff (i,j) is a backedge according to a DFS of a numbered graph starting from some root. * @@ -49,14 +53,23 @@ public class Acyclic { if (G == null) { throw new IllegalArgumentException("G is null"); } - BasicNaturalRelation result = new BasicNaturalRelation(); + + final BasicNaturalRelation result = new BasicNaturalRelation(); - Set visited = HashSetFactory.make(); - Set onstack = HashSetFactory.make(); - dfs(result, root, G, visited, onstack); + // for large methods (e.g. obfuscated library code as found in android libraries 'com.google.ads.ad.a([B[B)V') + // the recursive dfs can lead to a stack overflow error. + // for smaller methods the recursive solution seems to be faster, so we keep it. + if (G.getNumberOfNodes() <= THRESHOLD_FOR_NONRECURSIVE_DFS) { + final Set visited = HashSetFactory.make(); + final Set onstack = HashSetFactory.make(); + dfs(result, root, G, visited, onstack); + } else { + dfsNonRecursive(result, root, G); + } + return result; } - + private static void dfs(BasicNaturalRelation result, T root, NumberedGraph G, Set visited, Set onstack) { visited.add(root); onstack.add(root); @@ -74,6 +87,48 @@ public class Acyclic { onstack.remove(root); } + private static void dfsNonRecursive(final BasicNaturalRelation result, final T root, final NumberedGraph G) { + final Stack stack = new Stack(); + final Set stackSet = new HashSet(); + final Stack> stackIt = new Stack>(); + final Set finished = new HashSet(); + stack.push(root); + stackSet.add(root); + stackIt.push(G.getSuccNodes(root)); + + while (!stack.isEmpty()) { + final T current = stack.pop(); + stackSet.remove(current); + final Iterator currentIt = stackIt.pop(); + if (finished.contains(current)) { continue; } + + boolean isFinished = true; + while (isFinished && currentIt.hasNext() ) { + final T succ = currentIt.next(); + if (!finished.contains(succ)) { + if (succ == current || stackSet.contains(succ)) { + // found a backedge + final int src = G.getNumber(current); + final int dst = G.getNumber(succ); + result.add(src, dst); + } else { + stack.push(current); + stackSet.add(current); + stackIt.push(currentIt); + stack.push(succ); + stackSet.add(succ); + stackIt.push(G.getSuccNodes(succ)); + isFinished = false; + } + } + } + + if (isFinished) { + finished.add(current); + } + } + } + public static boolean hasIncomingBackEdges(Path p, NumberedGraph G, T root) { /* * TODO: pull out computeBackEdges, and pass in the backedge relation as a parameter to this call