diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ai/GetReceivers.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ai/GetReceivers.scala index 4e7e13d4a1..cafe6f2eb0 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ai/GetReceivers.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ai/GetReceivers.scala @@ -11,10 +11,10 @@ import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.analyses.Project import org.opalj.br.instructions.NEW import org.opalj.br.Method -import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.ai.domain.Origin import org.opalj.ai.domain.RecordDefUse import org.opalj.ai.fpcf.domain.L1DefaultDomainWithCFGAndDefUseAndSignatureRefinement +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey /** * Extracts the information about receivers of method calls. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo.scala index 573a17c587..8d28fa78f4 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/ImmutabilityAnalysisDemo.scala @@ -15,10 +15,10 @@ import org.opalj.br.analyses.Project import org.opalj.br.fpcf.properties.ClassImmutability import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.TypeImmutability -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.analyses.EagerClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldMutabilityAnalysis +import org.opalj.si.PropertyStoreKey /** * Determines the immutability of the classes of a project. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/InterProceduralEscapeAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/InterProceduralEscapeAnalysisDemo.scala index 52f5de6918..f2157a8811 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/InterProceduralEscapeAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/InterProceduralEscapeAnalysisDemo.scala @@ -26,10 +26,10 @@ import org.opalj.br.fpcf.properties.EscapeViaReturn import org.opalj.br.fpcf.properties.EscapeViaStaticField import org.opalj.br.fpcf.properties.GlobalEscape import org.opalj.br.fpcf.properties.NoEscape -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.ai.domain.l2.DefaultPerformInvocationsDomainWithCFGAndDefUse import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.common.DefinitionSite import org.opalj.tac.fpcf.analyses.escape.EagerInterProceduralEscapeAnalysis diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PurityAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PurityAnalysisDemo.scala index 2abc6af54c..034fb1add7 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PurityAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PurityAnalysisDemo.scala @@ -14,11 +14,11 @@ import org.opalj.br.analyses.Project import org.opalj.br.fpcf.properties.Pure import org.opalj.br.Field import org.opalj.br.DefinedMethod -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.analyses.EagerL0PurityAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldMutabilityAnalysis import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.Purity +import org.opalj.si.PropertyStoreKey /** * Runs the purity analysis including all analyses that may improve the overall result. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/SelfReferenceLeakageAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/SelfReferenceLeakageAnalysisDemo.scala index 4f14fcb5e5..175898fdcc 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/SelfReferenceLeakageAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/SelfReferenceLeakageAnalysisDemo.scala @@ -11,10 +11,10 @@ import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.analyses.Project import org.opalj.br.ClassFile -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.analyses.L0SelfReferenceLeakageAnalysis import org.opalj.br.fpcf.properties.DoesNotLeakSelfReference import org.opalj.br.fpcf.properties.SelfReferenceLeakage +import org.opalj.si.PropertyStoreKey /** * Runs the default self-reference leakage analysis. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/SimpleEscapeAnalysisDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/SimpleEscapeAnalysisDemo.scala index b33a2c3c1e..edb94072df 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/SimpleEscapeAnalysisDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/SimpleEscapeAnalysisDemo.scala @@ -27,9 +27,9 @@ import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.analyses.Project import org.opalj.br.analyses.VirtualFormalParameter -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.ai.fpcf.analyses.LazyL0BaseAIAnalysis +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey import org.opalj.tac.common.DefinitionSite import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis import org.opalj.tac.fpcf.analyses.TACAITransformer diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/UnnecessarySynchronizationAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/UnnecessarySynchronizationAnalysis.scala index 7c27d531a0..9361581c09 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/UnnecessarySynchronizationAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/UnnecessarySynchronizationAnalysis.scala @@ -13,8 +13,8 @@ import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.br.fpcf.properties.EscapeViaNormalAndAbnormalReturn -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey import org.opalj.tac.Assignment import org.opalj.tac.DVar import org.opalj.tac.MonitorEnter diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/FieldAndArrayUsageAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/FieldAndArrayUsageAnalysis.scala index 28480bca8d..3103da560e 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/FieldAndArrayUsageAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/FieldAndArrayUsageAnalysis.scala @@ -11,8 +11,6 @@ import org.opalj.fpcf.FinalP import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.properties.AtMost import org.opalj.br.fpcf.properties.EscapeInCallee import org.opalj.br.fpcf.properties.EscapeProperty @@ -21,6 +19,8 @@ import org.opalj.br.fpcf.properties.EscapeViaParameter import org.opalj.br.fpcf.properties.EscapeViaReturn import org.opalj.br.fpcf.properties.NoEscape import org.opalj.ai.fpcf.analyses.LazyL0BaseAIAnalysis +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis import org.opalj.tac.fpcf.analyses.TACAITransformer diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/BasicIFDSTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/BasicIFDSTaintAnalysis.scala deleted file mode 100644 index c4bdfa237a..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/BasicIFDSTaintAnalysis.scala +++ /dev/null @@ -1,554 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package tac -package fpcf -package analyses - -import java.io.File - -import scala.collection.immutable.ArraySeq -import scala.collection.immutable.ListSet - -import com.typesafe.config.ConfigValueFactory - -import org.opalj.log.LogContext -import org.opalj.util.PerformanceEvaluation -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.Seconds -import org.opalj.fpcf.PropertyKey -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.seq.PKESequentialPropertyStore -import org.opalj.fpcf.PropertyStoreContext -import org.opalj.bytecode.JRELibraryFolder -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.ObjectType -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.Context -import org.opalj.ai.domain.l0.PrimitiveTACAIDomain -import org.opalj.ai.domain.l1 -import org.opalj.ai.domain.l2 -import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.analyses.cg.CallGraphDeserializerScheduler -import org.opalj.tac.fpcf.properties.IFDSProperty -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation - -sealed trait Fact extends AbstractIFDSFact -case class Variable(index: Int) extends Fact -//case class ArrayElement(index: Int, element: Int) extends Fact -case class StaticField(classType: ObjectType, fieldName: String) extends Fact -case class InstanceField(index: Int, classType: ObjectType, fieldName: String) extends Fact -case class FlowFact(flow: ListSet[Context]) extends Fact { - // ListSet is only meant for VERY SMALL sets, but this seems to be ok here! - - override val hashCode: Int = { - // HERE, a foldLeft introduces a lot of overhead due to (un)boxing. - var r = 1 - flow.foreach(f => r = (r + f.hashCode()) * 31) - r - } -} -case object NullFact extends Fact with AbstractIFDSNullFact - -/** - * A simple IFDS taint analysis. - * - * @author Dominik Helm - * @author Mario Trageser - * @author Michael Eichberg - */ -class BasicIFDSTaintAnalysis private ( - implicit - val project: SomeProject -) extends AbstractIFDSAnalysis[Fact] { - - override val propertyKey: IFDSPropertyMetaInformation[Fact] = Taint - - override def createPropertyValue(result: Map[Statement, Set[Fact]]): IFDSProperty[Fact] = { - new Taint(result) - } - - override def normalFlow(stmt: Statement, succ: Statement, in: Set[Fact]): Set[Fact] = - stmt.stmt.astID match { - case Assignment.ASTID => - handleAssignment(stmt, stmt.stmt.asAssignment.expr, in) - - /*case ArrayStore.ASTID => - val store = stmt.stmt.asArrayStore - val definedBy = store.arrayRef.asVar.definedBy - val index = getConstValue(store.index, stmt.code) - if (isTainted(store.value, in)) - if (index.isDefined) // Taint known array index - // Instead of using an iterator, we are going to use internal iteration - // in ++ definedBy.iterator.map(ArrayElement(_, index.get)) - definedBy.foldLeft(in) { (c, n) => c + ArrayElement(n, index.get) } - else // Taint whole array if index is unknown - // Instead of using an iterator, we are going to use internal iteration: - // in ++ definedBy.iterator.map(Variable) - definedBy.foldLeft(in) { (c, n) => c + Variable(n) } - else in*/ - - case PutStatic.ASTID => - val put = stmt.stmt.asPutStatic - if (isTainted(put.value, in)) - in + StaticField(put.declaringClass, put.name) - else - in - - /*case PutField.ASTID => - val put = stmt.stmt.asPutField - if (isTainted(put.value, in)) in + StaticField(put.declaringClass, put.name) - else in*/ - case PutField.ASTID => - val put = stmt.stmt.asPutField - val definedBy = put.objRef.asVar.definedBy - if (isTainted(put.value, in)) - definedBy.foldLeft(in) { (in, defSite) => - in + InstanceField(defSite, put.declaringClass, put.name) - } - else - in - - case _ => in - } - - /** - * Returns true if the expression contains a taint. - */ - def isTainted(expr: Expr[V], in: Set[Fact]): Boolean = { - expr.isVar && in.exists { - case Variable(index) => expr.asVar.definedBy.contains(index) - //case ArrayElement(index, _) => expr.asVar.definedBy.contains(index) - case InstanceField(index, _, _) => expr.asVar.definedBy.contains(index) - case _ => false - } - } - - /** - * Returns the constant int value of an expression if it exists, None otherwise. - */ - /*def getConstValue(expr: Expr[V], code: Array[Stmt[V]]): Option[Int] = { - if (expr.isIntConst) Some(expr.asIntConst.value) - else if (expr.isVar) { - // TODO The following looks optimizable! - val constVals = expr.asVar.definedBy.iterator.map[Option[Int]] { idx => - if (idx >= 0) { - val stmt = code(idx) - if (stmt.astID == Assignment.ASTID && stmt.asAssignment.expr.isIntConst) - Some(stmt.asAssignment.expr.asIntConst.value) - else - None - } else None - }.toIterable - if (constVals.forall(option => option.isDefined && option.get == constVals.head.get)) - constVals.head - else None - } else None - }*/ - - def handleAssignment(stmt: Statement, expr: Expr[V], in: Set[Fact]): Set[Fact] = - expr.astID match { - case Var.ASTID => - // This path is not used if the representation is in standard SSA-like form. - // It is NOT optimized! - val newTaint = in.collect { - case Variable(index) if expr.asVar.definedBy.contains(index) => - Some(Variable(stmt.index)) - /*case ArrayElement(index, taintIndex) if expr.asVar.definedBy.contains(index) => - Some(ArrayElement(stmt.index, taintIndex))*/ - case _ => None - }.flatten - in ++ newTaint - - /*case ArrayLoad.ASTID => - val load = expr.asArrayLoad - if (in.exists { - // The specific array element may be tainted - case ArrayElement(index, taintedIndex) => - val element = getConstValue(load.index, stmt.code) - load.arrayRef.asVar.definedBy.contains(index) && - (element.isEmpty || taintedIndex == element.get) - // Or the whole array - case Variable(index) => load.arrayRef.asVar.definedBy.contains(index) - case _ => false - }) - in + Variable(stmt.index) - else - in*/ - - case GetStatic.ASTID => - val get = expr.asGetStatic - if (in.contains(StaticField(get.declaringClass, get.name))) - in + Variable(stmt.index) - else - in - - /*case GetField.ASTID => - val get = expr.asGetField - if (in.contains(StaticField(get.declaringClass, get.name))) - in + Variable(stmt.index) - else in*/ - case GetField.ASTID => - val get = expr.asGetField - if (in.exists { - // The specific field may be tainted - case InstanceField(index, _, taintedField) => - taintedField == get.name && get.objRef.asVar.definedBy.contains(index) - // Or the whole object - case Variable(index) => get.objRef.asVar.definedBy.contains(index) - case _ => false - }) - in + Variable(stmt.index) - else - in - - case _ => in - } - - override def callFlow( - stmt: Statement, - calleeContext: Context, - in: Set[Fact] - ): Set[Fact] = { - val callee = calleeContext.method - val call = asCall(stmt.stmt) - val allParams = call.allParams - if (callee.name == "sink") { - if (in.exists { - case Variable(index) => allParams.exists(p => p.asVar.definedBy.contains(index)) - case _ => false - }) { - println(s"Found flow: $stmt") - } - } else if (callee.name == "forName" && (callee.declaringClassType eq ObjectType.Class) && - callee.descriptor.parameterTypes == ArraySeq(ObjectType.String)) { - if (in.exists { - case Variable(index) => call.params.exists(p => p.asVar.definedBy.contains(index)) - case _ => false - }) { - println(s"Found flow: $stmt") - } - } - if (true || (callee.descriptor.returnType eq ObjectType.Class) || - (callee.descriptor.returnType eq ObjectType.Object) || - (callee.descriptor.returnType eq ObjectType.String)) { - var facts = Set.empty[Fact] - in.foreach { - case Variable(index) => // Taint formal parameter if actual parameter is tainted - allParams.iterator.zipWithIndex.foreach { - case (param, pIndex) if param.asVar.definedBy.contains(index) => - facts += Variable(paramToIndex(pIndex, !callee.definedMethod.isStatic)) - case _ => // Nothing to do - } - - /*case ArrayElement(index, taintedIndex) => - // Taint element of formal parameter if element of actual parameter is tainted - allParams.zipWithIndex.collect { - case (param, pIndex) if param.asVar.definedBy.contains(index) => - ArrayElement(paramToIndex(pIndex, !callee.definedMethod.isStatic), taintedIndex) - }*/ - - case InstanceField(index, declClass, taintedField) => - // Taint field of formal parameter if field of actual parameter is tainted - // Only if the formal parameter is of a type that may have that field! - allParams.iterator.zipWithIndex.foreach { - case (param, pIndex) if param.asVar.definedBy.contains(index) && - (paramToIndex(pIndex, !callee.definedMethod.isStatic) != -1 || - classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) => - facts += InstanceField(paramToIndex(pIndex, !callee.definedMethod.isStatic), declClass, taintedField) - case _ => // Nothing to do - } - - case sf: StaticField => - facts += sf - - case _ => // Nothing to do - } - facts - } else Set.empty - } - - override def returnFlow( - stmt: Statement, - calleeContext: Context, - exit: Statement, - succ: Statement, - in: Set[Fact] - ): Set[Fact] = { - val callee = calleeContext.method - if (callee.name == "source" && stmt.stmt.astID == Assignment.ASTID) - Set(Variable(stmt.index)) - else if (callee.name == "sanitize") - Set.empty - else { - val call = asCall(stmt.stmt) - val allParams = call.allParams - var flows: Set[Fact] = Set.empty - in.foreach { - /*case ArrayElement(index, taintedIndex) if index < 0 && index > -100 => - // Taint element of actual parameter if element of formal parameter is tainted - val param = - allParams(paramToIndex(index, !callee.definedMethod.isStatic)) - flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex))*/ - - case InstanceField(index, declClass, taintedField) if index < 0 && index > -255 => - // Taint field of actual parameter if field of formal parameter is tainted - val param = allParams(paramToIndex(index, !callee.definedMethod.isStatic)) - param.asVar.definedBy.foreach { defSite => - flows += InstanceField(defSite, declClass, taintedField) - } - - case sf: StaticField => - flows += sf - - case FlowFact(flow) => - val newFlow = flow + stmt.context - if (entryPoints.contains(exit.context)) { - //println(s"flow: "+newFlow.map(_.toJava).mkString(", ")) - } else { - flows += FlowFact(newFlow) - } - - case _ => - } - - // Propagate taints of the return value - if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { - val returnValue = exit.stmt.asReturnValue.expr.asVar - in.foreach { - case Variable(index) if returnValue.definedBy.contains(index) => - flows += Variable(stmt.index) - /*case ArrayElement(index, taintedIndex) if returnValue.definedBy.contains(index) => - ArrayElement(stmt.index, taintedIndex)*/ - case InstanceField(index, declClass, taintedField) if returnValue.definedBy.contains(index) => - flows += InstanceField(stmt.index, declClass, taintedField) - - case _ => // nothing to do - } - } - - flows - } - } - - /** - * Converts a parameter origin to the index in the parameter seq (and vice-versa). - */ - def paramToIndex(param: Int, includeThis: Boolean): Int = (if (includeThis) -1 else -2) - param - - override def callToReturnFlow(stmt: Statement, succ: Statement, in: Set[Fact]): Set[Fact] = { - val call = asCall(stmt.stmt) - if (call.name == "sanitize") { - // This branch is only used by the test cases and therefore NOT optimized! - in.filter { - case Variable(index) => - !(call.params ++ call.receiverOption).exists { p => - val definedBy = p.asVar.definedBy - definedBy.size == 1 && definedBy.contains(index) - } - case _ => true - } - } else if (call.name == "forName" && - (call.declaringClass eq ObjectType.Class) && - call.descriptor.parameterTypes == ArraySeq(ObjectType.String)) { - if (in.exists { - case Variable(index) => call.params.exists(p => p.asVar.definedBy.contains(index)) - case _ => false - }) { - /*if (entryPoints.contains(declaredMethods(stmt.method))) { - println(s"flow: "+stmt.method.toJava) - in - } else*/ - in + FlowFact(ListSet(stmt.context)) - } else { - in - } - } else { - in - } - } - - /** - * If forName is called, we add a FlowFact. - */ - override def nativeCall( - statement: Statement, calleeContext: Context, successor: Statement, in: Set[Fact] - ): Set[Fact] = { - /* val allParams = asCall(statement.stmt).allParams - if (statement.stmt.astID == Assignment.ASTID && in.exists { - case Variable(index) => - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) => true - case _ => false - } - /*case ArrayElement(index, _) => - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) => true - case _ => false - }*/ - case _ => false - }) Set(Variable(statement.index)) - else*/ Set.empty - } - - val entryPoints: Map[Context, Fact] = (for { - m <- p.allMethodsWithBody - if (m.isPublic || m.isProtected) && (m.descriptor.returnType == ObjectType.Object || m.descriptor.returnType == ObjectType.Class) - index <- m.descriptor.parameterTypes.zipWithIndex.collect { case (pType, index) if pType == ObjectType.String => index } - } //yield (declaredMethods(m), null) - yield typeIterator.newContext(declaredMethods(m)) -> Variable(-2 - index)).toMap - -} - -object BasicIFDSTaintAnalysis extends IFDSAnalysis[Fact] { - override def init(p: SomeProject, ps: PropertyStore) = new BasicIFDSTaintAnalysis()(p) - - override def property: IFDSPropertyMetaInformation[Fact] = Taint -} - -class Taint(val flows: Map[Statement, Set[Fact]]) extends IFDSProperty[Fact] { - - override type Self = Taint - - def key: PropertyKey[Taint] = Taint.key -} - -object Taint extends IFDSPropertyMetaInformation[Fact] { - override type Self = Taint - - val key: PropertyKey[Taint] = PropertyKey.create( - "TestTaint", - new Taint(Map.empty) - ) -} - -object BasicIFDSTaintAnalysisRunner { - - def main(args: Array[String]): Unit = { - if (args.contains("--help")) { - println("Potential parameters:") - println(" -cp=/Path/To/Project (to analyze the given project instead of the JDK)") - println(" -cg=/Path/To/CallGraph (to use a serialized call graph instead of a new RTA)") - println(" -seq (to use the SequentialPropertyStore)") - println(" -l2 (to use the l2 domain instead of the default l1 domain)") - println(" -primitive (to use the primitive l0 domain instead of the default l1 domain)") - println(" -delay (for a three seconds delay before the taint flow analysis is started)") - println(" -debug (to set the PropertyStore's debug flag)") - println(" -evalSchedulingStrategies (evaluates all available scheduling strategies)") - } - - if (args.contains("-debug")) { - PropertyStore.updateDebug(true) - } - - val cpArg = args.find(_.startsWith("-cp=")) - val p = - if (cpArg.isDefined) - Project(new File(cpArg.get.substring(4))) - else - Project(JRELibraryFolder) - - def evalProject(p: SomeProject): Seconds = { - if (args.contains("-l2")) { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None => - Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) - case Some(requirements) => - requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] - } - } else if (args.contains("-primitive")) { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None => Set(classOf[PrimitiveTACAIDomain]) - case Some(requirements) => requirements + classOf[PrimitiveTACAIDomain] - } - } else { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None => - Set(classOf[l1.DefaultDomainWithCFGAndDefUse[_]]) - case Some(requirements) => - requirements + classOf[l1.DefaultDomainWithCFGAndDefUse[_]] - } - } - - val ps = p.get(PropertyStoreKey) - val manager = p.get(FPCFAnalysesManagerKey) - var analysisTime: Seconds = Seconds.None - - val cgArg = args.find(_.startsWith("-cg=")) - PerformanceEvaluation.time { - if (cgArg.isDefined) - manager.runAll( - new CallGraphDeserializerScheduler(new File(cgArg.get.substring(4))) - ) - else - p.get(RTACallGraphKey) - } { t => println(s"CG took ${t.toSeconds}s.") } - - println("Start: "+new java.util.Date) - val analyses = time { - manager.runAll(BasicIFDSTaintAnalysis) - }(t => analysisTime = t.toSeconds)._2 - - val entryPoints = - analyses.collect { case (_, a: BasicIFDSTaintAnalysis) => a.entryPoints }.head - for { - e <- entryPoints - flows = ps(e, BasicIFDSTaintAnalysis.property.key) - fact <- flows.ub.asInstanceOf[IFDSProperty[Fact]].flows.values.flatten.toSet[Fact] - } { - fact match { - case FlowFact(flow) => - println(s"flow: "+flow.map(_.method.toJava).mkString(", ")) - case _ => - } - } - println(s"The analysis took $analysisTime.") - println( - ps.statistics.iterator.map(_.toString()).toList - .sorted - .mkString("PropertyStore Statistics:\n\t", "\n\t", "\n") - ) - - analysisTime - } - - p.getOrCreateProjectInformationKeyInitializationData( - PropertyStoreKey, - (context: List[PropertyStoreContext[AnyRef]]) => { - implicit val lg: LogContext = p.logContext - val ps = - if (args.contains("-seq")) - PKESequentialPropertyStore.apply(context: _*) - else - PKESequentialPropertyStore.apply(context: _*) // TODO exchange for a concurrent one once one exists - ps - } - ) - - if (args.contains("-delay")) { - println("Sleeping for three seconds.") - Thread.sleep(3000) - } - - if (args.contains("-evalSchedulingStrategies")) { - val results = for { - i <- 1 to 2 - strategy <- PKESequentialPropertyStore.Strategies - } yield { - println(s"Round: $i - $strategy") - val strategyValue = ConfigValueFactory.fromAnyRef(strategy) - val newConfig = - p.config.withValue(PKESequentialPropertyStore.TasksManagerKey, strategyValue) - val analysisTime = evalProject(Project.recreate(p, newConfig)) - (i, strategy, analysisTime) - org.opalj.util.gc() - } - println(results.mkString("AllResults:\n\t", "\n\t", "\n")) - } else { - evalProject(p) - } - } -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala new file mode 100644 index 0000000000..781cea9d58 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -0,0 +1,154 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.taint +/* TODO Fix as soon as backwards analysis is implemented +import org.opalj.br.analyses.SomeProject +import org.opalj.br.{DeclaredMethod, DefinedMethod, Method} +import org.opalj.fpcf.{EPS, FinalEP, PropertyStore} +import org.opalj.ifds.IFDSPropertyMetaInformation +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.JavaMethod +import org.opalj.tac.fpcf.analyses.ifds.old.taint.BackwardTaintProblem +import org.opalj.tac.fpcf.analyses.ifds.old._ +import org.opalj.tac.fpcf.analyses.ifds.taint._ +import org.opalj.tac.fpcf.properties.OldTaint + +import java.io.File + +/** + * A backward IFDS taint analysis, which tracks the String parameters of all methods of the rt.jar, + * * which are callable from outside the library, to calls of Class.forName. + * + * @author Mario Trageser + */ +class BackwardClassForNameTaintAnalysisScheduler private (implicit val project: SomeProject) + extends BackwardIFDSAnalysis(new BackwardClassForNameTaintProblem(project), OldTaint) + +class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProblem(p) { + + /** + * The string parameters of all public methods are entry points. + */ + override val entryPoints: Seq[(DeclaredMethod, TaintFact)] = + p.allProjectClassFiles.filter(classFile => + classFile.thisType.fqn == "java/lang/Class") + .flatMap(classFile => classFile.methods) + .filter(_.name == "forName") + .map(method => declaredMethods(method) -> Variable(-2)) + + /** + * There is no sanitizing in this analysis. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = false + + /** + * There is no sanitizing in this analysis. + */ + override protected def sanitizesParameter(call: DeclaredMethodJavaStatement, in: TaintFact): Boolean = false + + /** + * Do not perform unbalanced return for methods, which can be called from outside the library. + */ + override def shouldPerformUnbalancedReturn(source: (DeclaredMethod, TaintFact)): Boolean = { + super.shouldPerformUnbalancedReturn(source) && + (!canBeCalledFromOutside(source._1) || + // The source is callable from outside, but should create unbalanced return facts. + entryPoints.contains(source)) + } + + /** + * This analysis does not create FlowFacts at calls. + * Instead, FlowFacts are created at the start node of methods. + */ + override protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[TaintFact], + source: (DeclaredMethod, TaintFact)): Option[FlowFact] = None + + /** + * This analysis does not create FlowFacts at returns. + * Instead, FlowFacts are created at the start node of methods. + */ + protected def applyFlowFactFromCallee( + calleeFact: FlowFact, + source: (DeclaredMethod, TaintFact) + ): Option[FlowFact] = None + + /** + * If we analyzed a transitive caller of the sink, which is callable from outside the library, + * and a formal parameter is tainted, we create a FlowFact. + */ + override protected def createFlowFactAtBeginningOfMethod( + in: Set[TaintFact], + source: (DeclaredMethod, TaintFact) + ): Option[FlowFact] = { + if (source._2.isInstanceOf[UnbalancedReturnFact[TaintFact @unchecked]] && + canBeCalledFromOutside(source._1) && in.exists { + // index < 0 means, that it is a parameter. + case Variable(index) if index < 0 => true + case ArrayElement(index, _) if index < 0 => true + case InstanceField(index, _, _) if index < 0 => true + case _ => false + }) { + Some(FlowFact(currentCallChain(source).map(JavaMethod(_)))) + } else None + } +} + +object BackwardClassForNameTaintAnalysisScheduler extends IFDSAnalysisScheduler[TaintFact] { + + override def init(p: SomeProject, ps: PropertyStore): BackwardClassForNameTaintAnalysisScheduler = { + p.get(RTACallGraphKey) + new BackwardClassForNameTaintAnalysisScheduler()(p) + } + + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, TaintFact] = OldTaint +} + +class BackwardClassForNameTaintAnalysisRunner extends AbsractIFDSAnalysisRunner { + + override def analysisClass: BackwardClassForNameTaintAnalysisScheduler.type = BackwardClassForNameTaintAnalysisScheduler + + override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = { + val propertyKey = BackwardClassForNameTaintAnalysisScheduler.property.key + val flowFactsAtSources = ps.entities(propertyKey).collect { + case EPS((m: DefinedMethod, inputFact)) if canBeCalledFromOutside(m, ps) => + (m, inputFact) + }.flatMap(ps(_, propertyKey) match { + case FinalEP(_, OldTaint(result, _)) => + result.values.fold(Set.empty)((acc, facts) => acc ++ facts).filter { + case FlowFact(_) => true + case _ => false + } + case _ => Seq.empty + }) + for { + fact <- flowFactsAtSources + } { + fact match { + case FlowFact(flow) => println(s"flow: "+flow.asInstanceOf[Seq[Method]].map(_.toJava).mkString(", ")) + case _ => + } + } + } +} + +object BackwardClassForNameTaintAnalysisRunner { + def main(args: Array[String]): Unit = { + if (args.contains("--help")) { + println("Potential parameters:") + println(" -seq (to use the SequentialPropertyStore)") + println(" -l2 (to use the l2 domain instead of the default l1 domain)") + println(" -delay (for a three seconds delay before the taint flow analysis is started)") + println(" -debug (for debugging mode in the property store)") + println(" -evalSchedulingStrategies (evaluates all available scheduling strategies)") + println(" -f (Stores the average runtime to this file)") + } else { + val fileIndex = args.indexOf("-f") + new BackwardClassForNameTaintAnalysisRunner().run( + args.contains("-debug"), + args.contains("-l2"), + args.contains("-delay"), + args.contains("-evalSchedulingStrategies"), + if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None + ) + } + } +}*/ \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala new file mode 100644 index 0000000000..4ea139a192 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala @@ -0,0 +1,152 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.taint + +import org.opalj.br.analyses.{DeclaredMethodsKey, JavaProjectInformationKeys, SomeProject} +import org.opalj.br.fpcf.JavaIFDSAnalysisScheduler +import org.opalj.br.{DeclaredMethod, Method, ObjectType} +import org.opalj.fpcf.{FinalEP, PropertyBounds, PropertyStore} +import org.opalj.fpcf.ifds.IFDSAnalysis +import org.opalj.fpcf.ifds.IFDSProperty +import org.opalj.fpcf.ifds.IFDSPropertyMetaInformation +import org.opalj.si.PropertyStoreKey +import org.opalj.tac.cg.{RTACallGraphKey, TypeIteratorKey} +import org.opalj.tac.fpcf.analyses.ifds.taint.{FlowFact, ForwardTaintProblem, TaintFact, TaintProblem, Variable} +import org.opalj.tac.fpcf.analyses.ifds._ +import org.opalj.tac.fpcf.properties.{TACAI, Taint} +import org.opalj.tac.fpcf.properties.cg.Callers + +import java.io.File + +/** + * A forward IFDS taint analysis, which tracks the String parameters of all methods of the rt.jar, + * which are callable from outside the library, to calls of Class.forName. + * + * @author Dominik Helm + * @author Mario Trageser + * @author Michael Eichberg + */ +class ForwardClassForNameTaintAnalysis(project: SomeProject) + extends IFDSAnalysis()(project, new ForwardClassForNameTaintProblem(project), Taint) + +class ForwardClassForNameTaintProblem(project: SomeProject) + extends ForwardTaintProblem(project) with TaintProblem[Method, JavaStatement, TaintFact] { + private val propertyStore = project.get(PropertyStoreKey) + /** + * Returns all methods, that can be called from outside the library. + * The call graph must be computed, before this method may be invoked. + * + * @return All methods, that can be called from outside the library. + */ + protected def methodsCallableFromOutside: Set[DeclaredMethod] = { + declaredMethods.declaredMethods.filter(canBeCalledFromOutside).toSet + } + + /** + * Checks, if some `method` can be called from outside the library. + * The call graph must be computed, before this method may be invoked. + * + * @param method The method, which may be callable from outside. + * @return True, if `method` can be called from outside the library. + */ + protected def canBeCalledFromOutside(method: DeclaredMethod): Boolean = { + val FinalEP(_, callers) = propertyStore(method, Callers.key) + callers.hasCallersWithUnknownContext + } + /** + * The string parameters of all public methods are entry points. + */ + override def entryPoints: Seq[(Method, TaintFact)] = for { + m <- methodsCallableFromOutside.toSeq + if !m.definedMethod.isNative + index <- m.descriptor.parameterTypes.zipWithIndex.collect { + case (pType, index) if pType == ObjectType.String => index + } + } yield (m.definedMethod, Variable(-2 - index)) + + /** + * There is no sanitizing in this analysis. + */ + override protected def sanitizesReturnValue(callee: Method): Boolean = false + + /** + * There is no sanitizing in this analysis. + */ + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false + + /** + * This analysis does not create new taints on the fly. + * Instead, the string parameters of all public methods are tainted in the entry points. + */ + override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = + Set.empty + + /** + * Create a FlowFact, if Class.forName is called with a tainted variable for the first parameter. + */ + override protected def createFlowFact(callee: Method, call: JavaStatement, + in: TaintFact): Option[FlowFact] = { + if (isClassForName(declaredMethods(callee)) && in == Variable(-2)) + Some(FlowFact(Seq(JavaMethod(call.method)))) + else None + } + + /** + * Checks, if a `method` is Class.forName. + * + * @param method The method. + * @return True, if the method is Class.forName. + */ + private def isClassForName(method: DeclaredMethod): Boolean = + method.declaringClassType == ObjectType.Class && method.name == "forName" +} + +object ForwardClassForNameTaintAnalysisScheduler extends JavaIFDSAnalysisScheduler[TaintFact, Method, JavaStatement] { + + override def init(p: SomeProject, ps: PropertyStore) = new ForwardClassForNameTaintAnalysis(p) + + override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint + + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, TypeIteratorKey, PropertyStoreKey, RTACallGraphKey) + + override def uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) +} + +class ForwardClassForNameAnalysisRunner extends EvaluationRunner { + + override def analysisClass: ForwardClassForNameTaintAnalysisScheduler.type = ForwardClassForNameTaintAnalysisScheduler + + override def printAnalysisResults(analysis: IFDSAnalysis[?, ?, ?], ps: PropertyStore): Unit = + for { + e <- analysis.ifdsProblem.entryPoints + flows = ps(e, ForwardClassForNameTaintAnalysisScheduler.property.key) + fact <- flows.ub.asInstanceOf[IFDSProperty[JavaStatement, TaintFact]].flows.values.flatten.toSet[TaintFact] + } { + fact match { + case FlowFact(flow) => println(s"flow: "+flow.asInstanceOf[Set[Method]].map(_.toJava).mkString(", ")) + case _ => + } + } +} + +object ForwardClassForNameAnalysisRunner { + def main(args: Array[String]): Unit = { + if (args.contains("--help")) { + println("Potential parameters:") + println(" -seq (to use the SequentialPropertyStore)") + println(" -l2 (to use the l2 domain instead of the default l1 domain)") + println(" -delay (for a three seconds delay before the taint flow analysis is started)") + println(" -debug (for debugging mode in the property store)") + println(" -evalSchedulingStrategies (evaluates all available scheduling strategies)") + println(" -f (Stores the average runtime to this file)") + } else { + val fileIndex = args.indexOf("-f") + new ForwardClassForNameAnalysisRunner().run( + args.contains("-debug"), + args.contains("-l2"), + args.contains("-delay"), + args.contains("-evalSchedulingStrategies"), + if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None + ) + } + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/CallGraph.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/CallGraph.scala index 929b1d9466..4c1556fdf4 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/CallGraph.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/CallGraph.scala @@ -25,7 +25,6 @@ import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.analyses.VirtualFormalParameter -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.br.fpcf.properties.pointsto.AllocationSitePointsToSet @@ -37,6 +36,7 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.ai.Domain import org.opalj.ai.domain.RecordDefUse import org.opalj.fpcf.seq.PKESequentialPropertyStore +import org.opalj.si.PropertyStoreKey import org.opalj.tac.cg.AllocationSiteBasedPointsToCallGraphKey import org.opalj.tac.cg.CallGraphSerializer import org.opalj.tac.cg.CFA_1_0_CallGraphKey diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldLocality.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldLocality.scala index 16d251ee00..e407996ea6 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldLocality.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldLocality.scala @@ -11,7 +11,7 @@ import org.opalj.br.fpcf.properties.ExtensibleLocalFieldWithGetter import org.opalj.br.fpcf.properties.LocalField import org.opalj.br.fpcf.properties.LocalFieldWithGetter import org.opalj.br.fpcf.properties.NoLocalField -import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldMutability.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldMutability.scala index c9b2c11310..fc6b534dd9 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldMutability.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/FieldMutability.scala @@ -8,13 +8,13 @@ import java.net.URL import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication -import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.properties.DeclaredFinalField import org.opalj.br.fpcf.properties.EffectivelyFinalField import org.opalj.br.fpcf.properties.LazyInitializedField import org.opalj.br.fpcf.properties.NonFinalFieldByAnalysis import org.opalj.br.fpcf.properties.NonFinalFieldByLackOfInformation +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis import org.opalj.tac.fpcf.analyses.purity.LazyL2PurityAnalysis diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ImmutabilityAnalysis.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ImmutabilityAnalysis.scala index ba13b91235..d347974684 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ImmutabilityAnalysis.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ImmutabilityAnalysis.scala @@ -14,9 +14,9 @@ import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis import org.opalj.br.fpcf.properties.ClassImmutability import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.br.ObjectType -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.analyses.EagerClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerL0FieldMutabilityAnalysis +import org.opalj.si.PropertyStoreKey /** * Determines the immutability of the classes of a project. diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/PureVoidMethods.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/PureVoidMethods.scala index be727b6be1..91f3e524e8 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/PureVoidMethods.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/PureVoidMethods.scala @@ -10,8 +10,6 @@ import org.opalj.br.DefinedMethod import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectAnalysisApplication -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyStaticDataUsageAnalysis @@ -19,6 +17,8 @@ import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.properties.CompileTimePure import org.opalj.br.fpcf.properties.Pure import org.opalj.br.fpcf.properties.SideEffectFree +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.purity.EagerL2PurityAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Purity.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Purity.scala index f76c6b6fee..b258f72c12 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Purity.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Purity.scala @@ -7,11 +7,9 @@ import java.io.File import java.io.FileOutputStream import java.io.PrintWriter import java.util.Calendar - import com.typesafe.config.Config import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigValueFactory - import org.opalj.util.PerformanceEvaluation.time import org.opalj.util.Seconds import org.opalj.collection.immutable.IntTrieSet @@ -20,11 +18,6 @@ import org.opalj.fpcf.FinalEP import org.opalj.fpcf.FinalP import org.opalj.fpcf.PropertyStore import org.opalj.bytecode.JRELibraryFolder -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFAnalysisScheduler -import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyL0CompileTimeConstancyAnalysis import org.opalj.br.fpcf.analyses.LazyL0FieldMutabilityAnalysis @@ -58,9 +51,14 @@ import org.opalj.ai.domain.RecordDefUse import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.fpcf.analyses.EagerUnsoundPrematurelyReadFieldsAnalysis import org.opalj.br.fpcf.analyses.LazyUnsoundPrematurelyReadFieldsAnalysis +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler +import org.opalj.br.fpcf.JavaFPCFLazyAnalysisScheduler import org.opalj.fpcf.PropertyStoreContext +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey import org.opalj.fpcf.seq.PKESequentialPropertyStore import org.opalj.log.LogContext +import org.opalj.si.FPCFAnalysis +import org.opalj.si.PropertyStoreKey import org.opalj.tac.cg.CallGraphKey import org.opalj.tac.cg.AllocationSiteBasedPointsToCallGraphKey import org.opalj.tac.cg.CHACallGraphKey @@ -123,8 +121,8 @@ object Purity { cp: File, projectDir: Option[String], libDir: Option[String], - analysis: FPCFLazyAnalysisScheduler, - support: List[FPCFAnalysisScheduler], + analysis: JavaFPCFLazyAnalysisScheduler, + support: List[JavaFPCFAnalysisScheduler], domain: Class[_ <: Domain with RecordDefUse], configurationName: Option[String], schedulingStrategy: Option[String], @@ -520,8 +518,8 @@ object Purity { return ; } - var support: List[FPCFAnalysisScheduler] = Nil - val analysis: FPCFLazyAnalysisScheduler = analysisName match { + var support: List[JavaFPCFAnalysisScheduler] = Nil + val analysis: JavaFPCFLazyAnalysisScheduler = analysisName match { case Some("L0") => LazyL0PurityAnalysis case Some("L1") => LazyL1PurityAnalysis diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ReturnValueFreshness.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ReturnValueFreshness.scala index bb28187488..ade4f842dd 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ReturnValueFreshness.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ReturnValueFreshness.scala @@ -13,7 +13,7 @@ import org.opalj.br.fpcf.properties.FreshReturnValue import org.opalj.br.fpcf.properties.Getter import org.opalj.br.fpcf.properties.NoFreshReturnValue import org.opalj.br.fpcf.properties.PrimitiveReturnValue -import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.escape.EagerReturnValueFreshnessAnalysis import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ThrownExceptions.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ThrownExceptions.scala index c863d2e777..0f0c13896f 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ThrownExceptions.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/ThrownExceptions.scala @@ -16,10 +16,10 @@ import org.opalj.br.analyses.ProjectAnalysisApplication import org.opalj.br.analyses.Project import org.opalj.br.collection.TypesSet import org.opalj.br.fpcf.properties.{ThrownExceptions => ThrownExceptionsProperty} -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.analyses.EagerL1ThrownExceptionsAnalysis import org.opalj.br.fpcf.analyses.LazyVirtualMethodThrownExceptionsAnalysis +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey /** * Prints out the information about the exceptions thrown by methods. diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/UnusedResults.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/UnusedResults.scala index a0d23bc70f..ad12df0f62 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/UnusedResults.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/UnusedResults.scala @@ -23,14 +23,14 @@ import org.opalj.br.fpcf.properties.{Purity => PurityProperty} import org.opalj.br.fpcf.properties.VirtualMethodPurity.VCompileTimePure import org.opalj.br.fpcf.properties.VirtualMethodPurity.VPure import org.opalj.br.fpcf.properties.VirtualMethodPurity.VSideEffectFree -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.analyses.LazyClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.LazyTypeImmutabilityAnalysis import org.opalj.br.fpcf.properties.CompileTimePure import org.opalj.br.fpcf.properties.Pure import org.opalj.br.fpcf.properties.SideEffectFree import org.opalj.br.fpcf.properties.VirtualMethodPurity +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey import org.opalj.tac.fpcf.analyses.purity.EagerL2PurityAnalysis import org.opalj.tac.DUVar import org.opalj.tac.ExprStmt diff --git a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Values.scala b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Values.scala index fcf1fa0616..01fd5c4c16 100644 --- a/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Values.scala +++ b/DEVELOPING_OPAL/tools/src/main/scala/org/opalj/support/info/Values.scala @@ -7,7 +7,6 @@ import org.opalj.log.OPALLogger import org.opalj.util.PerformanceEvaluation import org.opalj.fpcf.Entity import org.opalj.fpcf.EPS -import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.Method import org.opalj.br.analyses.BasicReport import org.opalj.br.analyses.ProjectAnalysisApplication @@ -15,6 +14,7 @@ import org.opalj.br.analyses.Project import org.opalj.br.Field import org.opalj.ai.fpcf.properties.FieldValue import org.opalj.ai.fpcf.properties.MethodReturnValue +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey /** * Computes information regarding the values stored in fields and returned by methods. diff --git a/DEVELOPING_OPAL/validate/src/it/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphIntegrationTest.scala b/DEVELOPING_OPAL/validate/src/it/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphIntegrationTest.scala index c138314388..b8edbbc4cd 100644 --- a/DEVELOPING_OPAL/validate/src/it/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphIntegrationTest.scala +++ b/DEVELOPING_OPAL/validate/src/it/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphIntegrationTest.scala @@ -26,8 +26,8 @@ import org.opalj.br.analyses.cg.ClosedPackagesKey import org.opalj.br.analyses.cg.IsOverridableMethodKey import org.opalj.br.analyses.cg.TypeExtensibilityKey import org.opalj.br.analyses.Project +import org.opalj.si.PropertyStoreKey import org.opalj.tac.fpcf.properties.cg.NoCallers -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.tac.cg.CallGraph import org.opalj.tac.cg.CHACallGraphKey import org.opalj.tac.cg.RTACallGraphKey diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java new file mode 100644 index 0000000000..e3b5427b35 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java @@ -0,0 +1,417 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.taint; + +import org.opalj.fpcf.properties.taint.BackwardFlowPath; +import org.opalj.fpcf.properties.taint.ForwardFlowPath; + +/** + * @author Mario Trageser + */ +public class TaintAnalysisTestClass { + + private static int staticField; + + private int instanceField; + + @ForwardFlowPath({"callChainsAreConsidered", "passToSink"}) + @BackwardFlowPath({"callChainsAreConsidered", "passToSink", "sink"}) + public void callChainsAreConsidered() { + passToSink(source()); + } + + @ForwardFlowPath({"returnEdgesFromInstanceMethodsArePresent"}) + @BackwardFlowPath({"returnEdgesFromInstanceMethodsArePresent", "sink"}) + public void returnEdgesFromInstanceMethodsArePresent() { + sink(callSourcePublic()); + } + + @ForwardFlowPath({"returnEdgesFromPrivateMethodsArePresent"}) + @BackwardFlowPath({"returnEdgesFromPrivateMethodsArePresent", "sink"}) + public void returnEdgesFromPrivateMethodsArePresent() { + sink(callSourceNonStatic()); + } + + @ForwardFlowPath({"multiplePathsAreConsidered_1", "indirectPassToSink", "passToSink"}) + @BackwardFlowPath({"multiplePathsAreConsidered_1", "indirectPassToSink", "passToSink", "sink"}) + public void multiplePathsAreConsidered_1() { + int i = source(); + passToSink(i); + indirectPassToSink(i); + } + + @ForwardFlowPath({"multiplePathsAreConsidered_2", "passToSink"}) + @BackwardFlowPath({"multiplePathsAreConsidered_2", "passToSink", "sink"}) + public void multiplePathsAreConsidered_2() { + int i = source(); + passToSink(i); + indirectPassToSink(i); + } + + @ForwardFlowPath({"ifEdgesAreConsidered"}) + @BackwardFlowPath({"ifEdgesAreConsidered", "sink"}) + public void ifEdgesAreConsidered() { + int i; + if(Math.random() < .5) { + i = source(); + } else { + i = 0; + } + sink(i); + } + + @ForwardFlowPath({"elseEdgesAreConsidered"}) + @BackwardFlowPath({"elseEdgesAreConsidered", "sink"}) + public void elseEdgesAreConsidered() { + int i; + if(Math.random() < .5) { + i = 0; + } else { + i = source(); + } + sink(i); + } + + @ForwardFlowPath({"forLoopsAreConsidered"}) + @BackwardFlowPath({"forLoopsAreConsidered", "sink"}) + public void forLoopsAreConsidered() { + int[] arr = new int[2]; + for(int i = 0; i < arr.length; i++) { + sink(arr[0]); + arr[i] = source(); + } + } + + @ForwardFlowPath("returnOfIdentityFunctionIsConsidered") + @BackwardFlowPath({"returnOfIdentityFunctionIsConsidered", "sink"}) + public void returnOfIdentityFunctionIsConsidered() { + sink(identity(source())); + } + + @ForwardFlowPath({"summaryEdgesOfRecursiveFunctionsAreComputedCorrectly"}) + @BackwardFlowPath({"summaryEdgesOfRecursiveFunctionsAreComputedCorrectly", "sink"}) + public void summaryEdgesOfRecursiveFunctionsAreComputedCorrectly() { + sink(recursion(0)); + } + + public int recursion(int i) { + return i == 0 ? recursion(source()) : i; + } + + @ForwardFlowPath({"codeInCatchNodesIsConsidered"}) + @BackwardFlowPath({"codeInCatchNodesIsConsidered", "sink"}) + public void codeInCatchNodesIsConsidered() { + int i = source(); + try { + throw new RuntimeException(); + } catch(RuntimeException e) { + sink(i); + } + } + + @ForwardFlowPath({"codeInFinallyNodesIsConsidered"}) + @BackwardFlowPath({"codeInFinallyNodesIsConsidered", "sink"}) + public void codeInFinallyNodesIsConsidered() { + int i = 1; + try { + throw new RuntimeException(); + } catch(RuntimeException e) { + i = source(); + } finally { + sink(i); + } + } + + @ForwardFlowPath({"unaryExpressionsPropagateTaints"}) + @BackwardFlowPath({"unaryExpressionsPropagateTaints", "sink"}) + public void unaryExpressionsPropagateTaints() { + sink(-source()); + } + + @ForwardFlowPath({"binaryExpressionsPropagateTaints"}) + @BackwardFlowPath({"binaryExpressionsPropagateTaints", "sink"}) + public void binaryExpressionsPropagateTaints() { + sink(source() + 1); + } + + @ForwardFlowPath({"arrayLengthPropagatesTaints"}) + @BackwardFlowPath({"arrayLengthPropagatesTaints", "sink"}) + public void arrayLengthPropagatesTaints() { + sink(new Object[source()].length); + } + + @ForwardFlowPath({"singleArrayIndicesAreTainted_1"}) + @BackwardFlowPath({"singleArrayIndicesAreTainted_1", "sink"}) + public void singleArrayIndicesAreTainted_1() { + int[] arr = new int[2]; + arr[0] = source(); + sink(arr[0]); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void singleArrayIndicesAreTainted_2() { + int[] arr = new int[2]; + arr[0] = source(); + sink(arr[1]); + } + + @ForwardFlowPath({"wholeArrayTaintedIfIndexUnknown"}) + @BackwardFlowPath({"wholeArrayTaintedIfIndexUnknown", "sink"}) + public void wholeArrayTaintedIfIndexUnknown() { + int[] arr = new int[2]; + arr[(int) (Math.random() * 2)] = source(); + sink(arr[0]); + } + + @ForwardFlowPath({"arrayElementTaintsArePropagatedToCallee_1", "passFirstArrayElementToSink"}) + @BackwardFlowPath({"arrayElementTaintsArePropagatedToCallee_1", "passFirstArrayElementToSink", + "sink"}) + public void arrayElementTaintsArePropagatedToCallee_1() { + int[] arr = new int[2]; + arr[0] = source(); + passFirstArrayElementToSink(arr); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void arrayElementTaintsArePropagatedToCallee_2() { + int[] arr = new int[2]; + arr[1] = source(); + passFirstArrayElementToSink(arr); + } + + @ForwardFlowPath({"arrayElementTaintsArePropagatedBack_1", "passFirstArrayElementToSink"}) + @BackwardFlowPath({"arrayElementTaintsArePropagatedBack_1", "passFirstArrayElementToSink", + "sink"}) + public void arrayElementTaintsArePropagatedBack_1() { + int[] arr = new int[2]; + taintRandomElement(arr); + passFirstArrayElementToSink(arr); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void arrayElementTaintsArePropagatedBack_2() { + int[] arr = new int[2]; + taintFirstElement(arr); + sink(arr[1]); + } + + @ForwardFlowPath({"callerParameterIsTaintedIfCalleeTaintsFormalParameter", + "passFirstArrayElementToSink"}) + @BackwardFlowPath({"callerParameterIsTaintedIfCalleeTaintsFormalParameter", + "passFirstArrayElementToSink", "sink"}) + public void callerParameterIsTaintedIfCalleeTaintsFormalParameter() { + int[] arr = new int[2]; + taintRandomElement(arr); + passFirstArrayElementToSink(arr); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void taintDisappearsWhenReassigning() { + int[] arr = new int[2]; + arr[0] = source(); + arr[0] = 0; + sink(arr[0]); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void nativeMethodsCanBeHandeled() { + int j = nativeMethod(0); + sink(j); + } + + @ForwardFlowPath({"returnValueOfNativeMethodIsTainted"}) + @BackwardFlowPath({"returnValueOfNativeMethodIsTainted", "sink"}) + public void returnValueOfNativeMethodIsTainted() { + sink(nativeMethod(source())); + } + + @ForwardFlowPath({"analysisUsesCallGraph_1"}) + @BackwardFlowPath({"analysisUsesCallGraph_1", "sink"}) + public void analysisUsesCallGraph_1() { + A a = new B(); + sink(a.get()); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void analysisUsesCallGraph_2() { + A a = new C(); + sink(a.get()); + } + + @ForwardFlowPath({"analysisUsesCallGraph_3"}) + @BackwardFlowPath({"analysisUsesCallGraph_3", "sink"}) + public void analysisUsesCallGraph_3() { + A a; + if(Math.random() < .5) + a = new B(); + else + a = new C(); + sink(a.get()); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void sanitizeRemovesTaint() { + sink(sanitize(source())); + } + + @ForwardFlowPath({"instanceFieldsAreTainted"}) + @BackwardFlowPath({"instanceFieldsAreTainted", "sink"}) + public void instanceFieldsAreTainted() { + instanceField = source(); + sink(instanceField); + } + + @ForwardFlowPath({"staticFieldsAreTainted"}) + @BackwardFlowPath({"staticFieldsAreTainted", "sink"}) + public void staticFieldsAreTainted() { + staticField = source(); + sink(staticField); + } + + @ForwardFlowPath({"fieldTaintsArePassed", "passWrappedValueToSink"}) + @BackwardFlowPath({"fieldTaintsArePassed", "passWrappedValueToSink", "sink"}) + public void fieldTaintsArePassed() { + passWrappedValueToSink(new Wrapper(source())); + } + + @ForwardFlowPath({"fieldTaintsAreAppliedInReturnFlow"}) + @BackwardFlowPath({"fieldTaintsAreAppliedInReturnFlow", "sink"}) + public void fieldTaintsAreAppliedInReturnFlow() { + sink(createTaintedWrapper().field); + } + + @ForwardFlowPath({"fieldTaintsOfParametersAreAppliedInReturnFlow"}) + @BackwardFlowPath({"fieldTaintsOfParametersAreAppliedInReturnFlow", "sink"}) + public void fieldTaintsOfParametersAreAppliedInReturnFlow() { + Wrapper wrapper = new Wrapper(); + taintWrappedValue(wrapper); + sink(wrapper.field); + } + + @ForwardFlowPath({"fieldTaintsAreConsideredInComputations"}) + @BackwardFlowPath({"fieldTaintsAreConsideredInComputations", "sink"}) + public void fieldTaintsAreConsideredInComputations() { + Wrapper wrapper = new Wrapper(source()); + sink(wrapper.field + 1); + } + + //TODO Tests für statische Felder über Methodengrenzen + + //Does not work, because we do not know which exceptions cannot be thrown. + /*@ForwardFlowPath({}) + public void onlyThrowableExceptionsAreConsidered() { + int i = 0; + try { + divide(1, i); + } catch(IllegalArgumentException e) { + i = source(); + } + sink(i); + }*/ + + //Does not work, because the analysis does not know that there is only one iteration. + /*@ForwardFlowPath({}) + public void iterationCountIsConsidered() { + int[] arr = new int[2]; + for(int i = 0; i < 1; i++) { + sink(arr[0]); + arr[i] = source(); + } + }*/ + + public int callSourcePublic() { + return source(); + } + + private int callSourceNonStatic() { + return source(); + } + + private static void passToSink(int i) { + sink(i); + } + + private void indirectPassToSink(int i) { + passToSink(i); + } + + private void passFirstArrayElementToSink(int[] arr) { + sink(arr[0]); + } + + private void taintRandomElement(int[] arr) { + arr[Math.random() < .5 ? 0 : 1] = source(); + } + + private void taintFirstElement(int[] arr) { + arr[0] = source(); + } + + private native int nativeMethod(int i); + + private int identity(int i) {return i;} + + private void passWrappedValueToSink(Wrapper wrapper) { + sink(wrapper.field); + } + + private Wrapper createTaintedWrapper() { + return new Wrapper(source()); + } + + private void taintWrappedValue(Wrapper wrapper) { + wrapper.field = source(); + } + + //If it throws an exception, it is only an arithmetic exception. + private static int divide(int i, int j) { + return i / j; + } + + public static int source() { + return 1; + } + + private static int sanitize(int i) {return i;} + + private static void sink(int i) { + System.out.println(i); + } + +} + +abstract class A { + abstract int get(); +} + +class B extends A { + @Override + int get() { + return TaintAnalysisTestClass.source(); + } +} + +class C extends A { + @Override + int get() { + return 0; + } +} + +class Wrapper { + public int field; + + Wrapper() { + + } + + Wrapper(int field) { + this.field = field; + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/vta/VTATestClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/vta/VTATestClass.java new file mode 100644 index 0000000000..ae4c77876c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/vta/VTATestClass.java @@ -0,0 +1,126 @@ +package org.opalj.fpcf.fixtures.vta; + +import org.opalj.fpcf.properties.vta.ExpectedCallee; +import org.opalj.fpcf.properties.vta.ExpectedType; + +public class VTATestClass { + + @ExpectedType.List({ @ExpectedType(lineNumber = 10, value = "B", upperBound = false) }) + public void instantiationsAreConsidered() { + A a = new B(); + } + + @ExpectedType.List({ + @ExpectedType(lineNumber = 17, value = "B", upperBound = false), + @ExpectedType(lineNumber = 18, value = "C", upperBound = false) }) + public void factsAreRemembered() { + A x = new B(); + A y = new C(); + } + + @ExpectedType.List({ + @ExpectedType(lineNumber = 25, value = "B[]", upperBound = false), + @ExpectedType(lineNumber = 25, value = "C[]", upperBound = false) }) + public void arrayTypesAreConsidered_1() { + A[] a = new A[2]; + a[0] = new B(); + a[1] = new C(); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 33, value = "B", upperBound = false)}) + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 34, value = "B", upperBound = false)}) + public void callTargetsAreConsidered() { + A a = new B(); + a.doIt(); + } + + @ExpectedType.List({ + @ExpectedType(lineNumber = 42, value = "B", upperBound = false), + @ExpectedType(lineNumber = 43, value = "C", upperBound = false) }) + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 45, value = "B", upperBound = false)}) + public void variableAssignmentsAreConsidered_1() { + A b = new B(); + A c = new C(); + if(Math.random() < .5) b = c; + b.doIt(); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 51, value = "B[]", upperBound = false)}) + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 52, value = "B", upperBound = false)}) + public void arrayLoadsAreConsidered() { + A[] a = new A[] {new B()}; + a[0].doIt(); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 57, value = "B", upperBound = false)}) + public void typesOfParametersArePassed() { + A a = new B(); + typesOfParametersArePassed_callee(a); + } + + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 63, value = "B", upperBound = false)}) + private void typesOfParametersArePassed_callee(A a) { + a.doIt(); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 69, value = "B", upperBound = false)}) + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 70, value = "B", upperBound = false)}) + public void returnFlowIsConsidered() { + A a = returnB(); + a.doIt(); + } + + private A returnB() { + return new B(); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 80, value = "A", upperBound = true)}) + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 81, value = "A", upperBound = true)}) + public void nativeCallsAreConsidered() { + A a = nativeMethod(); + a.doIt(); + } + + public native A nativeMethod(); + + @ExpectedType.List({@ExpectedType(lineNumber = 88, value = "String", upperBound = true)}) + public void staticFieldReadsAreConsidered() { + Object o = A.STATIC_FIELD; + System.out.println(o); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 94, value = "String", upperBound = true)}) + public void fieldReadsAreConsidered() { + Object o = new B().field; + System.out.println(o); + } + + @ExpectedType.List({ @ExpectedType(lineNumber = 100, value = "B", upperBound = false) }) + protected void protectedMethodsAreConsidered() { + A a = new B(); + } + +} + +abstract class A { + + public static String STATIC_FIELD = "STATIC_FIELD"; + + public String field = "field"; + + public abstract void doIt(); +} + +class B extends A { + @Override + public void doIt() { + System.out.println("B"); + } +} + +class C extends A { + @Override + public void doIt() { + System.out.println("C"); + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeInCallee.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeInCallee.java index cc98606a60..b8c643c9e9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeInCallee.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeInCallee.java @@ -1,8 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.si.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaAbnormalReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaAbnormalReturn.java index 1d1d75281a..b3605d3d86 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaAbnormalReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaAbnormalReturn.java @@ -1,8 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; + import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.si.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaNormalAndAbnormalReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaNormalAndAbnormalReturn.java index d46fb71df9..1621e1f51e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaNormalAndAbnormalReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaNormalAndAbnormalReturn.java @@ -1,8 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.si.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameter.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameter.java index 07de41a74e..a6cb60d256 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameter.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameter.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndAbnormalReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndAbnormalReturn.java index a70e6cec3b..e7310a4dbb 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndAbnormalReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndAbnormalReturn.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndNormalAndAbnormalReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndNormalAndAbnormalReturn.java index 55a01af464..3c5857ddf8 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndNormalAndAbnormalReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndNormalAndAbnormalReturn.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndReturn.java index 7536193d6d..8f2c0c1ad8 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaParameterAndReturn.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaReturn.java index 39550379e8..b3bd51f20e 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostEscapeViaReturn.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostNoEscape.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostNoEscape.java index 7e2f3cf7a8..77d262d8ed 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostNoEscape.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/AtMostNoEscape.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeInCallee.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeInCallee.java index 3b806c113e..f43bb6a6bd 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeInCallee.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeInCallee.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaAbnormalReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaAbnormalReturn.java index fa5dcd3389..fcbc11b980 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaAbnormalReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaAbnormalReturn.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; import org.opalj.fpcf.properties.PropertyValidator; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaHeapObject.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaHeapObject.java index 36aab97ea2..dc30e22d6a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaHeapObject.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaHeapObject.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; import org.opalj.fpcf.properties.PropertyValidator; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaNormalAndAbnormalReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaNormalAndAbnormalReturn.java index d2c8e41987..4b63eea3b4 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaNormalAndAbnormalReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaNormalAndAbnormalReturn.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; import org.opalj.fpcf.properties.PropertyValidator; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameter.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameter.java index 6085770000..0fefd6b983 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameter.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameter.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndAbnormalReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndAbnormalReturn.java index c775a9cd87..f9c2124bf2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndAbnormalReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndAbnormalReturn.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; import org.opalj.fpcf.properties.PropertyValidator; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndNormalAndAbnormalReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndNormalAndAbnormalReturn.java index 82d6064570..a449e90982 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndNormalAndAbnormalReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndNormalAndAbnormalReturn.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; import org.opalj.fpcf.properties.PropertyValidator; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndReturn.java index 74a0f143b3..8750027774 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaParameterAndReturn.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; import org.opalj.fpcf.properties.PropertyValidator; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaReturn.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaReturn.java index cda3f28190..7d29a69c1c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaReturn.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaReturn.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaStaticField.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaStaticField.java index 3cb408a19e..364db25ee6 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaStaticField.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/EscapeViaStaticField.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/GlobalEscape.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/GlobalEscape.java index d5981083e9..1f5514069a 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/GlobalEscape.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/GlobalEscape.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; import org.opalj.fpcf.properties.PropertyValidator; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/NoEscape.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/NoEscape.java index 4597f947c8..7e67446bf0 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/NoEscape.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/escape/NoEscape.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.escape; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.escape.InterProceduralEscapeAnalysis; import org.opalj.tac.fpcf.analyses.escape.SimpleEscapeAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/DeclaredFinal.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/DeclaredFinal.java index 5ced6e5306..dedb4a944d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/DeclaredFinal.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/DeclaredFinal.java @@ -28,7 +28,7 @@ */ package org.opalj.fpcf.properties.field_mutability; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.br.fpcf.analyses.L0FieldMutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/EffectivelyFinal.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/EffectivelyFinal.java index 34c31ef187..5dfd8852a4 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/EffectivelyFinal.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/EffectivelyFinal.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.field_mutability; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.br.fpcf.analyses.L0FieldMutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/LazyInitialized.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/LazyInitialized.java index 85361f9449..c0106dea11 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/LazyInitialized.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/LazyInitialized.java @@ -28,7 +28,7 @@ */ package org.opalj.fpcf.properties.field_mutability; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.L2FieldMutabilityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/NonFinal.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/NonFinal.java index 23a8f5dfe1..ee41b93b2d 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/NonFinal.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/field_mutability/NonFinal.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.field_mutability; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.br.fpcf.analyses.L0FieldMutabilityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.L1FieldMutabilityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/CompileTimePure.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/CompileTimePure.java index 684c1b191c..e61e828376 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/CompileTimePure.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/CompileTimePure.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/ContextuallyPure.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/ContextuallyPure.java index 9c0bcad1a6..2768cdcccd 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/ContextuallyPure.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/ContextuallyPure.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/ContextuallySideEffectFree.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/ContextuallySideEffectFree.java index 1139af3058..b3dd3720e9 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/ContextuallySideEffectFree.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/ContextuallySideEffectFree.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificContextuallyPure.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificContextuallyPure.java index 1e974bb43f..adcfb4e2dc 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificContextuallyPure.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificContextuallyPure.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificContextuallySideEffectFree.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificContextuallySideEffectFree.java index 8fd99000ed..338d05ce51 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificContextuallySideEffectFree.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificContextuallySideEffectFree.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificPure.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificPure.java index d92c7b2d41..1d3e31ce95 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificPure.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificPure.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.purity.L1PurityAnalysis; import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificSideEffectFree.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificSideEffectFree.java index 880710173b..ad8d3e5ab1 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificSideEffectFree.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/DomainSpecificSideEffectFree.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.purity.L1PurityAnalysis; import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/EP.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/EP.java index 5bc6184d12..2710abd93c 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/EP.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/EP.java @@ -1,6 +1,6 @@ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; public @interface EP { diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/Impure.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/Impure.java index 26fb9addd3..170fc5c6d2 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/Impure.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/Impure.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.br.fpcf.analyses.L0PurityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.purity.L1PurityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/Pure.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/Pure.java index fffb578624..44dcb31c95 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/Pure.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/Pure.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.br.fpcf.analyses.L0PurityAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.purity.L1PurityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/SideEffectFree.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/SideEffectFree.java index 65975d35fd..9a1348620f 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/SideEffectFree.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/purity/SideEffectFree.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.purity; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import org.opalj.tac.fpcf.analyses.purity.L1PurityAnalysis; import org.opalj.tac.fpcf.analyses.purity.L2PurityAnalysis; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/BackwardFlowPath.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/BackwardFlowPath.java new file mode 100644 index 0000000000..9bc1fe1763 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/BackwardFlowPath.java @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.taint; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * @author Mario Trageser + */ +// TODO Enable once Backward analysis is implemented +//@PropertyValidator(key = BackwardFlowPath.PROPERTY_VALIDATOR_KEY, validator = BackwardFlowPathMatcher.class) +@Target(ElementType.METHOD) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface BackwardFlowPath { + + String PROPERTY_VALIDATOR_KEY = "BackwardFlowPath"; + + String[] value(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/ForwardFlowPath.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/ForwardFlowPath.java new file mode 100644 index 0000000000..9da140e6d4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/ForwardFlowPath.java @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.taint; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.taint.ForwardFlowPathMatcher; + +import java.lang.annotation.*; + +/** + * @author Mario Trageser + */ +@PropertyValidator(key = ForwardFlowPath.PROPERTY_VALIDATOR_KEY, validator = ForwardFlowPathMatcher.class) +@Target(ElementType.METHOD) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ForwardFlowPath { + + String PROPERTY_VALIDATOR_KEY = "ForwardFlowPath"; + + String[] value(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/DoesNotThrowException.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/DoesNotThrowException.java index 6bd51f3a9d..6cb1cf1ac7 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/DoesNotThrowException.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/DoesNotThrowException.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.thrown_exceptions; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import java.lang.annotation.Documented; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/ExpectedExceptions.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/ExpectedExceptions.java index 1983572539..c3585d3f86 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/ExpectedExceptions.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/ExpectedExceptions.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.thrown_exceptions; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import java.lang.annotation.Documented; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/ExpectedExceptionsByOverridingMethods.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/ExpectedExceptionsByOverridingMethods.java index a0e5c2d16a..33cd998bdc 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/ExpectedExceptionsByOverridingMethods.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/thrown_exceptions/ExpectedExceptionsByOverridingMethods.java @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.thrown_exceptions; -import org.opalj.br.fpcf.FPCFAnalysis; +import org.opalj.si.FPCFAnalysis; import org.opalj.fpcf.properties.PropertyValidator; import java.lang.annotation.Documented; diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedCallee.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedCallee.java new file mode 100644 index 0000000000..c860eac1fc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedCallee.java @@ -0,0 +1,26 @@ +package org.opalj.fpcf.properties.vta; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.vta.ExpectedCalleeMatcher; + +import java.lang.annotation.*; + +public @interface ExpectedCallee { + + String PROPERTY_VALIDATOR_KEY = "ExpectedCallee"; + + int lineNumber(); + + String value(); + + boolean upperBound(); + + @PropertyValidator(key = ExpectedCallee.PROPERTY_VALIDATOR_KEY, validator = ExpectedCalleeMatcher.class) + @Target(ElementType.METHOD) + @Documented + @Retention(RetentionPolicy.CLASS) + @interface List { + + ExpectedCallee[] value(); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedType.java new file mode 100644 index 0000000000..e0dbd93144 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedType.java @@ -0,0 +1,26 @@ +package org.opalj.fpcf.properties.vta; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.vta.ExpectedTypeMatcher; + +import java.lang.annotation.*; + +public @interface ExpectedType { + + String PROPERTY_VALIDATOR_KEY = "ExpectedType"; + + int lineNumber(); + + String value(); + + boolean upperBound(); + + @PropertyValidator(key = ExpectedType.PROPERTY_VALIDATOR_KEY, validator = ExpectedTypeMatcher.class) + @Target(ElementType.METHOD) + @Documented + @Retention(RetentionPolicy.CLASS) + @interface List { + + ExpectedType[] value(); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.c new file mode 100644 index 0000000000..ce8eb75058 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.c @@ -0,0 +1,106 @@ +#include +#include +#include +#include "TaintTest.h" +JNIEXPORT int JNICALL +Java_TaintTest_sum(JNIEnv *env, jobject obj, jint a, jint b) { + return a + b; +} + +JNIEXPORT int JNICALL +Java_TaintTest_propagate_1source(JNIEnv *env, jobject obj) { + return source() + 23; +} + +JNIEXPORT int JNICALL +Java_TaintTest_propagate_1sanitize(JNIEnv *env, jobject obj, jint a) { + return sanitize(a); +} + +JNIEXPORT int JNICALL +Java_TaintTest_propagate_1sink(JNIEnv *env, jobject obj, jint a) { + sink(a); + return 23; +} + +JNIEXPORT int JNICALL +Java_TaintTest_sanitize_1only_1a_1into_1sink(JNIEnv *env, jobject obj, jint a, jint b) { + a = sanitize(a); + sink(a + b); + return b; +} + +JNIEXPORT void JNICALL +Java_TaintTest_propagate_1identity_1to_1sink(JNIEnv *env, jobject obj, jint a) { + int b = identity(a); + sink(b); +} + +JNIEXPORT void JNICALL +Java_TaintTest_propagate_1zero_1to_1sink(JNIEnv *env, jobject obj, jint a) { + int b = zero(a); + sink(b); +} + +JNIEXPORT void JNICALL +Java_TaintTest_native_1array_1tainted(JNIEnv *env, jobject obj) { + int a[2] = {0, 0}; + a[1] = source(); + sink(a[1]); +} + +JNIEXPORT void JNICALL +Java_TaintTest_native_1array_1untainted(JNIEnv *env, jobject obj) { + int a[2] = {0, 0}; + a[0] = source(); + sink(a[1]); +} + +JNIEXPORT void JNICALL +Java_TaintTest_propagate_1to_1java_1sink(JNIEnv *env, jobject obj, jint a) { + jclass klass = (*env)->GetObjectClass(env, obj); + // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/types.html#type-signatures + // not documented, but "V" is "void" + jmethodID java_sink = (*env)->GetMethodID(env, klass, "indirect_sink", "(I)V"); + (*env)->CallVoidMethod(env, obj, java_sink, a); +} + +JNIEXPORT int JNICALL +Java_TaintTest_propagate_1from_1java_1source(JNIEnv *env, jobject obj) { + jclass klass = (*env)->GetObjectClass(env, obj); + jmethodID java_source = (*env)->GetMethodID(env, klass, "indirect_source", "()I"); + return (*env)->CallIntMethod(env, obj, java_source); +} + +JNIEXPORT int JNICALL +Java_TaintTest_propagate_1java_1sanitize(JNIEnv *env, jobject obj, jint a) { + jclass klass = (*env)->GetObjectClass(env, obj); + jmethodID java_sanitize = (*env)->GetMethodID(env, klass, "indirect_sanitize", "(I)I"); + return (*env)->CallIntMethod(env, obj, java_sanitize, a); +} + +int +identity(int a) { + return a; +} + +int +zero(int a) { + return 0; +} + +int +source() { + return 6*7; +} + +void +sink(int num) { + printf("native %d\n", num); +} + +int +sanitize(int num) { + return num - 19; +} + diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.class new file mode 100644 index 0000000000..32654c849d Binary files /dev/null and b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.class differ diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.h b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.h new file mode 100644 index 0000000000..338af49d15 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.h @@ -0,0 +1,48 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class TaintTest */ + +#ifndef _Included_TaintTest +#define _Included_TaintTest +#ifdef __cplusplus +extern "C" { +#endif + +JNIEXPORT int JNICALL Java_TaintTest_sum(JNIEnv *env, jobject obj, jint a, jint b); + +JNIEXPORT int JNICALL Java_TaintTest_propagate_1source(JNIEnv *env, jobject obj); + +JNIEXPORT int JNICALL Java_TaintTest_propagate_1sanitize(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT int JNICALL Java_TaintTest_propagate_1sink(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT int JNICALL Java_TaintTest_sanitize_1only_1a_1into_1sink(JNIEnv *env, jobject obj, jint a, jint b); + +JNIEXPORT void JNICALL Java_TaintTest_propagate_1identity_1to_1sink(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT void JNICALL Java_TaintTest_propagate_1zero_1to_1sink(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT void JNICALL Java_TaintTest_native_1array_1tainted(JNIEnv *env, jobject obj); + +JNIEXPORT void JNICALL Java_TaintTest_native_1array_1untainted(JNIEnv *env, jobject obj); + +JNIEXPORT void JNICALL Java_TaintTest_propagate_1to_1java_1sink(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT int JNICALL Java_TaintTest_propagate_1from_1java_1source(JNIEnv *env, jobject obj); + +JNIEXPORT int JNICALL Java_TaintTest_propagate_1java_1sanitize(JNIEnv *env, jobject obj, jint a); + +int identity(int a); + +int zero(int a); + +int source(); + +void sink(int num); + +int sanitize(int num); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.java b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.java new file mode 100644 index 0000000000..add60865a7 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.java @@ -0,0 +1,156 @@ +public class TaintTest { + private native int sum (int a, int b); + private native int propagate_source (); + private native int propagate_sanitize (int a); + private native int propagate_sink (int a); + private native int sanitize_only_a_into_sink (int a, int b); + private native void propagate_identity_to_sink(int a); + private native void propagate_zero_to_sink(int a); + private native void native_array_tainted(); + private native void native_array_untainted(); + private native void propagate_to_java_sink(int a); + private native int propagate_from_java_source(); + private native int propagate_java_sanitize(int a); + static + { + System.loadLibrary ("tainttest"); + } + public static void main (String[] args) + { + TaintTest demo = new TaintTest(); + // force call graph analysis of indirect methods + // otherwise their callees are not analyzed, + // as they are only reachable through native code + // TODO: trigger cga from within other analysis + demo.indirect_sink(demo.indirect_sanitize(demo.indirect_source())); + + demo.test_java_flow(); + demo.test_java_sanitize_no_flow(); + demo.test_java_untainted_no_flow(); + demo.test_native_sum_flow(); + demo.test_native_to_java_to_native_flow(); + demo.test_native_to_java_to_native_sanitized_no_flow(); + demo.test_native_indirect_sanitized_no_flow(); + demo.test_native_indirect_flow(); + demo.test_native_identity_flow(); + demo.test_native_zero_no_flow(); + demo.test_native_array_tainted_flow(); + demo.test_native_array_untainted_no_flow(); + demo.test_native_call_java_sink_flow(); + demo.test_native_call_java_source_flow(); + demo.test_native_call_java_sanitize_no_flow(); + System.out.println("done"); + } + + public void test_java_flow() { + System.out.println("java"); + int tainted = this.source(); + this.sink(tainted); + } + + public void test_java_sanitize_no_flow() { + System.out.println("java sanitize"); + int tainted = this.source(); + this.sink(this.sanitize(tainted)); + } + + public void test_java_untainted_no_flow() { + System.out.println("java untainted"); + int untainted = 23; + this.sink(untainted); + } + + public void test_native_sum_flow() { + System.out.println("native sum"); + int tainted = this.source(); + int untainted = 23; + int native_tainted = this.sum(tainted, untainted); + this.sink(native_tainted); + } + + public void test_native_to_java_to_native_flow() { + System.out.println("native to java to native"); + int taint = this.propagate_source(); + this.propagate_sink(taint); + } + + public void test_native_to_java_to_native_sanitized_no_flow() { + System.out.println("native to java to native sanitized"); + this.propagate_sink(this.propagate_sanitize(this.propagate_source())); + } + + public void test_native_indirect_sanitized_no_flow() { + System.out.println("native indirect sanitized"); + int tainted = this.source(); + int untainted = 23; + this.sink(this.sanitize_only_a_into_sink(tainted, untainted)); + } + + public void test_native_indirect_flow() { + System.out.println("native indirect"); + int tainted = this.source(); + int untainted = 23; + this.sink(this.sanitize_only_a_into_sink(untainted, tainted)); + } + + public void test_native_identity_flow() { + System.out.println("native identity"); + this.propagate_identity_to_sink(source()); + } + + public void test_native_zero_no_flow() { + System.out.println("native zero"); + this.propagate_zero_to_sink(source()); + } + + public void test_native_array_tainted_flow() { + System.out.println("native array tainted"); + this.native_array_tainted(); + } + + public void test_native_array_untainted_no_flow() { + System.out.println("native array untainted"); + this.native_array_untainted(); + } + + public void test_native_call_java_sink_flow() { + System.out.println("native call java sink"); + this.propagate_to_java_sink(source()); + } + + public void test_native_call_java_source_flow() { + System.out.println("native call java source"); + this.sink(this.propagate_from_java_source()); + } + + public void test_native_call_java_sanitize_no_flow() { + System.out.println("native call java sanitize"); + this.sink(this.propagate_java_sanitize(this.source())); + } + + public int indirect_source() { + return source(); + } + + public void indirect_sink(int a) { + sink(a); + } + + public int indirect_sanitize(int a) { + return sanitize(a); + } + + private static int source() + { + return 42; + } + + private static void sink(int a) { + System.out.println("java " + a); + } + + private static int sanitize(int a) + { + return a - 19; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.ll b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.ll new file mode 100644 index 0000000000..fcbab7ea71 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.ll @@ -0,0 +1,333 @@ +; ModuleID = 'TaintTest.c' +source_filename = "TaintTest.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +%struct.JNINativeInterface_ = type { i8*, i8*, i8*, i8*, i32 (%struct.JNINativeInterface_**)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*, %struct._jobject*, i8*, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, i8)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**, i8*)*, i32 (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i64)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, float)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, double)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i64)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, float)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, double)*, %struct._jobject* (%struct.JNINativeInterface_**, i16*, i32)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, i32, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i32* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i64* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, float* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, double* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i64*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, float*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, double*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i32*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i64*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, float*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, double*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i32*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i64*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, float*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, double*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct.JNINativeMethod*, i32)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct.JNIInvokeInterface_***)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*, i64)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)* } +%struct._jmethodID = type opaque +%struct._jfieldID = type opaque +%struct.__va_list_tag = type { i32, i32, i8*, i8* } +%union.jvalue = type { i64 } +%struct.JNINativeMethod = type { i8*, i8*, i8* } +%struct.JNIInvokeInterface_ = type { i8*, i8*, i8*, i32 (%struct.JNIInvokeInterface_**)*, i32 (%struct.JNIInvokeInterface_**, i8**, i8*)*, i32 (%struct.JNIInvokeInterface_**)*, i32 (%struct.JNIInvokeInterface_**, i8**, i32)*, i32 (%struct.JNIInvokeInterface_**, i8**, i8*)* } +%struct._jobject = type opaque + +@.str = private unnamed_addr constant [14 x i8] c"indirect_sink\00", align 1 +@.str.1 = private unnamed_addr constant [5 x i8] c"(I)V\00", align 1 +@.str.2 = private unnamed_addr constant [16 x i8] c"indirect_source\00", align 1 +@.str.3 = private unnamed_addr constant [4 x i8] c"()I\00", align 1 +@.str.4 = private unnamed_addr constant [18 x i8] c"indirect_sanitize\00", align 1 +@.str.5 = private unnamed_addr constant [5 x i8] c"(I)I\00", align 1 +@.str.6 = private unnamed_addr constant [11 x i8] c"native %d\0A\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_TaintTest_sum(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2, i32 noundef %3) #0 { + %5 = alloca %struct.JNINativeInterface_**, align 8 + %6 = alloca %struct._jobject*, align 8 + %7 = alloca i32, align 4 + %8 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %5, align 8 + store %struct._jobject* %1, %struct._jobject** %6, align 8 + store i32 %2, i32* %7, align 4 + store i32 %3, i32* %8, align 4 + %9 = load i32, i32* %7, align 4 + %10 = load i32, i32* %8, align 4 + %11 = add nsw i32 %9, %10 + ret i32 %11 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_TaintTest_propagate_1source(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1) #0 { + %3 = alloca %struct.JNINativeInterface_**, align 8 + %4 = alloca %struct._jobject*, align 8 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %3, align 8 + store %struct._jobject* %1, %struct._jobject** %4, align 8 + %5 = call i32 @source() + %6 = add nsw i32 %5, 23 + ret i32 %6 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_TaintTest_propagate_1sanitize(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %7 = load i32, i32* %6, align 4 + %8 = call i32 @sanitize(i32 noundef %7) + ret i32 %8 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @sanitize(i32 noundef %0) #0 { + %2 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + %3 = load i32, i32* %2, align 4 + %4 = sub nsw i32 %3, 19 + ret i32 %4 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_TaintTest_propagate_1sink(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %7 = load i32, i32* %6, align 4 + call void @sink(i32 noundef %7) + ret i32 23 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @sink(i32 noundef %0) #0 { + %2 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + %3 = load i32, i32* %2, align 4 + %4 = call i32 (i8*, ...) @printf(i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @.str.6, i64 0, i64 0), i32 noundef %3) + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_TaintTest_sanitize_1only_1a_1into_1sink(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2, i32 noundef %3) #0 { + %5 = alloca %struct.JNINativeInterface_**, align 8 + %6 = alloca %struct._jobject*, align 8 + %7 = alloca i32, align 4 + %8 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %5, align 8 + store %struct._jobject* %1, %struct._jobject** %6, align 8 + store i32 %2, i32* %7, align 4 + store i32 %3, i32* %8, align 4 + %9 = load i32, i32* %7, align 4 + %10 = call i32 @sanitize(i32 noundef %9) + store i32 %10, i32* %7, align 4 + %11 = load i32, i32* %7, align 4 + %12 = load i32, i32* %8, align 4 + %13 = add nsw i32 %11, %12 + call void @sink(i32 noundef %13) + %14 = load i32, i32* %8, align 4 + ret i32 %14 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @Java_TaintTest_propagate_1identity_1to_1sink(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + %7 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %8 = load i32, i32* %6, align 4 + %9 = call i32 @identity(i32 noundef %8) + store i32 %9, i32* %7, align 4 + %10 = load i32, i32* %7, align 4 + call void @sink(i32 noundef %10) + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @identity(i32 noundef %0) #0 { + %2 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + %3 = load i32, i32* %2, align 4 + ret i32 %3 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @Java_TaintTest_propagate_1zero_1to_1sink(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + %7 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %8 = load i32, i32* %6, align 4 + %9 = call i32 @zero(i32 noundef %8) + store i32 %9, i32* %7, align 4 + %10 = load i32, i32* %7, align 4 + call void @sink(i32 noundef %10) + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @zero(i32 noundef %0) #0 { + %2 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + ret i32 0 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @Java_TaintTest_native_1array_1tainted(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1) #0 { + %3 = alloca %struct.JNINativeInterface_**, align 8 + %4 = alloca %struct._jobject*, align 8 + %5 = alloca [2 x i32], align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %3, align 8 + store %struct._jobject* %1, %struct._jobject** %4, align 8 + %6 = bitcast [2 x i32]* %5 to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %6, i8 0, i64 8, i1 false) + %7 = call i32 @source() + %8 = getelementptr inbounds [2 x i32], [2 x i32]* %5, i64 0, i64 1 + store i32 %7, i32* %8, align 4 + %9 = getelementptr inbounds [2 x i32], [2 x i32]* %5, i64 0, i64 1 + %10 = load i32, i32* %9, align 4 + call void @sink(i32 noundef %10) + ret void +} + +; Function Attrs: argmemonly nofree nounwind willreturn writeonly +declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i1 immarg) #1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @Java_TaintTest_native_1array_1untainted(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1) #0 { + %3 = alloca %struct.JNINativeInterface_**, align 8 + %4 = alloca %struct._jobject*, align 8 + %5 = alloca [2 x i32], align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %3, align 8 + store %struct._jobject* %1, %struct._jobject** %4, align 8 + %6 = bitcast [2 x i32]* %5 to i8* + call void @llvm.memset.p0i8.i64(i8* align 4 %6, i8 0, i64 8, i1 false) + %7 = call i32 @source() + %8 = getelementptr inbounds [2 x i32], [2 x i32]* %5, i64 0, i64 0 + store i32 %7, i32* %8, align 4 + %9 = getelementptr inbounds [2 x i32], [2 x i32]* %5, i64 0, i64 1 + %10 = load i32, i32* %9, align 4 + call void @sink(i32 noundef %10) + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @Java_TaintTest_propagate_1to_1java_1sink(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + %7 = alloca %struct._jobject*, align 8 + %8 = alloca %struct._jmethodID*, align 8 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %9 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %10 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %9, align 8 + %11 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %10, i32 0, i32 31 + %12 = load %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)** %11, align 8 + %13 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %14 = load %struct._jobject*, %struct._jobject** %5, align 8 + %15 = call %struct._jobject* %12(%struct.JNINativeInterface_** noundef %13, %struct._jobject* noundef %14) + store %struct._jobject* %15, %struct._jobject** %7, align 8 + %16 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %17 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %16, align 8 + %18 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %17, i32 0, i32 33 + %19 = load %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)** %18, align 8 + %20 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %21 = load %struct._jobject*, %struct._jobject** %7, align 8 + %22 = call %struct._jmethodID* %19(%struct.JNINativeInterface_** noundef %20, %struct._jobject* noundef %21, i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0), i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @.str.1, i64 0, i64 0)) + store %struct._jmethodID* %22, %struct._jmethodID** %8, align 8 + %23 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %24 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %23, align 8 + %25 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %24, i32 0, i32 61 + %26 = load void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)** %25, align 8 + %27 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %28 = load %struct._jobject*, %struct._jobject** %5, align 8 + %29 = load %struct._jmethodID*, %struct._jmethodID** %8, align 8 + %30 = load i32, i32* %6, align 4 + call void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...) %26(%struct.JNINativeInterface_** noundef %27, %struct._jobject* noundef %28, %struct._jmethodID* noundef %29, i32 noundef %30) + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_TaintTest_propagate_1from_1java_1source(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1) #0 { + %3 = alloca %struct.JNINativeInterface_**, align 8 + %4 = alloca %struct._jobject*, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca %struct._jmethodID*, align 8 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %3, align 8 + store %struct._jobject* %1, %struct._jobject** %4, align 8 + %7 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %8 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %7, align 8 + %9 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %8, i32 0, i32 31 + %10 = load %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)** %9, align 8 + %11 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %12 = load %struct._jobject*, %struct._jobject** %4, align 8 + %13 = call %struct._jobject* %10(%struct.JNINativeInterface_** noundef %11, %struct._jobject* noundef %12) + store %struct._jobject* %13, %struct._jobject** %5, align 8 + %14 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %15 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %14, align 8 + %16 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %15, i32 0, i32 33 + %17 = load %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)** %16, align 8 + %18 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %19 = load %struct._jobject*, %struct._jobject** %5, align 8 + %20 = call %struct._jmethodID* %17(%struct.JNINativeInterface_** noundef %18, %struct._jobject* noundef %19, i8* noundef getelementptr inbounds ([16 x i8], [16 x i8]* @.str.2, i64 0, i64 0), i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @.str.3, i64 0, i64 0)) + store %struct._jmethodID* %20, %struct._jmethodID** %6, align 8 + %21 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %22 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %21, align 8 + %23 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %22, i32 0, i32 49 + %24 = load i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)** %23, align 8 + %25 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %26 = load %struct._jobject*, %struct._jobject** %4, align 8 + %27 = load %struct._jmethodID*, %struct._jmethodID** %6, align 8 + %28 = call i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...) %24(%struct.JNINativeInterface_** noundef %25, %struct._jobject* noundef %26, %struct._jmethodID* noundef %27) + ret i32 %28 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_TaintTest_propagate_1java_1sanitize(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + %7 = alloca %struct._jobject*, align 8 + %8 = alloca %struct._jmethodID*, align 8 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %9 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %10 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %9, align 8 + %11 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %10, i32 0, i32 31 + %12 = load %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)** %11, align 8 + %13 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %14 = load %struct._jobject*, %struct._jobject** %5, align 8 + %15 = call %struct._jobject* %12(%struct.JNINativeInterface_** noundef %13, %struct._jobject* noundef %14) + store %struct._jobject* %15, %struct._jobject** %7, align 8 + %16 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %17 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %16, align 8 + %18 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %17, i32 0, i32 33 + %19 = load %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)** %18, align 8 + %20 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %21 = load %struct._jobject*, %struct._jobject** %7, align 8 + %22 = call %struct._jmethodID* %19(%struct.JNINativeInterface_** noundef %20, %struct._jobject* noundef %21, i8* noundef getelementptr inbounds ([18 x i8], [18 x i8]* @.str.4, i64 0, i64 0), i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i64 0, i64 0)) + store %struct._jmethodID* %22, %struct._jmethodID** %8, align 8 + %23 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %24 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %23, align 8 + %25 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %24, i32 0, i32 49 + %26 = load i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)** %25, align 8 + %27 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %28 = load %struct._jobject*, %struct._jobject** %5, align 8 + %29 = load %struct._jmethodID*, %struct._jmethodID** %8, align 8 + %30 = load i32, i32* %6, align 4 + %31 = call i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...) %26(%struct.JNINativeInterface_** noundef %27, %struct._jobject* noundef %28, %struct._jmethodID* noundef %29, i32 noundef %30) + ret i32 %31 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @source() #0 { + ret i32 42 +} + +declare i32 @printf(i8* noundef, ...) #2 + +attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { argmemonly nofree nounwind willreturn writeonly } +attributes #2 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.module.flags = !{!0, !1, !2, !3, !4} +!llvm.ident = !{!5} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"PIC Level", i32 2} +!2 = !{i32 7, !"PIE Level", i32 2} +!3 = !{i32 7, !"uwtable", i32 1} +!4 = !{i32 7, !"frame-pointer", i32 2} +!5 = !{!"Ubuntu clang version 14.0.0-1ubuntu1"} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/build_run.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/build_run.sh new file mode 100755 index 0000000000..3f98574bdb --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/build_run.sh @@ -0,0 +1,4 @@ +clang -shared -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ -o libtainttest.so TaintTest.c +clang -S -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ TaintTest.c -emit-llvm +javac TaintTest.java +java -Djava.library.path=. TaintTest \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/libtainttest.so b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/libtainttest.so new file mode 100755 index 0000000000..39c3cf47a4 Binary files /dev/null and b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/libtainttest.so differ diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/run.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/run.sh new file mode 100755 index 0000000000..7d5eb2bdee --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/run.sh @@ -0,0 +1 @@ +java -Djava.library.path=. TaintTest diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.c new file mode 100644 index 0000000000..0372fb1352 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.c @@ -0,0 +1,20 @@ +#include + +int foo = 23; + +int pure_function(int a, int b) { + return a + b; +} + +void impure_function() { + foo = 42; +} + +int main() { + printf("hello world, foo is %i\n", foo); + int result = pure_function(1,2); + printf("result: %i\n", result); + impure_function(); + printf("foo: %i\n", foo); + return 0; +} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll b/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll new file mode 100644 index 0000000000..1078c5ec4e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll @@ -0,0 +1,57 @@ +; ModuleID = 'purity.c' +source_filename = "purity.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@foo = dso_local global i32 23, align 4 +@.str = private unnamed_addr constant [24 x i8] c"hello world, foo is %i\0A\00", align 1 +@.str.1 = private unnamed_addr constant [12 x i8] c"result: %i\0A\00", align 1 +@.str.2 = private unnamed_addr constant [8 x i8] c"foo: %i\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @pure_function(i32 %0, i32 %1) #0 { + %3 = alloca i32, align 4 + %4 = alloca i32, align 4 + store i32 %0, i32* %3, align 4 + store i32 %1, i32* %4, align 4 + %5 = load i32, i32* %3, align 4 + %6 = load i32, i32* %4, align 4 + %7 = add nsw i32 %5, %6 + ret i32 %7 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @impure_function() #0 { + store i32 42, i32* @foo, align 4 + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { + %1 = alloca i32, align 4 + %2 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + %3 = load i32, i32* @foo, align 4 + %4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([24 x i8], [24 x i8]* @.str, i64 0, i64 0), i32 %3) + %5 = call i32 @pure_function(i32 1, i32 2) + store i32 %5, i32* %2, align 4 + %6 = load i32, i32* %2, align 4 + %7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.1, i64 0, i64 0), i32 %6) + call void @impure_function() + %8 = load i32, i32* @foo, align 4 + %9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i64 0, i64 0), i32 %8) + ret i32 0 +} + +declare dso_local i32 @printf(i8*, ...) #1 + +attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.module.flags = !{!0, !1, !2} +!llvm.ident = !{!3} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{!"Ubuntu clang version 13.0.0-2"} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/CallGraphTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/CallGraphTests.scala index 04754b507b..190b4502f7 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/CallGraphTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/CallGraphTests.scala @@ -3,15 +3,13 @@ package org.opalj package fpcf import java.net.URL - import com.typesafe.config.Config import com.typesafe.config.ConfigValueFactory - import org.opalj.br.analyses.cg.InitialEntryPointsKey import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey -import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.fpcf.properties.callgraph.TypePropagationVariant import org.opalj.br.analyses.Project +import org.opalj.si.FPCFAnalysis import org.opalj.tac.cg.CallGraphKey import org.opalj.tac.cg.CHACallGraphKey import org.opalj.tac.cg.CTACallGraphKey diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldLocalityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldLocalityTests.scala index 48eaebe952..39fd7790f9 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldLocalityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/FieldLocalityTests.scala @@ -3,11 +3,10 @@ package org.opalj package fpcf import java.net.URL - import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.ai.domain.l1.DefaultDomainWithCFGAndDefUse import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.EagerFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.escape.LazyInterProceduralEscapeAnalysis @@ -15,7 +14,7 @@ import org.opalj.tac.fpcf.analyses.escape.LazyReturnValueFreshnessAnalysis class FieldLocalityTests extends PropertiesTest { - val analyses = Set[FPCFAnalysisScheduler]( + val analyses = Set[JavaFPCFAnalysisScheduler]( EagerFieldLocalityAnalysis, LazyInterProceduralEscapeAnalysis, LazyReturnValueFreshnessAnalysis diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala index 4cb596345a..890395a775 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala @@ -4,14 +4,11 @@ package fpcf import java.io.File import java.net.URL - import com.typesafe.config.Config import com.typesafe.config.ConfigValueFactory - import org.opalj.bi.reader.ClassFileReader import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers - import org.opalj.log.LogContext import org.opalj.util.ScalaMajorVersion import org.opalj.fpcf.properties.PropertyMatcher @@ -37,11 +34,11 @@ import org.opalj.br.analyses.Project import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.InitialEntryPointsKey import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.properties.Context import org.opalj.br.fpcf.properties.SimpleContextsKey +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.FPCFAnalysis +import org.opalj.si.PropertyStoreKey import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSitesKey diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReturnValueFreshnessTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReturnValueFreshnessTests.scala index db7f412d75..178379033f 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReturnValueFreshnessTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ReturnValueFreshnessTests.scala @@ -3,9 +3,8 @@ package org.opalj package fpcf import java.net.URL - import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.LazyFieldLocalityAnalysis import org.opalj.tac.fpcf.analyses.escape.EagerReturnValueFreshnessAnalysis @@ -28,7 +27,7 @@ class ReturnValueFreshnessTests extends PropertiesTest { List("org/opalj/fpcf/fixtures/return_freshness") } - val analysisSchedulers: Set[FPCFAnalysisScheduler] = Set[FPCFAnalysisScheduler]( + val analysisSchedulers: Set[JavaFPCFAnalysisScheduler] = Set[JavaFPCFAnalysisScheduler]( LazyInterProceduralEscapeAnalysis, LazyFieldLocalityAnalysis, EagerReturnValueFreshnessAnalysis diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala new file mode 100644 index 0000000000..270d4ee496 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala @@ -0,0 +1,42 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.Project +import org.opalj.fpcf.PropertiesTest +import org.opalj.fpcf.properties.taint.ForwardFlowPath +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintAnalysisFixtureScheduler +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact + +import java.net.URL + +/** + * @author Mario Trageser + */ +class ForwardTaintAnalysisFixtureTest extends PropertiesTest { + + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData( + AIDomainFactoryKey + )( + (_: Option[Set[Class[_ <: AnyRef]]]) => + Set[Class[_ <: AnyRef]]( + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] + ) + ) + p.get(RTACallGraphKey) + } + + describe("Test the ForwardFlowPath annotations") { + val testContext = executeAnalyses(ForwardTaintAnalysisFixtureScheduler) + val project = testContext.project + val eas = methodsWithAnnotations(project).map { + case (method, entityString, annotations) => + ((method, TaintNullFact), entityString, annotations) + } + testContext.propertyStore.shutdown() + validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala new file mode 100644 index 0000000000..1eed699b05 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala @@ -0,0 +1,42 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.Project +import org.opalj.fpcf.PropertiesTest +import org.opalj.fpcf.properties.vta.{ExpectedCallee, ExpectedType} +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.VTANullFact +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisScheduler + +import java.net.URL + +class VTATest extends PropertiesTest { + + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData( + AIDomainFactoryKey + )( + (_: Option[Set[Class[_ <: AnyRef]]]) => + Set[Class[_ <: AnyRef]]( + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] + ) + ) + p.get(RTACallGraphKey) + } + + describe("Test the ExpectedType annotations") { + val testContext = executeAnalyses(new IFDSBasedVariableTypeAnalysisScheduler) + val project = testContext.project + // val declaredMethods = project.get(DeclaredMethodsKey) + val eas = methodsWithAnnotations(project).map { + case (method, entityString, annotations) => + ((method, VTANullFact), entityString, annotations) + } + testContext.propertyStore.shutdown() + validateProperties(testContext, eas, Set(ExpectedType.PROPERTY_VALIDATOR_KEY)) + validateProperties(testContext, eas, Set(ExpectedCallee.PROPERTY_VALIDATOR_KEY)) + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/callgraph/AvailableTypesMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/callgraph/AvailableTypesMatcher.scala index 49324a2315..a34d6b00bb 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/callgraph/AvailableTypesMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/callgraph/AvailableTypesMatcher.scala @@ -9,11 +9,11 @@ import org.opalj.br.ElementValue import org.opalj.br.ObjectType import org.opalj.br.ReferenceType import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.tac.fpcf.properties.cg.InstantiatedTypes import org.opalj.collection.immutable.UIDSet import org.opalj.log.LogContext import org.opalj.log.OPALLogger +import org.opalj.si.PropertyStoreKey import scala.collection.mutable diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/callgraph/DirectCallMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/callgraph/DirectCallMatcher.scala index d17e9aee2e..101bbef628 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/callgraph/DirectCallMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/callgraph/DirectCallMatcher.scala @@ -16,7 +16,7 @@ import org.opalj.br.ObjectType import org.opalj.br.StringValue import org.opalj.br.VoidType import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.si.PropertyStoreKey import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.analyses.cg.TypeIterator import org.opalj.tac.fpcf.properties.cg.Callees diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_mutability/NonFinalMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_mutability/NonFinalMatcher.scala index 4853bd98a5..2bf3defe2c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_mutability/NonFinalMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/field_mutability/NonFinalMatcher.scala @@ -8,11 +8,11 @@ import org.opalj.br.AnnotationLike import org.opalj.br.ObjectType import org.opalj.br.BooleanValue import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.FieldPrematurelyRead import org.opalj.br.fpcf.properties.NonFinalField import org.opalj.br.fpcf.properties.PrematurelyReadField +import org.opalj.si.PropertyStoreKey /** * Matches a field's `FieldMutability` property. The match is successful if the field either diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/purity/PurityMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/purity/PurityMatcher.scala index 5a0ddaccf2..9a85861714 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/purity/PurityMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/purity/PurityMatcher.scala @@ -17,9 +17,9 @@ import org.opalj.br.fpcf.properties.FieldLocality import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.Purity import org.opalj.br.fpcf.properties.ReturnValueFreshness -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.properties.ClassifiedImpure import org.opalj.br.fpcf.properties.SimpleContextsKey +import org.opalj.si.PropertyStoreKey /** * Base trait for matchers that match a method's `Purity` property. diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala new file mode 100644 index 0000000000..f5d342b77c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala @@ -0,0 +1,52 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.taint + +/* TODO Fix as soon as backwards analysis is implemented +import org.opalj.br._ +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.properties.AbstractPropertyMatcher +import org.opalj.fpcf.{EPS, Entity, FinalEP, Property} +import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysisFixtureScheduler +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact} +import org.opalj.tac.fpcf.properties.OldTaint + +/** + * @author Mario Trageser + */ +class BackwardFlowPathMatcher extends AbstractPropertyMatcher { + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val method = entity.asInstanceOf[(DefinedMethod, TaintFact)]._1.definedMethod + val expectedFlow = a.elementValuePairs.map((evp: ElementValuePair) => + evp.value.asArrayValue.values.map((value: ElementValue) => + value.asStringValue.value)).head.toIndexedSeq + val propertyStore = p.get(PropertyStoreKey) + val propertyKey = BackwardTaintAnalysisFixtureScheduler.property.key + val allReachableFlowFacts = + propertyStore.entities(propertyKey).collect { + case EPS((m: DefinedMethod, inputFact)) if m.definedMethod == method => + (m, inputFact) + }.flatMap(propertyStore(_, propertyKey) match { + case FinalEP(_, OldTaint(result, _)) => + result.values.fold(Set.empty)((acc, facts) => acc ++ facts).collect { + case FlowFact(methods) => methods.map(_.name) + } + case _ => Seq.empty + }).toIndexedSeq + if (expectedFlow.isEmpty) { + if (allReachableFlowFacts.nonEmpty) return Some(s"There should be no flow for $entity") + None + } else { + if (allReachableFlowFacts.contains(expectedFlow)) None + else Some(expectedFlow.mkString(", ")) + } + } +} +*/ \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala new file mode 100644 index 0000000000..b3452fcb55 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala @@ -0,0 +1,42 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.taint + +import org.opalj.br.analyses.SomeProject +import org.opalj.br.{AnnotationLike, ElementValue, ElementValuePair, ObjectType} +import org.opalj.fpcf.ifds.IFDSProperty +import org.opalj.fpcf.properties.AbstractPropertyMatcher +import org.opalj.fpcf.{Entity, Property} +import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact + +/** + * @author Mario Trageser + */ +class ForwardFlowPathMatcher extends AbstractPropertyMatcher { + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val expectedFlow = a.elementValuePairs.map((evp: ElementValuePair) => + evp.value.asArrayValue.values.map((value: ElementValue) => + value.asStringValue.value)).head.toIndexedSeq + val flows = properties.filter(_.isInstanceOf[IFDSProperty[_, _]]).head + .asInstanceOf[IFDSProperty[_, _]] + .flows + .values + .fold(Set.empty)((acc, facts) => acc ++ facts) + .collect { + case FlowFact(methods) => methods.map(_.name).toIndexedSeq + } + if (expectedFlow.isEmpty) { + if (flows.nonEmpty) return Some(s"There should be no flow for $entity") + None + } else { + if (flows.contains(expectedFlow)) None + else Some(expectedFlow.mkString(", ")) + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala new file mode 100644 index 0000000000..444e08bc60 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala @@ -0,0 +1,51 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.vta + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.EPS +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.Property +import org.opalj.value.ValueInformation +import org.opalj.br.analyses.SomeProject +import org.opalj.br.AnnotationLike +import org.opalj.br.Method +import org.opalj.si.PropertyStoreKey +import org.opalj.tac.DUVar +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.fpcf.analyses.ifds.{CalleeType, IFDSBasedVariableTypeAnalysisScheduler, VTAResult} + +class ExpectedCalleeMatcher extends VTAMatcher { + + def validateSingleAnnotation(project: SomeProject, entity: Entity, + taCode: TACode[TACMethodParameter, DUVar[ValueInformation]], + method: Method, annotation: AnnotationLike, + properties: Iterable[Property]): Option[String] = { + val elementValuePairs = annotation.elementValuePairs + val expected = ( + elementValuePairs.head.value.asIntValue.value, + elementValuePairs(1).value.asStringValue.value, + elementValuePairs(2).value.asBooleanValue.value + ) + val propertyStore = project.get(PropertyStoreKey) + val propertyKey = new IFDSBasedVariableTypeAnalysisScheduler().property.key + // Get ALL the exit facts for the method for ALL input facts + val allReachableExitFacts = + propertyStore.entities(propertyKey).collect { + case EPS((m: Method, inputFact)) if m == method => + (m, inputFact) + }.flatMap(propertyStore(_, propertyKey) match { + case FinalEP(_, VTAResult(result, _)) => + result.values.fold(Set.empty)((acc, facts) => acc ++ facts).collect { + case CalleeType(index, t, upperBound) => + Seq(( + taCode.lineNumber(method.body.get, index).get, + referenceTypeToString(t), upperBound + )) + } + case _ => Seq.empty + }).flatten.toSet + if (allReachableExitFacts.contains(expected)) None + else Some(expected.toString) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala new file mode 100644 index 0000000000..167f6f0957 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala @@ -0,0 +1,40 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.vta + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.value.ValueInformation +import org.opalj.br.analyses.SomeProject +import org.opalj.br.AnnotationLike +import org.opalj.br.Method +import org.opalj.tac.DUVar +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.fpcf.analyses.ifds.{VTAResult, VariableType} + +class ExpectedTypeMatcher extends VTAMatcher { + + def validateSingleAnnotation(project: SomeProject, entity: Entity, + taCode: TACode[TACMethodParameter, DUVar[ValueInformation]], + method: Method, annotation: AnnotationLike, + properties: Iterable[Property]): Option[String] = { + val elementValuePairs = annotation.elementValuePairs + val expected = ( + elementValuePairs.head.value.asIntValue.value, + elementValuePairs(1).value.asStringValue.value, + elementValuePairs(2).value.asBooleanValue.value + ) + val result = properties.filter(_.isInstanceOf[VTAResult]).head + .asInstanceOf[VTAResult] + .flows + .values + .fold(Set.empty)((acc, facts) => acc ++ facts) + .collect { + case VariableType(definedBy, t, upperBound) => + (taCode.lineNumber(method.body.get, definedBy).get, referenceTypeToString(t), + upperBound) + } + if (result.contains(expected)) None + else Some(expected.toString) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala new file mode 100644 index 0000000000..1a825d1ee3 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala @@ -0,0 +1,58 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.vta + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher +import org.opalj.value.ValueInformation +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.ArrayType +import org.opalj.br.Method +import org.opalj.br.ReferenceType +import org.opalj.si.PropertyStoreKey +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.tac.fpcf.properties.TheTACAI +import org.opalj.tac.DUVar +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.fpcf.analyses.ifds.VTAFact + +abstract class VTAMatcher extends AbstractPropertyMatcher { + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Iterable[Property] + ): Option[String] = { + val method = entity.asInstanceOf[(Method, VTAFact)]._1 + val taCode = p.get(PropertyStoreKey)(method, TACAI.key) match { + case FinalP(TheTACAI(tac)) => tac + case _ => + throw new IllegalStateException( + "TAC of annotated method not present after analysis" + ) + } + val result = a.elementValuePairs(0).value.asArrayValue.values + .map(annotationValue => + validateSingleAnnotation(p, entity, taCode, method, + annotationValue.asAnnotationValue.annotation, properties)).filter(_.isDefined) + if (result.isEmpty) None + else Some(result.map(_.get).mkString(", ")) + } + + def validateSingleAnnotation(project: SomeProject, entity: Entity, + taCode: TACode[TACMethodParameter, DUVar[ValueInformation]], + method: Method, annotation: AnnotationLike, + properties: Iterable[Property]): Option[String] + + def referenceTypeToString(t: ReferenceType): String = t match { + case objectType: ObjectType => objectType.simpleName + case arrayType: ArrayType => + referenceTypeToString(arrayType.elementType.asReferenceType)+"[]" + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala new file mode 100644 index 0000000000..1926483c15 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala @@ -0,0 +1,77 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import com.typesafe.config.ConfigValueFactory + +import org.opalj.br.analyses.Project +import org.opalj.fpcf.ifds +import org.opalj.fpcf.ifds.IFDSProperty +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement} +import org.opalj.ll.fpcf.analyses.ifds.taint.{JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler, NativeTaintFact, NativeTaintNullFact} +import org.opalj.ll.fpcf.analyses.ifds.taint.SimpleJavaForwardTaintAnalysis +import org.opalj.ll.llvm.value.Function + +import org.opalj.log.GlobalLogContext +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.taint.{FlowFact, TaintFact} +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers + +class CrossLanguageForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers { + describe("CrossLanguageForwardTaintAnalysis") { + implicit val config = BaseConfig.withValue(ifds.ConfigKeyPrefix+"debug", ConfigValueFactory.fromAnyRef(true)) + val project = + Project( + new java.io.File("./DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint"), + GlobalLogContext, + config + ) + + project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( + current => List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.ll") + ) + project.get(LLVMProjectKey) + project.get(RTACallGraphKey) + val manager = project.get(FPCFAnalysesManagerKey) + val (ps, analyses) = manager.runAll(JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler) + for ((method, fact) <- analyses.head._2.asInstanceOf[SimpleJavaForwardTaintAnalysis].ifdsProblem.entryPoints) { + val flows = + ps((method, fact), JavaForwardTaintAnalysisScheduler.property.key) + println("---METHOD: "+method.toJava+" ---") + val flowFacts = flows.ub + .asInstanceOf[IFDSProperty[JavaStatement, TaintFact]] + .flows + .values + .flatten + .toSet[TaintFact] + .flatMap { + case FlowFact(flow) => Some(flow) + case _ => None + } + for (flow <- flowFacts) + println(s"flow: "+flow.map(_.name).mkString(", ")) + if (method.name.contains("no_flow")) { + it(s"${method.name} has no flow") { + assert(flowFacts.isEmpty) + } + } else if (method.name.contains("flow")) { + it(s"${method.name} has some flow") { + assert(!flowFacts.isEmpty) + } + } + } + + val function: Function = project.get(LLVMProjectKey).function("Java_TaintTest_native_1array_1tainted").get + val debugData = ps((LLVMFunction(function), NativeTaintNullFact), NativeForwardTaintAnalysisScheduler.property.key).ub.asInstanceOf[IFDSProperty[LLVMStatement, NativeTaintFact]].debugData + for { + bb <- function.basicBlocks + instruction <- bb.instructions + } { + for (fact <- debugData.getOrElse(LLVMStatement(instruction), Set.empty)) + println("\t"+fact) + println(instruction.repr) + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala new file mode 100644 index 0000000000..b05caa8fb7 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala @@ -0,0 +1,36 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import org.opalj.br.analyses.Project +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.ll.fpcf.analyses.{EagerSimplePurityAnalysis, Impure, Pure} +import org.opalj.ll.llvm.value.Function +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers + +import scala.collection.immutable.List + +class SimplePurityTests extends AnyFunSpec with Matchers { + describe("SimplePurityAnalysis") { + it("executes") { + val project = Project(Iterable.empty) + project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( + current => List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll") + ) + val (propertyStore, _) = project.get(FPCFAnalysesManagerKey).runAll(EagerSimplePurityAnalysis) + + val impureFunctionNames = propertyStore + .finalEntities(Impure) + .asInstanceOf[Iterator[Function]] + .map(_.name) + .toList + impureFunctionNames should contain("impure_function") + val pureFunctionNames = propertyStore + .finalEntities(Pure) + .asInstanceOf[Iterator[Function]] + .map(_.name) + .toList + pureFunctionNames should contain("pure_function") + } + } +} diff --git a/OPAL/ai/src/main/scala/org/opalj/ai/common/SimpleAIKey.scala b/OPAL/ai/src/main/scala/org/opalj/ai/common/SimpleAIKey.scala index a0c0b862dd..3889fa87e8 100644 --- a/OPAL/ai/src/main/scala/org/opalj/ai/common/SimpleAIKey.scala +++ b/OPAL/ai/src/main/scala/org/opalj/ai/common/SimpleAIKey.scala @@ -4,12 +4,10 @@ package ai package common import scala.collection.concurrent.TrieMap - import org.opalj.log.LogContext import org.opalj.log.OPALLogger import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{JavaProjectInformationKey, SomeProject} import org.opalj.ai.domain.RecordDefUse import org.opalj.ai.domain.l1.DefaultDomainWithCFGAndDefUse @@ -27,22 +25,19 @@ import org.opalj.ai.domain.l1.DefaultDomainWithCFGAndDefUse * } * ) * }}} - * * @note To get the index use the [[org.opalj.br.analyses.Project]]'s `get` method and * pass in `this` object. - * * @note '''If you are developing analyses using the `PropertyStore` use an appropriate analysis * that stores the results of an abstract interpretation in the store.''' - * * @author Michael Eichberg */ object SimpleAIKey - extends ProjectInformationKey[Method => AIResult { val domain: Domain with RecordDefUse }, /*DomainFactory*/ Method => Domain with RecordDefUse] { + extends JavaProjectInformationKey[Method => AIResult { val domain: Domain with RecordDefUse }, /*DomainFactory*/ Method => Domain with RecordDefUse] { /** * The SimpleAIKey has no special prerequisites. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[Nothing, Nothing]] = Nil /** * Returns an object which performs and caches the result of the abstract interpretation of a diff --git a/OPAL/ai/src/main/scala/org/opalj/ai/domain/TheProject.scala b/OPAL/ai/src/main/scala/org/opalj/ai/domain/TheProject.scala index ad960585de..b3b1a0a52c 100644 --- a/OPAL/ai/src/main/scala/org/opalj/ai/domain/TheProject.scala +++ b/OPAL/ai/src/main/scala/org/opalj/ai/domain/TheProject.scala @@ -6,7 +6,7 @@ package domain import org.opalj.log.LogContext import org.opalj.br.{ClassHierarchy => BRClassHierarchy} import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.si.PropertyStoreKey /** * Provides information about the underlying project. @@ -31,7 +31,6 @@ import org.opalj.br.fpcf.PropertyStoreKey * does not use the `override` access flag.''' * This way the compiler will issue a warning if two implementations are used * to create a final domain. - * * @author Michael Eichberg */ trait TheProject extends ThePropertyStore with LogContextProvider { diff --git a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/DomainBasedFPCFAnalysisScheduler.scala b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/DomainBasedFPCFAnalysisScheduler.scala index a7c3ee3d03..5c2b8bd0b6 100644 --- a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/DomainBasedFPCFAnalysisScheduler.scala +++ b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/DomainBasedFPCFAnalysisScheduler.scala @@ -7,9 +7,9 @@ package analyses import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.ai.fpcf.domain.PropertyStoreBased import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler /** * Scheduler that can be used by analyses which perform abstract interpretations and where @@ -17,7 +17,7 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey * * @author Michael Eichberg */ -trait DomainBasedFPCFAnalysisScheduler extends FPCFAnalysisScheduler { +trait DomainBasedFPCFAnalysisScheduler extends JavaFPCFAnalysisScheduler { override def uses(p: SomeProject, ps: PropertyStore): Set[PropertyBounds] = { if (p.allMethodsWithBody.isEmpty) diff --git a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/L0BaseAIResultAnalysis.scala b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/L0BaseAIResultAnalysis.scala index 2d4e19ed29..7145797530 100644 --- a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/L0BaseAIResultAnalysis.scala +++ b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/L0BaseAIResultAnalysis.scala @@ -12,11 +12,8 @@ import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.{JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} +import org.opalj.br.fpcf.{JavaBasicFPCFEagerAnalysisScheduler, JavaBasicFPCFLazyAnalysisScheduler} import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.ai.fpcf.properties.AnAIResult import org.opalj.ai.fpcf.properties.BaseAIResult @@ -27,7 +24,7 @@ import org.opalj.ai.fpcf.properties.ProjectSpecificAIExecutor * * @author Michael Eichberg */ -class L0BaseAIResultAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { +class L0BaseAIResultAnalysis private[analyses] (val project: SomeProject) extends ProjectBasedAnalysis { final implicit val aiFactory: ProjectSpecificAIExecutor = project.get(AIDomainFactoryKey) @@ -74,7 +71,7 @@ object L0BaseAIResultAnalysis { sealed trait L0BaseAIResultAnalysisScheduler extends DomainBasedFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(AIDomainFactoryKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(AIDomainFactoryKey) final def derivedProperty: PropertyBounds = PropertyBounds.lub(BaseAIResult) @@ -82,13 +79,13 @@ sealed trait L0BaseAIResultAnalysisScheduler extends DomainBasedFPCFAnalysisSche object EagerL0BaseAIAnalysis extends L0BaseAIResultAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesCollaboratively: Set[PropertyBounds] = Set.empty override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new L0BaseAIResultAnalysis(p) // <= NOT TO BE CREATED IN INIT! val methods = p.allMethodsWithBody ps.scheduleEagerComputationsForEntities(methods)(analysis.performAI) @@ -98,11 +95,11 @@ object EagerL0BaseAIAnalysis object LazyL0BaseAIAnalysis extends L0BaseAIResultAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new L0BaseAIResultAnalysis(p) // <= NOT TO BE CREATED IN INIT! ps.registerLazyPropertyComputation(BaseAIResult.key, analysis.performAI) analysis diff --git a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBFieldValuesAnalysis.scala b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBFieldValuesAnalysis.scala index 3a866faac7..aab8840237 100644 --- a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBFieldValuesAnalysis.scala +++ b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBFieldValuesAnalysis.scala @@ -5,7 +5,6 @@ package fpcf package analyses import scala.collection.mutable - import org.opalj.log.OPALLogger import org.opalj.fpcf.Entity import org.opalj.fpcf.EOptionP @@ -23,21 +22,18 @@ import org.opalj.fpcf.Result import org.opalj.fpcf.Results import org.opalj.fpcf.SinglePropertiesBoundType import org.opalj.fpcf.SomeEPS -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{FieldAccessInformationKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.ClassFile import org.opalj.br.Code import org.opalj.br.FieldType import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.PC -import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.Field -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.ai.domain import org.opalj.ai.fpcf.analyses.FieldValuesAnalysis.ignoredFields import org.opalj.ai.fpcf.domain.RefinedTypeLevelFieldAccessInstructions +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler //import org.opalj.ai.fpcf.domain.RefinedTypeLevelInvokeInstructions import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.ai.fpcf.properties.FieldValue @@ -99,7 +95,7 @@ import org.opalj.ai.fpcf.properties.ValueBasedFieldValueInformation */ class LBFieldValuesAnalysis private[analyses] ( val project: SomeProject -) extends FPCFAnalysis { analysis => +) extends ProjectBasedAnalysis { analysis => final val fieldAccessInformation = project.get(FieldAccessInformationKey) @@ -428,9 +424,9 @@ object FieldValuesAnalysis { } -object EagerLBFieldValuesAnalysis extends BasicFPCFEagerAnalysisScheduler { +object EagerLBFieldValuesAnalysis extends JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(FieldAccessInformationKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(FieldAccessInformationKey) override def init(p: SomeProject, ps: PropertyStore): Null = { // To ensure that subsequent analyses are able to pick-up the results of this @@ -450,7 +446,7 @@ object EagerLBFieldValuesAnalysis extends BasicFPCFEagerAnalysisScheduler { override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new LBFieldValuesAnalysis(p) val classFiles = if (!p.libraryClassFilesAreInterfacesOnly) diff --git a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBMethodReturnValuesAnalysis.scala b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBMethodReturnValuesAnalysis.scala index 5e26a043cb..3a4a187551 100644 --- a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBMethodReturnValuesAnalysis.scala +++ b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/analyses/LBMethodReturnValuesAnalysis.scala @@ -18,18 +18,16 @@ import org.opalj.fpcf.Result import org.opalj.fpcf.SinglePropertiesBoundType import org.opalj.fpcf.SomeEPS import org.opalj.value.ValueInformation -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.Method import org.opalj.br.PC -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.ai.domain import org.opalj.ai.fpcf.domain.RefinedTypeLevelFieldAccessInstructions import org.opalj.ai.fpcf.domain.RefinedTypeLevelInvokeInstructions import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.ai.fpcf.properties.MethodReturnValue import org.opalj.ai.fpcf.properties.TheMethodReturnValue +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler /** * Computes for each method that returns object typed values general information about the @@ -39,7 +37,7 @@ import org.opalj.ai.fpcf.properties.TheMethodReturnValue */ class LBMethodReturnValuesAnalysis private[analyses] ( val project: SomeProject -) extends FPCFAnalysis { analysis => +) extends ProjectBasedAnalysis { analysis => /** * A very basic domain that we use for analyzing the values returned by a method. @@ -178,9 +176,9 @@ class LBMethodReturnValuesAnalysis private[analyses] ( } -object EagerLBMethodReturnValuesAnalysis extends BasicFPCFEagerAnalysisScheduler { +object EagerLBMethodReturnValuesAnalysis extends JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq.empty + override def requiredProjectInformation: JavaProjectInformationKeys = Seq.empty override def init(p: SomeProject, ps: PropertyStore): Null = { // To ensure that subsequent analyses are able to pick-up the results of this @@ -200,7 +198,7 @@ object EagerLBMethodReturnValuesAnalysis extends BasicFPCFEagerAnalysisScheduler override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new LBMethodReturnValuesAnalysis(p) val methods = p.allMethodsWithBody.iterator.filter { m => val returnType = m.returnType diff --git a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/properties/AIDomainFactoryKey.scala b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/properties/AIDomainFactoryKey.scala index 50f9d85c0f..af584a490a 100644 --- a/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/properties/AIDomainFactoryKey.scala +++ b/OPAL/ai/src/main/scala/org/opalj/ai/fpcf/properties/AIDomainFactoryKey.scala @@ -6,8 +6,7 @@ package properties import org.opalj.log.OPALLogger import org.opalj.log.LogContext import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{JavaProjectInformationKey, SomeProject} import org.opalj.ai.common.DomainRegistry /** @@ -37,14 +36,14 @@ class ProjectSpecificAIExecutor( * @author Michael Eichberg */ object AIDomainFactoryKey - extends ProjectInformationKey[ProjectSpecificAIExecutor, Set[Class[_ <: AnyRef]]] { + extends JavaProjectInformationKey[ProjectSpecificAIExecutor, Set[Class[_ <: AnyRef]]] { /** * This key has no special prerequisites. * * @note The configuration is done using '''ProjectInformationKeyInitializationData'''. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[Nothing, Nothing]] = Nil /** * Returns an object which performs and caches the result of the abstract interpretation of a diff --git a/OPAL/bc/src/test/scala/org/opalj/test/fixtures/dynamicConstants/DynamicConstantsCreationTest.scala b/OPAL/bc/src/test/scala/org/opalj/test/fixtures/dynamicConstants/DynamicConstantsCreationTest.scala index d6018b6af9..5703392259 100644 --- a/OPAL/bc/src/test/scala/org/opalj/test/fixtures/dynamicConstants/DynamicConstantsCreationTest.scala +++ b/OPAL/bc/src/test/scala/org/opalj/test/fixtures/dynamicConstants/DynamicConstantsCreationTest.scala @@ -406,7 +406,7 @@ class DynamicConstantsCreationTest extends AnyFlatSpec with Matchers with Before (0xff & 176).toByte // areturn )) )) - ), + ) ), attributes = ArraySeq( BootstrapMethods_attribute(17, ArraySeq( diff --git a/OPAL/br/src/main/scala/org/opalj/br/ElementValue.scala b/OPAL/br/src/main/scala/org/opalj/br/ElementValue.scala index d95c6b3313..ed8cde73b8 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/ElementValue.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/ElementValue.scala @@ -20,6 +20,7 @@ sealed trait ElementValue extends Attribute { def toJava: String def asIntValue: IntValue = throw new ClassCastException(); + def asBooleanValue: BooleanValue = throw new ClassCastException(); def asEnumValue: EnumValue = throw new ClassCastException(); def asAnnotationValue: AnnotationValue = throw new ClassCastException(); def asStringValue: StringValue = throw new ClassCastException(); @@ -168,6 +169,8 @@ case class BooleanValue(value: Boolean) extends BaseTypeElementValue { override def kindId: Int = BooleanValue.KindId + final override def asBooleanValue: BooleanValue = this + } object BooleanValue { diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/DeclaredMethodsKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/DeclaredMethodsKey.scala index e5a0d4aaa9..004a818c5b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/DeclaredMethodsKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/DeclaredMethodsKey.scala @@ -25,7 +25,7 @@ import scala.jdk.CollectionConverters._ * @author Dominik Helm * @author Florian Kuebler */ -object DeclaredMethodsKey extends ProjectInformationKey[DeclaredMethods, Nothing] { +object DeclaredMethodsKey extends JavaProjectInformationKey[DeclaredMethods, Nothing] { // The following lists were created using the Java 10 specification private val methodHandleSignaturePolymorphicMethods = List( @@ -72,7 +72,7 @@ object DeclaredMethodsKey extends ProjectInformationKey[DeclaredMethods, Nothing * * @return `Nil`. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[Nothing, Nothing]] = Nil // TODO [Java9+] Needs to be updated for Java9+ projects which use Modules. /** diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/FieldAccessInformationKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/FieldAccessInformationKey.scala index 176c280d6d..67cffbee0f 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/FieldAccessInformationKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/FieldAccessInformationKey.scala @@ -12,14 +12,14 @@ import org.opalj.concurrent.defaultIsInterrupted * * @author Michael Eichberg */ -object FieldAccessInformationKey extends ProjectInformationKey[FieldAccessInformation, Nothing] { +object FieldAccessInformationKey extends JavaProjectInformationKey[FieldAccessInformation, Nothing] { /** * The [[FieldAccessInformationAnalysis]] has no special prerequisites. * * @return `Nil`. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[Nothing, Nothing]] = Nil /** * Computes the field access information. diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/JavaProjectInformationKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/JavaProjectInformationKey.scala new file mode 100644 index 0000000000..dc35cd7d04 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/JavaProjectInformationKey.scala @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.br.analyses + +import org.opalj.si.{MetaProject, ProjectInformationKey} + +trait JavaProjectInformationKey[T <: AnyRef, I <: AnyRef] extends ProjectInformationKey[SomeProject, T, I] { + + final override def compute(project: MetaProject): T = { + project match { + case someProject: SomeProject => compute(someProject) + case _ => throw new IllegalArgumentException("Wrong Project Kind") // TODO: Be more graceful + } + } + + def compute(project: SomeProject): T + + final override def requirements(project: MetaProject): JavaProjectInformationKeys = { + project match { + case someProject: SomeProject => requirements(someProject) + case _ => throw new IllegalArgumentException("Wrong Project Kind") // TODO: Be more graceful + } + } + + def requirements(project: SomeProject): JavaProjectInformationKeys +} \ No newline at end of file diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/Project.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/Project.scala index 302caf5e93..507f6132ef 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/Project.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/Project.scala @@ -9,7 +9,6 @@ import java.io.File import java.lang.ref.SoftReference import java.net.URL import java.util.Arrays.{sort => sortArray} -import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicReferenceArray import scala.collection.Map import scala.collection.Set @@ -43,6 +42,7 @@ import org.opalj.br.instructions.NonVirtualMethodInvocationInstruction import org.opalj.br.reader.BytecodeInstructionsCache import org.opalj.br.reader.Java17FrameworkWithDynamicRewritingAndCaching import org.opalj.br.reader.Java17LibraryFramework +import org.opalj.si.MetaProject import scala.collection.immutable.ArraySeq @@ -57,10 +57,10 @@ import scala.collection.immutable.ArraySeq * 1. It directly gives access to the project's class hierarchy. * 1. It serves as a container for project-wide information (e.g., a call graph, * information about the mutability of classes, constant values,...) that can - * be queried using [[org.opalj.br.analyses.ProjectInformationKey]]s. + * be queried using [[ProjectInformationKey]]s. * The list of project wide information that can be made available is equivalent * to the list of (concrete/singleton) objects implementing the trait - * [[org.opalj.br.analyses.ProjectInformationKey]]. + * [[ProjectInformationKey]]. * One of the most important project information keys is the * `PropertyStoreKey` which gives access to the property store. * @@ -148,12 +148,12 @@ class Project[Source] private ( final val overridingMethods: Map[Method, immutable.Set[Method]], final val nests: Map[ObjectType, ObjectType], // Note that the referenced array will never shrink! - @volatile private[this] var projectInformation: AtomicReferenceArray[AnyRef] = new AtomicReferenceArray[AnyRef](32) + @volatile var projectInformation: AtomicReferenceArray[AnyRef] = new AtomicReferenceArray[AnyRef](32) )( implicit final val logContext: LogContext, final val config: Config -) extends ProjectLike { +) extends ProjectLike with MetaProject { /** * Returns a shallow clone of this project with an updated log context and (optionally) @@ -425,180 +425,6 @@ class Project[Source] private ( info("project setup", s"computing functional interfaces took ${t.toSeconds}") } - // -------------------------------------------------------------------------------------------- - // - // CODE TO MAKE IT POSSIBLE TO ATTACH SOME INFORMATION TO A PROJECT (ON DEMAND) - // - // -------------------------------------------------------------------------------------------- - - /** - * Here, the usage of the project information key does not lead to its initialization! - */ - private[this] val projectInformationKeyInitializationData = { - new ConcurrentHashMap[ProjectInformationKey[AnyRef, AnyRef], AnyRef]() - } - - /** - * Returns the project specific initialization information for the given project information - * key. - */ - def getProjectInformationKeyInitializationData[T <: AnyRef, I <: AnyRef]( - key: ProjectInformationKey[T, I] - ): Option[I] = { - Option(projectInformationKeyInitializationData.get(key).asInstanceOf[I]) - } - - /** - * Gets the project information key specific initialization object. If an object is already - * registered, that object will be used otherwise `info` will be evaluated and that value - * will be added and also returned. - * - * @note Initialization data is discarded once the key is used. - */ - def getOrCreateProjectInformationKeyInitializationData[T <: AnyRef, I <: AnyRef]( - key: ProjectInformationKey[T, I], - info: => I - ): I = { - projectInformationKeyInitializationData.computeIfAbsent( - key.asInstanceOf[ProjectInformationKey[AnyRef, AnyRef]], - new java.util.function.Function[ProjectInformationKey[AnyRef, AnyRef], I] { - def apply(key: ProjectInformationKey[AnyRef, AnyRef]): I = info - } - ).asInstanceOf[I] - } - - /** - * Updates project information key specific initialization object. If an object is already - * registered, that object will be given to `info`. - * - * @note Initialization data is discarded once the key is used. - */ - def updateProjectInformationKeyInitializationData[T <: AnyRef, I <: AnyRef]( - key: ProjectInformationKey[T, I] - )( - info: Option[I] => I - ): I = { - projectInformationKeyInitializationData.compute( - key.asInstanceOf[ProjectInformationKey[AnyRef, AnyRef]], - (_, current: AnyRef) => { - info(Option(current.asInstanceOf[I])) - }: I - ).asInstanceOf[I] - } - - /** - * Returns the additional project information that is ''currently'' available. - * - * If some analyses are still running it may be possible that additional - * information will be made available as part of the execution of those - * analyses. - * - * @note This method redetermines the available project information on each call. - */ - def availableProjectInformation: List[AnyRef] = { - var pis = List.empty[AnyRef] - val projectInformation = this.projectInformation - for (i <- (0 until projectInformation.length())) { - val pi = projectInformation.get(i) - if (pi != null) { - pis = pi :: pis - } - } - pis - } - - /** - * Returns the information attached to this project that is identified by the - * given `ProjectInformationKey`. - * - * If the information was not yet required, the information is computed and - * returned. Subsequent calls will directly return the information. - * - * @note (Development Time) - * Every analysis using [[ProjectInformationKey]]s must list '''All - * requirements; failing to specify a requirement can end up in a deadlock.''' - * - * @see [[ProjectInformationKey]] for further information. - */ - def get[T <: AnyRef](pik: ProjectInformationKey[T, _]): T = { - val pikUId = pik.uniqueId - - /* Synchronization is done by the caller! */ - def derive(projectInformation: AtomicReferenceArray[AnyRef]): T = { - var className = pik.getClass.getSimpleName - if (className.endsWith("Key")) - className = className.substring(0, className.length - 3) - else if (className.endsWith("Key$")) - className = className.substring(0, className.length - 4) - - for (requiredProjectInformationKey <- pik.requirements(this)) { - get(requiredProjectInformationKey) - } - val pi = time { - val pi = pik.compute(this) - // we don't need the initialization data anymore - projectInformationKeyInitializationData.remove(pik) - pi - } { t => info("project", s"initialization of $className took ${t.toSeconds}") } - projectInformation.set(pikUId, pi) - pi - } - - val projectInformation = this.projectInformation - if (pikUId < projectInformation.length()) { - val pi = projectInformation.get(pikUId) - if (pi ne null) { - pi.asInstanceOf[T] - } else { - this.synchronized { - // It may be the case that the underlying array was replaced! - val projectInformation = this.projectInformation - // double-checked locking (works with Java >=6) - val pi = projectInformation.get(pikUId) - if (pi ne null) { - pi.asInstanceOf[T] - } else { - derive(projectInformation) - } - } - } - } else { - // We have to synchronize w.r.t. "this" object on write accesses - // to make sure that we do not loose a concurrent update or - // derive an information more than once. - this.synchronized { - val projectInformation = this.projectInformation - if (pikUId >= projectInformation.length()) { - val newLength = Math.max(projectInformation.length * 2, pikUId * 2) - val newProjectInformation = new AtomicReferenceArray[AnyRef](newLength) - org.opalj.control.iterateUntil(0, projectInformation.length()) { i => - newProjectInformation.set(i, projectInformation.get(i)) - } - this.projectInformation = newProjectInformation - return derive(newProjectInformation); - } - } - // else (pikUId < projectInformation.length()) => the underlying array is "large enough" - get(pik) - } - } - - /** - * Tests if the information identified by the given [[ProjectInformationKey]] - * is available. If the information is not (yet) available, the information - * will not be computed; `None` will be returned. - * - * @see [[ProjectInformationKey]] for further information. - */ - def has[T <: AnyRef](pik: ProjectInformationKey[T, _]): Option[T] = { - val pikUId = pik.uniqueId - - if (pikUId < this.projectInformation.length()) - Option(this.projectInformation.get(pikUId).asInstanceOf[T]) - else - None - } - OPALLogger.debug("progress", s"project created (${logContext.logContextId})") /* ------------------------------------------------------------------------------------------ *\ diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectBasedAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectBasedAnalysis.scala index b6964f7805..e65c4acf7a 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectBasedAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectBasedAnalysis.scala @@ -3,7 +3,7 @@ package org.opalj package br package analyses -import org.opalj.log.LogContext +import org.opalj.si.FPCFAnalysis /** * Common super trait of all analyses that use the fixpoint @@ -14,14 +14,13 @@ import org.opalj.log.LogContext * @author Michael Reif * @author Michael Eichberg */ -trait ProjectBasedAnalysis { +trait ProjectBasedAnalysis extends FPCFAnalysis { val project: SomeProject - implicit final def p: SomeProject = project - + override implicit def p: SomeProject = project implicit final def classHierarchy: ClassHierarchy = project.classHierarchy final def ch: ClassHierarchy = classHierarchy - implicit final def logContext: LogContext = project.logContext - } + +abstract class DefaultFPCFAnalysis(val project: SomeProject) extends ProjectBasedAnalysis diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectIndexKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectIndexKey.scala index ce0a9551c0..a503de3c28 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectIndexKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectIndexKey.scala @@ -9,17 +9,16 @@ package analyses * @example * To get the index use the [[Project]]'s `get` method and pass in * `this` object. - * * @author Michael Eichberg */ -object ProjectIndexKey extends ProjectInformationKey[ProjectIndex, Nothing] { +object ProjectIndexKey extends JavaProjectInformationKey[ProjectIndex, Nothing] { /** * The [[ProjectIndex]] has no special prerequisites. * * @return `Nil`. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[Nothing, Nothing]] = Nil /** * Computes the [[ProjectIndex]] for the given project. diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/StringConstantsInformationKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/StringConstantsInformationKey.scala index fbe2c86171..f8fbe91a03 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/StringConstantsInformationKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/StringConstantsInformationKey.scala @@ -22,14 +22,14 @@ import scala.collection.mutable * @author Michael Eichberg */ object StringConstantsInformationKey - extends ProjectInformationKey[StringConstantsInformation, Nothing] { + extends JavaProjectInformationKey[StringConstantsInformation, Nothing] { /** * The analysis has no special prerequisites. * * @return `Nil`. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[Nothing, Nothing]] = Nil /** * Computes the field access information. diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/VirtualFormalParametersKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/VirtualFormalParametersKey.scala index 4d43ee6d3a..2b4b503078 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/VirtualFormalParametersKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/VirtualFormalParametersKey.scala @@ -41,12 +41,12 @@ class VirtualFormalParameters private[analyses] ( * `this` object. * @author Florian Kuebler */ -object VirtualFormalParametersKey extends ProjectInformationKey[VirtualFormalParameters, Nothing] { +object VirtualFormalParametersKey extends JavaProjectInformationKey[VirtualFormalParameters, Nothing] { /** * The key uses the `VirtualForwardingMethodsKey`. */ - override def requirements(project: SomeProject): ProjectInformationKeys = List(DeclaredMethodsKey) + override def requirements(project: SomeProject): JavaProjectInformationKeys = List(DeclaredMethodsKey) /** * Collects all virtual formal parameters. diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/CallBySignatureKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/CallBySignatureKey.scala index 7ab27a5a40..6fa5a1426b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/CallBySignatureKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/CallBySignatureKey.scala @@ -1,10 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.br.analyses.cg -import org.opalj.br.analyses.ProjectIndexKey -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{JavaProjectInformationKey, JavaProjectInformationKeys, ProjectIndexKey, SomeProject} import org.opalj.br.Method import org.opalj.br.ObjectType @@ -23,9 +20,9 @@ import scala.collection.mutable.ListBuffer * * @author Michael Reif */ -object CallBySignatureKey extends ProjectInformationKey[CallBySignatureTargets, Nothing] { +object CallBySignatureKey extends JavaProjectInformationKey[CallBySignatureTargets, Nothing] { - override def requirements(project: SomeProject): ProjectInformationKeys = List( + override def requirements(project: SomeProject): JavaProjectInformationKeys = List( ProjectIndexKey, ClosedPackagesKey, ClassExtensibilityKey, diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/ClassExtensibilityKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/ClassExtensibilityKey.scala index 2b657afc73..dfa8e298fa 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/ClassExtensibilityKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/ClassExtensibilityKey.scala @@ -5,7 +5,6 @@ package analyses package cg import net.ceedubs.ficus.Ficus._ - import org.opalj.log.OPALLogger /** @@ -15,7 +14,7 @@ import org.opalj.log.OPALLogger * @see [[ClassExtensibility]] for further information. * @author Michael Reif */ -object ClassExtensibilityKey extends ProjectInformationKey[ClassExtensibility, Nothing] { +object ClassExtensibilityKey extends JavaProjectInformationKey[ClassExtensibility, Nothing] { final val ConfigKeyPrefix = "org.opalj.br.analyses.cg.ClassExtensibilityKey." @@ -26,7 +25,7 @@ object ClassExtensibilityKey extends ProjectInformationKey[ClassExtensibility, N /** * The [[ClassExtensibilityKey]] has the [[ClosedPackagesKey]] as prerequisite. */ - override def requirements(project: SomeProject): ProjectInformationKeys = Seq(ClosedPackagesKey) + override def requirements(project: SomeProject): JavaProjectInformationKeys = Seq(ClosedPackagesKey) /** * Computes the direct type extensibility information for the given project. diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/ClosedPackagesKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/ClosedPackagesKey.scala index a72f97e33a..d2ea81cfcf 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/ClosedPackagesKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/ClosedPackagesKey.scala @@ -5,7 +5,6 @@ package analyses package cg import net.ceedubs.ficus.Ficus._ - import org.opalj.log.OPALLogger.error /** @@ -35,7 +34,7 @@ import org.opalj.log.OPALLogger.error * * @author Michael Reif */ -object ClosedPackagesKey extends ProjectInformationKey[ClosedPackages, Nothing] { +object ClosedPackagesKey extends JavaProjectInformationKey[ClosedPackages, Nothing] { final val ConfigKeyPrefix = "org.opalj.br.analyses.cg.ClosedPackagesKey." @@ -46,7 +45,7 @@ object ClosedPackagesKey extends ProjectInformationKey[ClosedPackages, Nothing] * * @return `Nil`. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[Nothing, Nothing]] = Nil /** * Reflectively instantiates a ''ClosedPackagesAnalysis'' for the given project. diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala index 23d96c1298..cbf2456d4b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala @@ -348,4 +348,44 @@ object AllEntryPointsFinder extends EntryPointFinder { project.allProjectClassFiles.flatMap(_.methodsWithBody) else project.allMethodsWithBody } +} + +/** + * The AndroidEntryPointFinder considers specific methods of app components as entry points. + * It does not work for androidx + * + * @author Tom Nikisch + */ +object AndroidEntryPointsFinder extends EntryPointFinder { + + val activityEPS: List[String] = List("onCreate", "onRestart", "onStart", "onResume", + "onStop", "onDestroy", "onActivityResult") + val serviceEPS: List[String] = List("onCreate", "onStartCommand", "onBind", "onStart") + val contentProviderEPS: List[String] = List("onCreate", "query", "insert", "update") + val locationListenerEPS: List[String] = List("onLocationChanged", "onProviderDisabled", "onProviderEnabled", + "onStatusChanged") + val onNmeaMessageListenerEPS: List[String] = List("onNmeaMessage") + val defaultEPS = Map("android/app/Activity" -> activityEPS, "android/app/Service" -> serviceEPS, + "android/content/ContentProvider" -> contentProviderEPS, + "android/location/LocationListener" -> locationListenerEPS, + "android/location/onNmeaMessageListener" -> onNmeaMessageListenerEPS) + + override def collectEntryPoints(project: SomeProject): Iterable[Method] = { + val eps = ArrayBuffer.empty[Method] + for ((superClass, methodList) <- defaultEPS) { + eps ++= findEPS(ObjectType(superClass), methodList, project) + } + eps + } + + def findEPS(ot: ObjectType, possibleEPS: List[String], project: SomeProject): ArrayBuffer[Method] = { + val eps = ArrayBuffer.empty[Method] + val classHierarchy = project.classHierarchy + classHierarchy.foreachSubclass(ot, project) { sc => + for (pep <- possibleEPS; m <- sc.findMethod(pep) if m.body.isDefined && !eps.contains(m)) { + eps += m + } + } + eps + } } \ No newline at end of file diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/InitialEntryPointsKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/InitialEntryPointsKey.scala index 42d9385d1c..ceba6247f2 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/InitialEntryPointsKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/InitialEntryPointsKey.scala @@ -32,7 +32,7 @@ import net.ceedubs.ficus.Ficus._ * * @author Michael Reif */ -object InitialEntryPointsKey extends ProjectInformationKey[Iterable[Method], Nothing] { +object InitialEntryPointsKey extends JavaProjectInformationKey[Iterable[Method], Nothing] { final val ConfigKeyPrefix = "org.opalj.br.analyses.cg.InitialEntryPointsKey." final val ConfigKey = ConfigKeyPrefix+"analysis" @@ -44,7 +44,7 @@ object InitialEntryPointsKey extends ProjectInformationKey[Iterable[Method], Not * * @return `Nil`. */ - override def requirements(project: SomeProject): ProjectInformationKeys = { + override def requirements(project: SomeProject): JavaProjectInformationKeys = { Seq(TypeExtensibilityKey, ClosedPackagesKey, IsOverridableMethodKey) } diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/InitialInstantiatedTypesKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/InitialInstantiatedTypesKey.scala index 5a4d3523c9..48c0f0b0bc 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/InitialInstantiatedTypesKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/InitialInstantiatedTypesKey.scala @@ -24,11 +24,11 @@ import net.ceedubs.ficus.Ficus._ * * @author Florian Kuebler */ -object InitialInstantiatedTypesKey extends ProjectInformationKey[Iterable[ObjectType], Nothing] { +object InitialInstantiatedTypesKey extends JavaProjectInformationKey[Iterable[ObjectType], Nothing] { final val ConfigKeyPrefix = "org.opalj.br.analyses.cg.InitialInstantiatedTypesKey." - override def requirements(project: SomeProject): ProjectInformationKeys = Seq(ClosedPackagesKey) + override def requirements(project: SomeProject): JavaProjectInformationKeys = Seq(ClosedPackagesKey) override def compute(project: SomeProject): Iterable[ObjectType] = { val key = ConfigKeyPrefix+"analysis" diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/IsOverridableMethodKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/IsOverridableMethodKey.scala index 4e256f65c2..a93cdce684 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/IsOverridableMethodKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/IsOverridableMethodKey.scala @@ -13,14 +13,14 @@ package cg * * @author Michael Reif */ -object IsOverridableMethodKey extends ProjectInformationKey[Method => Answer, Nothing] { +object IsOverridableMethodKey extends JavaProjectInformationKey[Method => Answer, Nothing] { /** * The [[IsOverridableMethodKey]] has the [[TypeExtensibilityKey]] as prerequisite. * * @return Seq(TypeExtensibilityKey). */ - override def requirements(project: SomeProject): ProjectInformationKeys = Seq(TypeExtensibilityKey) + override def requirements(project: SomeProject): JavaProjectInformationKeys = Seq(TypeExtensibilityKey) override def compute(project: SomeProject): Method => Answer = { new IsOverridableMethodAnalysis( diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/TypeExtensibilityKey.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/TypeExtensibilityKey.scala index ddced02b9c..0f557aa269 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/TypeExtensibilityKey.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/TypeExtensibilityKey.scala @@ -10,10 +10,11 @@ package cg * A type is extensible if a developer could define a sub(*)type that is not part of the given * application/library. * + * * @author Michael Eichberg * @author Michael Reif */ -object TypeExtensibilityKey extends ProjectInformationKey[ObjectType => Answer, Nothing] { +object TypeExtensibilityKey extends JavaProjectInformationKey[ObjectType => Answer, Nothing] { /** * The [[TypeExtensibilityKey]] has the [[ClassExtensibilityKey]] as prerequisite. diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/package.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/package.scala index 0a1852ad63..197cb1fbcd 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/package.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/package.scala @@ -2,8 +2,9 @@ package org.opalj package br -import scala.collection.Map +import org.opalj.si.{MetaProject, ProjectInformationKey} +import scala.collection.Map import scala.collection.immutable.ArraySeq /** @@ -20,7 +21,8 @@ package object analyses { type ProgressEvent = ProgressEvents.Value - type ProjectInformationKeys = Seq[ProjectInformationKey[_ <: AnyRef, _ <: AnyRef]] + //trait JavaProjectInformationKeys extends Seq[JavaProjectInformationKey[_ <: AnyRef, _ <: AnyRef]] + type JavaProjectInformationKeys = Seq[ProjectInformationKey[_ <: MetaProject, _ <: AnyRef, _ <: AnyRef]] type StringConstantsInformation = Map[String, ArraySeq[PCInMethod]] diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysesManagerKey.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysesManagerKey.scala deleted file mode 100644 index 68fc25dd4d..0000000000 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysesManagerKey.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf - -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject - -/** - * The ''key'' object to get the [[FPCFAnalysesManager]]. - * - * @example - * To get an instance of the [[FPCFAnalysesManager]] pass this key to a - * project's `get` method. - * - * @author Michael Reif - */ -object FPCFAnalysesManagerKey extends ProjectInformationKey[FPCFAnalysesManager, Nothing] { - - override def requirements(project: SomeProject): ProjectInformationKeys = List(PropertyStoreKey) - - override def compute(project: SomeProject): FPCFAnalysesManager = new FPCFAnalysesManager(project) - -} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/JavaFPCFAnalysisScheduler.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/JavaFPCFAnalysisScheduler.scala new file mode 100644 index 0000000000..ce60757d89 --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/JavaFPCFAnalysisScheduler.scala @@ -0,0 +1,55 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package br +package fpcf + +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.scheduling.{BasicFPCFEagerAnalysisScheduler, BasicFPCFLazyAnalysisScheduler, BasicFPCFTransformerScheduler, BasicFPCFTriggeredAnalysisScheduler, FPCFAnalysisScheduler, FPCFEagerAnalysisScheduler, FPCFLazyAnalysisScheduler, FPCFTransformerScheduler, FPCFTriggeredAnalysisScheduler} + +import scala.reflect.ClassTag + +trait JavaFPCFAnalysisScheduler extends FPCFAnalysisScheduler[SomeProject] { + implicit val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} + +trait JavaFPCFEagerAnalysisScheduler + extends FPCFEagerAnalysisScheduler[SomeProject] + with JavaFPCFAnalysisScheduler { + implicit override val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} + +trait JavaFPCFLazyAnalysisScheduler + extends FPCFLazyAnalysisScheduler[SomeProject] + with JavaFPCFAnalysisScheduler { + implicit override val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} + +trait JavaFPCFTriggeredAnalysisScheduler + extends FPCFTriggeredAnalysisScheduler[SomeProject] + with JavaFPCFAnalysisScheduler { + implicit override val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} + +trait JavaBasicFPCFLazyAnalysisScheduler + extends BasicFPCFLazyAnalysisScheduler[SomeProject] + with JavaFPCFAnalysisScheduler + with JavaFPCFLazyAnalysisScheduler { + implicit override val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} + +trait JavaBasicFPCFEagerAnalysisScheduler + extends BasicFPCFEagerAnalysisScheduler[SomeProject] + with JavaFPCFEagerAnalysisScheduler + with JavaFPCFAnalysisScheduler { + implicit override val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} + +trait JavaBasicFPCFTriggeredAnalysisScheduler + extends BasicFPCFTriggeredAnalysisScheduler[SomeProject] + with JavaFPCFAnalysisScheduler { + implicit override val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} + +trait JavaFPCFTransformerScheduler extends FPCFTransformerScheduler[SomeProject] + +trait JavaBasicFPCFTransformerScheduler extends BasicFPCFTransformerScheduler[SomeProject] with JavaFPCFTransformerScheduler \ No newline at end of file diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/JavaIFDSAnalysisScheduler.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/JavaIFDSAnalysisScheduler.scala new file mode 100644 index 0000000000..ad4975e37b --- /dev/null +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/JavaIFDSAnalysisScheduler.scala @@ -0,0 +1,14 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package br +package fpcf + +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.ifds.{AbstractIFDSFact, IFDSAnalysisScheduler, Statement} + +import scala.reflect.ClassTag + +trait JavaIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]] + extends IFDSAnalysisScheduler[SomeProject, IFDSFact, C, S] { + implicit val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ClassImmutabilityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ClassImmutabilityAnalysis.scala index 2ae887e9a3..6c7a54a685 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ClassImmutabilityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ClassImmutabilityAnalysis.scala @@ -28,8 +28,7 @@ import org.opalj.fpcf.PropertyComputation import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.ClassImmutability import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.FinalField @@ -43,6 +42,9 @@ import org.opalj.br.fpcf.properties.MutableObjectDueToUnknownSupertypes import org.opalj.br.fpcf.properties.MutableType import org.opalj.br.fpcf.properties.NonFinalField import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.si.{FPCFAnalysis, MetaProject, ProjectInformationKey} + +import scala.reflect.ClassTag /** * Determines the mutability of instances of a specific class. In case the class @@ -66,7 +68,7 @@ import org.opalj.br.fpcf.properties.TypeImmutability * @author Florian Kübler * @author Dominik Helm */ -class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { +class ClassImmutabilityAnalysis(val project: SomeProject) extends ProjectBasedAnalysis { /* * The analysis is implemented as an incremental analysis which starts with the analysis * of those types which directly inherit from java.lang.Object and then propagates the @@ -402,9 +404,9 @@ class ClassImmutabilityAnalysis(val project: SomeProject) extends FPCFAnalysis { } } -trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +trait ClassImmutabilityAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq.empty + override def requiredProjectInformation: Seq[ProjectInformationKey[MetaProject, Nothing, Nothing]] = Seq.empty final def derivedProperty: PropertyBounds = PropertyBounds.lub(ClassImmutability) @@ -494,7 +496,9 @@ trait ClassImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { */ object EagerClassImmutabilityAnalysis extends ClassImmutabilityAnalysisScheduler - with FPCFEagerAnalysisScheduler { + with JavaFPCFEagerAnalysisScheduler { + + override implicit val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -521,7 +525,9 @@ object EagerClassImmutabilityAnalysis */ object LazyClassImmutabilityAnalysis extends ClassImmutabilityAnalysisScheduler - with FPCFLazyAnalysisScheduler { + with JavaFPCFLazyAnalysisScheduler { + + override implicit val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ConfiguredPurity.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ConfiguredPurity.scala index c2cfc93742..9a45054452 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ConfiguredPurity.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/ConfiguredPurity.scala @@ -6,14 +6,10 @@ package analyses import net.ceedubs.ficus.Ficus._ import net.ceedubs.ficus.readers.ArbitraryTypeReader._ - -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.ClassExtensibilityKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, JavaProjectInformationKey, JavaProjectInformationKeys, SomeProject} import org.opalj.br.fpcf.properties.Purity +import org.opalj.si.PropertyStoreKey /** * @author Dominik Helm @@ -83,9 +79,9 @@ class ConfiguredPurity( } -object ConfiguredPurityKey extends ProjectInformationKey[ConfiguredPurity, Nothing] { +object ConfiguredPurityKey extends JavaProjectInformationKey[ConfiguredPurity, Nothing] { - override def requirements(project: SomeProject): ProjectInformationKeys = Seq(PropertyStoreKey, DeclaredMethodsKey) + override def requirements(project: SomeProject): JavaProjectInformationKeys = Seq(PropertyStoreKey, DeclaredMethodsKey) override def compute(project: SomeProject): ConfiguredPurity = { val declaredMethods = project.get(DeclaredMethodsKey) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0AllocationFreenessAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0AllocationFreenessAnalysis.scala index f170a460ec..b0e3f99dc4 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0AllocationFreenessAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0AllocationFreenessAnalysis.scala @@ -4,28 +4,14 @@ package br package fpcf package analyses -import scala.annotation.switch -import org.opalj.fpcf.Entity -import org.opalj.fpcf.EOptionP -import org.opalj.fpcf.FinalP -import org.opalj.fpcf.InterimLUBP -import org.opalj.fpcf.InterimResult -import org.opalj.fpcf.InterimUBP -import org.opalj.fpcf.ProperPropertyComputationResult -import org.opalj.fpcf.Property -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.Result -import org.opalj.fpcf.SomeEOptionP -import org.opalj.fpcf.SomeEPS -import org.opalj.fpcf.SomeInterimEP -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.properties.AllocationFreeMethod -import org.opalj.br.fpcf.properties.AllocationFreeness -import org.opalj.br.fpcf.properties.MethodWithAllocations +import org.opalj.br.analyses.{DeclaredMethodsKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} +import org.opalj.br.fpcf.properties.{AllocationFreeMethod, AllocationFreeness, MethodWithAllocations} import org.opalj.br.instructions._ +import org.opalj.fpcf.{EOptionP, Entity, FinalP, InterimLUBP, InterimResult, InterimUBP, ProperPropertyComputationResult, Property, PropertyBounds, PropertyStore, Result, SomeEOptionP, SomeEPS, SomeInterimEP} +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler +import org.opalj.si.FPCFAnalysis + +import scala.annotation.switch /** * A simple analysis that identifies methods that never allocate any objects/arrays. @@ -34,8 +20,7 @@ import org.opalj.br.instructions._ */ class L0AllocationFreenessAnalysis private[analyses] ( final val project: SomeProject -) - extends FPCFAnalysis { +) extends ProjectBasedAnalysis { import project.nonVirtualCall @@ -248,9 +233,9 @@ class L0AllocationFreenessAnalysis private[analyses] ( } } -trait L0AllocationFreenessAnalysisScheduler extends FPCFAnalysisScheduler { +trait L0AllocationFreenessAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey) final override def uses: Set[PropertyBounds] = Set.empty @@ -260,7 +245,7 @@ trait L0AllocationFreenessAnalysisScheduler extends FPCFAnalysisScheduler { object EagerL0AllocationFreenessAnalysis extends L0AllocationFreenessAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -278,7 +263,7 @@ object EagerL0AllocationFreenessAnalysis object LazyL0AllocationFreenessAnalysis extends L0AllocationFreenessAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0CompileTimeConstancyAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0CompileTimeConstancyAnalysis.scala index 7aa4bf0f73..edb7e96b73 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0CompileTimeConstancyAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0CompileTimeConstancyAnalysis.scala @@ -15,8 +15,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.SomeInterimEP -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.CompileTimeConstancy import org.opalj.br.fpcf.properties.CompileTimeConstantField import org.opalj.br.fpcf.properties.CompileTimeVaryingField @@ -24,6 +23,7 @@ import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.FinalField import org.opalj.br.fpcf.properties.LazyInitializedField import org.opalj.br.fpcf.properties.NonFinalField +import org.opalj.si.FPCFAnalysis /** * A simple analysis that identifies constant (effectively) final static fields that are @@ -33,7 +33,7 @@ import org.opalj.br.fpcf.properties.NonFinalField * @author Dominik Helm */ class L0CompileTimeConstancyAnalysis private[analyses] ( final val project: SomeProject) - extends FPCFAnalysis { + extends ProjectBasedAnalysis { /** * Determines the compile-time constancy of the field. @@ -89,9 +89,9 @@ class L0CompileTimeConstancyAnalysis private[analyses] ( final val project: Some } } -trait L0CompileTimeConstancyAnalysisScheduler extends FPCFAnalysisScheduler { +trait L0CompileTimeConstancyAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq.empty + override def requiredProjectInformation: JavaProjectInformationKeys = Seq.empty final override def uses: Set[PropertyBounds] = PropertyBounds.lubs(FieldMutability) @@ -101,7 +101,7 @@ trait L0CompileTimeConstancyAnalysisScheduler extends FPCFAnalysisScheduler { object EagerL0CompileTimeConstancyAnalysis extends L0CompileTimeConstancyAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -116,7 +116,7 @@ object EagerL0CompileTimeConstancyAnalysis object LazyL0CompileTimeConstancyAnalysis extends L0CompileTimeConstancyAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0FieldMutabilityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0FieldMutabilityAnalysis.scala index a5495a77c1..4be9583c73 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0FieldMutabilityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0FieldMutabilityAnalysis.scala @@ -9,15 +9,14 @@ import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result -import org.opalj.br.analyses.FieldAccessInformationKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{FieldAccessInformationKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.DeclaredFinalField import org.opalj.br.fpcf.properties.EffectivelyFinalField import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.NonFinalFieldByAnalysis import org.opalj.br.fpcf.properties.NonFinalFieldByLackOfInformation import org.opalj.br.instructions.PUTSTATIC +import org.opalj.si.FPCFAnalysis /** * Determines if a private, static, non-final field is always initialized at most once or @@ -26,7 +25,7 @@ import org.opalj.br.instructions.PUTSTATIC * available data-store) are not considered. This is in-line with the semantics of final, * which also does not prevent reads of partially initialized objects. */ -class L0FieldMutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { +class L0FieldMutabilityAnalysis private[analyses] (val project: SomeProject) extends ProjectBasedAnalysis { final val fieldAccessInformation = project.get(FieldAccessInformationKey) @@ -95,9 +94,9 @@ class L0FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } } -trait L0FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L0FieldMutabilityAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(FieldAccessInformationKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(FieldAccessInformationKey) final override def uses: Set[PropertyBounds] = Set.empty @@ -113,7 +112,7 @@ trait L0FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { */ object EagerL0FieldMutabilityAnalysis extends L0FieldMutabilityAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -136,7 +135,7 @@ object EagerL0FieldMutabilityAnalysis object LazyL0FieldMutabilityAnalysis extends L0FieldMutabilityAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0PurityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0PurityAnalysis.scala index 048e950ed5..6a4c125683 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0PurityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0PurityAnalysis.scala @@ -5,7 +5,6 @@ package fpcf package analyses import scala.annotation.switch - import org.opalj.fpcf.Entity import org.opalj.fpcf.EOptionP import org.opalj.fpcf.EPS @@ -22,10 +21,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPS -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.CompileTimePure import org.opalj.br.fpcf.properties.Context import org.opalj.br.fpcf.properties.FieldMutability @@ -42,6 +38,7 @@ import org.opalj.br.fpcf.properties.SimpleContexts import org.opalj.br.fpcf.properties.SimpleContextsKey import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.br.instructions._ +import org.opalj.si.FPCFAnalysis /** * Very simple, fast, sound but also imprecise analysis of the purity of methods. See the @@ -55,7 +52,7 @@ import org.opalj.br.instructions._ * @author Michael Eichberg * @author Dominik Helm */ -class L0PurityAnalysis private[analyses] ( final val project: SomeProject) extends FPCFAnalysis { +class L0PurityAnalysis private[analyses] ( final val project: SomeProject) extends ProjectBasedAnalysis { import project.nonVirtualCall import project.resolveFieldReference @@ -340,9 +337,9 @@ class L0PurityAnalysis private[analyses] ( final val project: SomeProject) exten } -trait L0PurityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L0PurityAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, SimpleContextsKey) final override def uses: Set[PropertyBounds] = { @@ -355,7 +352,7 @@ trait L0PurityAnalysisScheduler extends FPCFAnalysisScheduler { object EagerL0PurityAnalysis extends L0PurityAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -376,7 +373,7 @@ object EagerL0PurityAnalysis object LazyL0PurityAnalysis extends L0PurityAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0SelfReferenceLeakageAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0SelfReferenceLeakageAnalysis.scala index 7f21fc8f9c..488b5aafa0 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0SelfReferenceLeakageAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L0SelfReferenceLeakageAnalysis.scala @@ -20,8 +20,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.DoesNotLeakSelfReference import org.opalj.br.fpcf.properties.LeaksSelfReference import org.opalj.br.fpcf.properties.SelfReferenceLeakage @@ -36,6 +35,7 @@ import org.opalj.br.instructions.INVOKEVIRTUAL import org.opalj.br.instructions.MethodInvocationInstruction import org.opalj.br.instructions.PUTFIELD import org.opalj.br.instructions.PUTSTATIC +import org.opalj.si.FPCFAnalysis /** * A shallow analysis that computes the self reference leakage property. @@ -45,7 +45,7 @@ import org.opalj.br.instructions.PUTSTATIC class L0SelfReferenceLeakageAnalysis( val project: SomeProject, val debug: Boolean -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { val SelfReferenceLeakageKey = SelfReferenceLeakage.Key @@ -213,9 +213,9 @@ class L0SelfReferenceLeakageAnalysis( } } -object L0SelfReferenceLeakageAnalysis extends BasicFPCFEagerAnalysisScheduler { +object L0SelfReferenceLeakageAnalysis extends JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq.empty + override def requiredProjectInformation: JavaProjectInformationKeys = Seq.empty override def uses: Set[PropertyBounds] = Set.empty diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L1ThrownExceptionsAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L1ThrownExceptionsAnalysis.scala index 31297bf4c9..fa474d4ba1 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L1ThrownExceptionsAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/L1ThrownExceptionsAnalysis.scala @@ -10,7 +10,7 @@ import org.opalj.br.collection.mutable.{TypesSet => BRMutableTypesSet} import org.opalj.br.ObjectType import org.opalj.br.Method import org.opalj.br.MethodDescriptor -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.instructions.Instruction import org.opalj.br.instructions.ATHROW import org.opalj.br.instructions.INVOKESPECIAL @@ -68,7 +68,7 @@ import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.si.FPCFAnalysis /** * Analysis of thrown exceptions; computes the [[org.opalj.br.fpcf.properties.ThrownExceptions]] @@ -78,7 +78,7 @@ import org.opalj.br.analyses.ProjectInformationKeys */ class L1ThrownExceptionsAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { private[analyses] def lazilyDetermineThrownExceptions( e: Entity @@ -389,9 +389,9 @@ class L1ThrownExceptionsAnalysis private[analyses] ( } } -abstract class ThrownExceptionsAnalysisScheduler extends FPCFAnalysisScheduler { +abstract class ThrownExceptionsAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq.empty + override def requiredProjectInformation: JavaProjectInformationKeys = Seq.empty final override def uses: Set[PropertyBounds] = { Set(PropertyBounds.lub(ThrownExceptionsByOverridingMethods)) @@ -409,7 +409,7 @@ abstract class ThrownExceptionsAnalysisScheduler extends FPCFAnalysisScheduler { */ object EagerL1ThrownExceptionsAnalysis extends ThrownExceptionsAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -435,7 +435,7 @@ object EagerL1ThrownExceptionsAnalysis */ object LazyL1ThrownExceptionsAnalysis extends ThrownExceptionsAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/StaticDataUsageAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/StaticDataUsageAnalysis.scala index 45383f67ce..d8e7e2b808 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/StaticDataUsageAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/StaticDataUsageAnalysis.scala @@ -5,7 +5,6 @@ package fpcf package analyses import scala.annotation.switch - import org.opalj.fpcf.Entity import org.opalj.fpcf.EOptionP import org.opalj.fpcf.EPS @@ -22,9 +21,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPS -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{DeclaredMethodsKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.CompileTimeConstancy import org.opalj.br.fpcf.properties.CompileTimeConstantField import org.opalj.br.fpcf.properties.CompileTimeVaryingField @@ -34,6 +31,7 @@ import org.opalj.br.fpcf.properties.UsesConstantDataOnly import org.opalj.br.fpcf.properties.UsesNoStaticData import org.opalj.br.fpcf.properties.UsesVaryingData import org.opalj.br.instructions._ +import org.opalj.si.FPCFAnalysis /** * A simple analysis that identifies methods that use global state that may vary during one or @@ -42,7 +40,7 @@ import org.opalj.br.instructions._ * @author Dominik Helm */ class StaticDataUsageAnalysis private[analyses] ( final val project: SomeProject) - extends FPCFAnalysis { + extends ProjectBasedAnalysis { import project.nonVirtualCall import project.resolveFieldReference @@ -227,9 +225,9 @@ class StaticDataUsageAnalysis private[analyses] ( final val project: SomeProject } } -trait StaticDataUsageAnalysisScheduler extends FPCFAnalysisScheduler { +trait StaticDataUsageAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey) final def derivedProperty: PropertyBounds = { // FIXME Just seems to derive the upper bound... @@ -245,7 +243,7 @@ trait StaticDataUsageAnalysisScheduler extends FPCFAnalysisScheduler { object EagerStaticDataUsageAnalysis extends StaticDataUsageAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -263,7 +261,7 @@ object EagerStaticDataUsageAnalysis object LazyStaticDataUsageAnalysis extends StaticDataUsageAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/TypeImmutabilityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/TypeImmutabilityAnalysis.scala index d80a86b42e..03eb9446c7 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/TypeImmutabilityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/TypeImmutabilityAnalysis.scala @@ -22,9 +22,8 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.analyses.cg.TypeExtensibilityKey -import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.fpcf.properties.ClassImmutability import org.opalj.br.fpcf.properties.ImmutableContainer import org.opalj.br.fpcf.properties.ImmutableContainerType @@ -33,6 +32,7 @@ import org.opalj.br.fpcf.properties.ImmutableType import org.opalj.br.fpcf.properties.MutableObject import org.opalj.br.fpcf.properties.MutableType import org.opalj.br.fpcf.properties.TypeImmutability +import org.opalj.si.FPCFAnalysis /** * Determines the mutability of a specific type by checking if all subtypes of a specific @@ -40,7 +40,7 @@ import org.opalj.br.fpcf.properties.TypeImmutability * * @author Michael Eichberg */ -class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnalysis { +class TypeImmutabilityAnalysis( final val project: SomeProject) extends ProjectBasedAnalysis { def doDetermineTypeMutability( typeExtensibility: ObjectType => Answer @@ -226,9 +226,9 @@ class TypeImmutabilityAnalysis( final val project: SomeProject) extends FPCFAnal } } -trait TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +trait TypeImmutabilityAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeExtensibilityKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(TypeExtensibilityKey) final def derivedProperty: PropertyBounds = PropertyBounds.lub(TypeImmutability) @@ -244,7 +244,7 @@ trait TypeImmutabilityAnalysisScheduler extends FPCFAnalysisScheduler { */ object EagerTypeImmutabilityAnalysis extends TypeImmutabilityAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -267,7 +267,7 @@ object EagerTypeImmutabilityAnalysis object LazyTypeImmutabilityAnalysis extends TypeImmutabilityAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) /** diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/UnsoundPrematurelyReadFieldsAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/UnsoundPrematurelyReadFieldsAnalysis.scala index ceaaa5e7c2..35f8da7eae 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/UnsoundPrematurelyReadFieldsAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/UnsoundPrematurelyReadFieldsAnalysis.scala @@ -8,10 +8,10 @@ import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.FieldPrematurelyRead import org.opalj.br.fpcf.properties.NotPrematurelyReadField +import org.opalj.si.FPCFAnalysis /** * Unsound 'analysis' that declares all fields to be @@ -20,16 +20,16 @@ import org.opalj.br.fpcf.properties.NotPrematurelyReadField * @author Dominik Helm */ class UnsoundPrematurelyReadFieldsAnalysis private[analyses] (val project: SomeProject) - extends FPCFAnalysis { + extends ProjectBasedAnalysis { def determinePrematureReads(field: Field): ProperPropertyComputationResult = { Result(field, NotPrematurelyReadField) } } -trait UnsoundPrematurelyReadFieldsAnalysisScheduler extends FPCFAnalysisScheduler { +trait UnsoundPrematurelyReadFieldsAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq.empty + override def requiredProjectInformation: JavaProjectInformationKeys = Seq.empty final override def uses: Set[PropertyBounds] = Set.empty @@ -41,7 +41,7 @@ trait UnsoundPrematurelyReadFieldsAnalysisScheduler extends FPCFAnalysisSchedule */ object EagerUnsoundPrematurelyReadFieldsAnalysis extends UnsoundPrematurelyReadFieldsAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { def start(project: SomeProject, propertyStore: PropertyStore, unused: Null): FPCFAnalysis = { val analysis = new UnsoundPrematurelyReadFieldsAnalysis(project) @@ -59,13 +59,13 @@ object EagerUnsoundPrematurelyReadFieldsAnalysis object LazyUnsoundPrematurelyReadFieldsAnalysis extends UnsoundPrematurelyReadFieldsAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { def register( project: SomeProject, propertyStore: PropertyStore, unused: Null - ): FPCFAnalysis = { + ): ProjectBasedAnalysis = { val analysis = new UnsoundPrematurelyReadFieldsAnalysis(project) propertyStore.registerLazyPropertyComputation( FieldPrematurelyRead.key, diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualCallAggregatingEscapeAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualCallAggregatingEscapeAnalysis.scala index ac28cd0ab1..f785bc5ef6 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualCallAggregatingEscapeAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualCallAggregatingEscapeAnalysis.scala @@ -14,16 +14,13 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPS -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject -import org.opalj.br.analyses.VirtualFormalParameter -import org.opalj.br.analyses.VirtualFormalParametersKey +import org.opalj.br.analyses.{DeclaredMethodsKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject, VirtualFormalParameter, VirtualFormalParametersKey} import org.opalj.br.fpcf.properties.AtMost import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.br.fpcf.properties.GlobalEscape import org.opalj.br.fpcf.properties.NoEscape import org.opalj.br.fpcf.properties.VirtualMethodEscapeProperty +import org.opalj.si.FPCFAnalysis /** * Aggregates the escape information for virtual formal parameters. @@ -35,7 +32,7 @@ import org.opalj.br.fpcf.properties.VirtualMethodEscapeProperty * * @author Florian Kuebler */ -class VirtualCallAggregatingEscapeAnalysis private[analyses] ( final val project: SomeProject) extends FPCFAnalysis { +class VirtualCallAggregatingEscapeAnalysis private[analyses] ( final val project: SomeProject) extends ProjectBasedAnalysis { private[this] val formalParameters = project.get(VirtualFormalParametersKey) private[this] val declaredMethods = project.get(DeclaredMethodsKey) @@ -110,9 +107,9 @@ class VirtualCallAggregatingEscapeAnalysis private[analyses] ( final val project } -sealed trait VirtualCallAggregatingEscapeAnalysisScheduler extends FPCFAnalysisScheduler { +sealed trait VirtualCallAggregatingEscapeAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq( + override def requiredProjectInformation: JavaProjectInformationKeys = Seq( VirtualFormalParametersKey, DeclaredMethodsKey ) @@ -125,7 +122,7 @@ sealed trait VirtualCallAggregatingEscapeAnalysisScheduler extends FPCFAnalysisS object EagerVirtualCallAggregatingEscapeAnalysis extends VirtualCallAggregatingEscapeAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -141,7 +138,7 @@ object EagerVirtualCallAggregatingEscapeAnalysis object LazyVirtualCallAggregatingEscapeAnalysis extends VirtualCallAggregatingEscapeAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodAllocationFreenessAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodAllocationFreenessAnalysis.scala index 256645385e..8ab99ee92b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodAllocationFreenessAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodAllocationFreenessAnalysis.scala @@ -14,15 +14,14 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPS -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{DeclaredMethodsKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.AllocationFreeMethod import org.opalj.br.fpcf.properties.AllocationFreeness import org.opalj.br.fpcf.properties.MethodWithAllocations import org.opalj.br.fpcf.properties.VirtualMethodAllocationFreeness import org.opalj.br.fpcf.properties.VirtualMethodAllocationFreeness.VAllocationFreeMethod import org.opalj.br.fpcf.properties.VirtualMethodAllocationFreeness.VMethodWithAllocations +import org.opalj.si.FPCFAnalysis /** * Determines the aggregated allocation freeness for virtual methods. @@ -31,7 +30,7 @@ import org.opalj.br.fpcf.properties.VirtualMethodAllocationFreeness.VMethodWithA */ class VirtualMethodAllocationFreenessAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { private[this] val declaredMethods = project.get(DeclaredMethodsKey) @@ -99,9 +98,9 @@ class VirtualMethodAllocationFreenessAnalysis private[analyses] ( } -trait VirtualMethodAllocationFreenessAnalysisScheduler extends FPCFAnalysisScheduler { +trait VirtualMethodAllocationFreenessAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey) final override def uses: Set[PropertyBounds] = Set(PropertyBounds.lub(AllocationFreeness)) @@ -111,7 +110,7 @@ trait VirtualMethodAllocationFreenessAnalysisScheduler extends FPCFAnalysisSched object EagerVirtualMethodAllocationFreenessAnalysis extends VirtualMethodAllocationFreenessAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -127,7 +126,7 @@ object EagerVirtualMethodAllocationFreenessAnalysis object LazyVirtualMethodAllocationFreenessAnalysis extends VirtualMethodAllocationFreenessAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodPurityAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodPurityAnalysis.scala index e2723d4a65..78ac85ca3f 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodPurityAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodPurityAnalysis.scala @@ -14,10 +14,7 @@ import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.ClassifiedImpure import org.opalj.br.fpcf.properties.CompileTimePure import org.opalj.br.fpcf.properties.Context @@ -27,13 +24,16 @@ import org.opalj.br.fpcf.properties.SimpleContextsKey import org.opalj.br.fpcf.properties.VirtualMethodPurity import org.opalj.br.fpcf.properties.VirtualMethodPurity.VImpureByAnalysis import org.opalj.br.fpcf.properties.VirtualMethodPurity.VImpureByLackOfInformation +import org.opalj.si.FPCFAnalysis + +import scala.reflect.ClassTag /** * Determines the aggregated purity for virtual methods. * * @author Dominik Helm */ -class VirtualMethodPurityAnalysis private[analyses] ( final val project: SomeProject) extends FPCFAnalysis { +class VirtualMethodPurityAnalysis private[analyses] ( final val project: SomeProject) extends ProjectBasedAnalysis { private[this] val declaredMethods = project.get(DeclaredMethodsKey) private[this] val simpleContexts = project.get(SimpleContextsKey) @@ -107,9 +107,9 @@ class VirtualMethodPurityAnalysis private[analyses] ( final val project: SomePro } -trait VirtualMethodPurityAnalysisScheduler extends FPCFAnalysisScheduler { +trait VirtualMethodPurityAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey) final override def uses: Set[PropertyBounds] = Set(PropertyBounds.lub(Purity)) @@ -119,7 +119,9 @@ trait VirtualMethodPurityAnalysisScheduler extends FPCFAnalysisScheduler { object EagerVirtualMethodPurityAnalysis extends VirtualMethodPurityAnalysisScheduler - with FPCFEagerAnalysisScheduler { + with JavaFPCFEagerAnalysisScheduler { + + override implicit val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) @@ -147,7 +149,7 @@ object EagerVirtualMethodPurityAnalysis p: SomeProject, ps: PropertyStore, data: InitializationData - ): FPCFAnalysis = { + ): ProjectBasedAnalysis = { val analysis = new VirtualMethodPurityAnalysis(p) val (declaredMethods, simpleContexts, configuredPurity) = data @@ -164,7 +166,7 @@ object EagerVirtualMethodPurityAnalysis object LazyVirtualMethodPurityAnalysis extends VirtualMethodPurityAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodStaticDataUsageAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodStaticDataUsageAnalysis.scala index 23905f1077..9d6eeb5e36 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodStaticDataUsageAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodStaticDataUsageAnalysis.scala @@ -15,9 +15,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEOptionP import org.opalj.fpcf.SomeEPS -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{DeclaredMethodsKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.StaticDataUsage import org.opalj.br.fpcf.properties.UsesConstantDataOnly import org.opalj.br.fpcf.properties.UsesNoStaticData @@ -33,7 +31,7 @@ import org.opalj.br.fpcf.properties.VirtualMethodStaticDataUsage.VUsesVaryingDat */ class VirtualMethodStaticDataUsageAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { private[this] val declaredMethods = project.get(DeclaredMethodsKey) def determineUsage(dm: DeclaredMethod): ProperPropertyComputationResult = { @@ -115,9 +113,9 @@ class VirtualMethodStaticDataUsageAnalysis private[analyses] ( } -trait VirtualMethodStaticDataUsageAnalysisScheduler extends FPCFAnalysisScheduler { +trait VirtualMethodStaticDataUsageAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey) final override def uses: Set[PropertyBounds] = Set(PropertyBounds.lub(StaticDataUsage)) @@ -127,13 +125,13 @@ trait VirtualMethodStaticDataUsageAnalysisScheduler extends FPCFAnalysisSchedule object EagerVirtualMethodStaticDataUsageAnalysis extends VirtualMethodStaticDataUsageAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new VirtualMethodStaticDataUsageAnalysis(p) val vms = p.get(DeclaredMethodsKey) ps.scheduleEagerComputationsForEntities(vms.declaredMethods)(analysis.determineUsage) @@ -143,11 +141,11 @@ object EagerVirtualMethodStaticDataUsageAnalysis object LazyVirtualMethodStaticDataUsageAnalysis extends VirtualMethodStaticDataUsageAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new VirtualMethodStaticDataUsageAnalysis(p) ps.registerLazyPropertyComputation( VirtualMethodAllocationFreeness.key, diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodThrownExceptionsAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodThrownExceptionsAnalysis.scala index d4fa57adee..2ee72cb269 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodThrownExceptionsAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualMethodThrownExceptionsAnalysis.scala @@ -15,9 +15,8 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.analyses.cg.IsOverridableMethodKey -import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.collection.mutable.{TypesSet => BRMutableTypesSet} import org.opalj.br.fpcf.properties.ThrownExceptions import org.opalj.br.fpcf.properties.ThrownExceptions.AnalysisLimitation @@ -39,7 +38,7 @@ import org.opalj.br.fpcf.properties.ThrownExceptionsByOverridingMethods.SomeExce */ class VirtualMethodThrownExceptionsAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { private[analyses] def lazilyAggregateExceptionsThrownByOverridingMethods( e: Entity @@ -136,9 +135,9 @@ class VirtualMethodThrownExceptionsAnalysis private[analyses] ( } } -trait VirtualMethodThrownExceptionsAnalysisScheduler extends FPCFAnalysisScheduler { +trait VirtualMethodThrownExceptionsAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(IsOverridableMethodKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(IsOverridableMethodKey) final override def uses: Set[PropertyBounds] = Set(PropertyBounds.lub(ThrownExceptions)) @@ -156,13 +155,13 @@ trait VirtualMethodThrownExceptionsAnalysisScheduler extends FPCFAnalysisSchedul */ object EagerVirtualMethodThrownExceptionsAnalysis extends VirtualMethodThrownExceptionsAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new VirtualMethodThrownExceptionsAnalysis(p) val allMethods = p.allMethodsWithBody // FIXME we need this information also for abstract methods ... ps.scheduleEagerComputationsForEntities(allMethods) { @@ -181,12 +180,12 @@ object EagerVirtualMethodThrownExceptionsAnalysis */ object LazyVirtualMethodThrownExceptionsAnalysis extends VirtualMethodThrownExceptionsAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) /** Registers an analysis to compute the exceptions thrown by overriding methods lazily. */ - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new VirtualMethodThrownExceptionsAnalysis(p) ps.registerLazyPropertyComputation( ThrownExceptionsByOverridingMethods.key, diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualReturnValueFreshnessAnalysis.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualReturnValueFreshnessAnalysis.scala index 74de1a265c..fac5f7d0a5 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualReturnValueFreshnessAnalysis.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/analyses/VirtualReturnValueFreshnessAnalysis.scala @@ -15,10 +15,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.fpcf.properties.NoFreshReturnValue import org.opalj.br.fpcf.properties.PrimitiveReturnValue import org.opalj.br.fpcf.properties.ReturnValueFreshness @@ -26,6 +23,7 @@ import org.opalj.br.fpcf.properties.VFreshReturnValue import org.opalj.br.fpcf.properties.VirtualMethodReturnValueFreshness import org.opalj.br.fpcf.properties.VNoFreshReturnValue import org.opalj.br.fpcf.properties.VPrimitiveReturnValue +import org.opalj.si.FPCFAnalysis /** * An analysis that aggregates whether the return value for all possible methods represented by a @@ -35,7 +33,7 @@ import org.opalj.br.fpcf.properties.VPrimitiveReturnValue */ class VirtualReturnValueFreshnessAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { private[this] val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) @@ -113,9 +111,9 @@ class VirtualReturnValueFreshnessAnalysis private[analyses] ( } -sealed trait VirtualReturnValueFreshnessAnalysisScheduler extends FPCFAnalysisScheduler { +sealed trait VirtualReturnValueFreshnessAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey) final def derivedProperty: PropertyBounds = { PropertyBounds.lub(VirtualMethodReturnValueFreshness) @@ -127,13 +125,13 @@ sealed trait VirtualReturnValueFreshnessAnalysisScheduler extends FPCFAnalysisSc object EagerVirtualReturnValueFreshnessAnalysis extends VirtualReturnValueFreshnessAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val declaredMethods = p.get(DeclaredMethodsKey).declaredMethods val analysis = new VirtualReturnValueFreshnessAnalysis(p) ps.scheduleEagerComputationsForEntities(declaredMethods)( @@ -145,7 +143,7 @@ object EagerVirtualReturnValueFreshnessAnalysis object LazyVirtualReturnValueFreshnessAnalysis extends VirtualReturnValueFreshnessAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/Context.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/Context.scala index 7aeeb30af7..47c0665a7f 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/Context.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/Context.scala @@ -6,11 +6,7 @@ package properties import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.locks.ReentrantReadWriteLock -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, JavaProjectInformationKey, JavaProjectInformationKeys, SomeProject} import scala.collection.mutable @@ -54,9 +50,9 @@ case class SimpleContext private[properties] (method: DeclaredMethod) extends Co override def id: Int = method.id } -object SimpleContextsKey extends ProjectInformationKey[SimpleContexts, Nothing] { +object SimpleContextsKey extends JavaProjectInformationKey[SimpleContexts, Nothing] { - override def requirements(project: SomeProject): ProjectInformationKeys = + override def requirements(project: SomeProject): JavaProjectInformationKeys = Seq(DeclaredMethodsKey) override def compute(p: SomeProject): SimpleContexts = { @@ -122,9 +118,9 @@ class CallStringContext private[properties] ( } } -object CallStringContextsKey extends ProjectInformationKey[CallStringContexts, Nothing] { +object CallStringContextsKey extends JavaProjectInformationKey[CallStringContexts, Nothing] { - override def requirements(project: SomeProject): ProjectInformationKeys = + override def requirements(project: SomeProject): JavaProjectInformationKeys = Seq(DeclaredMethodsKey) override def compute(p: SomeProject): CallStringContexts = { diff --git a/OPAL/br/src/test/scala/org/opalj/br/analyses/ProjectTest.scala b/OPAL/br/src/test/scala/org/opalj/br/analyses/ProjectTest.scala index 2067df8d2a..003c8521ce 100644 --- a/OPAL/br/src/test/scala/org/opalj/br/analyses/ProjectTest.scala +++ b/OPAL/br/src/test/scala/org/opalj/br/analyses/ProjectTest.scala @@ -7,7 +7,6 @@ import org.junit.runner.RunWith import org.scalatestplus.junit.JUnitRunner import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers - import org.opalj.bi.TestResources.locateTestResources import org.opalj.br.reader.Java11Framework.ClassFiles @@ -703,17 +702,17 @@ class ProjectTest extends AnyFlatSpec with Matchers { } } -private class TestProjectInformationKey extends ProjectInformationKey[Object, Nothing] { +private class TestProjectInformationKey extends JavaProjectInformationKey[Object, Nothing] { val theResult = new Object() override def compute(project: SomeProject): Object = theResult - override def requirements(project: SomeProject): Seq[ProjectInformationKey[_ <: AnyRef, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[_ <: AnyRef, Nothing]] = Nil } -private class TestProjectInformationWithDependenciesKey extends ProjectInformationKey[Object, Nothing] { +private class TestProjectInformationWithDependenciesKey extends JavaProjectInformationKey[Object, Nothing] { val theResult = new Object() @@ -721,7 +720,7 @@ private class TestProjectInformationWithDependenciesKey extends ProjectInformati override def compute(project: SomeProject): Object = theResult - override def requirements(project: SomeProject): Seq[ProjectInformationKey[_ <: AnyRef, _ <: AnyRef]] = depdencies + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[_ <: AnyRef, _ <: AnyRef]] = depdencies } diff --git a/OPAL/br/src/test/scala/org/opalj/br/fpcf/PropertyStoreKeyTest.scala b/OPAL/br/src/test/scala/org/opalj/br/fpcf/PropertyStoreKeyTest.scala index f6dec59f29..d37b835cc4 100644 --- a/OPAL/br/src/test/scala/org/opalj/br/fpcf/PropertyStoreKeyTest.scala +++ b/OPAL/br/src/test/scala/org/opalj/br/fpcf/PropertyStoreKeyTest.scala @@ -7,9 +7,9 @@ import org.junit.runner.RunWith import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.junit.JUnitRunner - import org.opalj.br.TestSupport.biProject import org.opalj.br.analyses.SomeProject +import org.opalj.si.{MetaProject, PropertyStoreKey} /** * @author Michael Eichberg @@ -23,7 +23,7 @@ class PropertyStoreKeyTest extends AnyFunSpec with Matchers { val ps = p.get(PropertyStoreKey) it("the context should always contain the project") { - assert(p == ps.context(classOf[SomeProject])) + assert(p == ps.context(classOf[MetaProject])) } } } diff --git a/OPAL/de/src/main/scala/org/opalj/de/DependencyStoreKey.scala b/OPAL/de/src/main/scala/org/opalj/de/DependencyStoreKey.scala index 638e410a45..f84b1e515e 100644 --- a/OPAL/de/src/main/scala/org/opalj/de/DependencyStoreKey.scala +++ b/OPAL/de/src/main/scala/org/opalj/de/DependencyStoreKey.scala @@ -2,8 +2,7 @@ package org.opalj package de -import org.opalj.br.analyses.SomeProject -import org.opalj.br.analyses.ProjectInformationKey +import org.opalj.br.analyses.{JavaProjectInformationKey, SomeProject} /** * Key that can be used to get a `DependencyStore` that contains all dependencies. @@ -13,9 +12,9 @@ import org.opalj.br.analyses.ProjectInformationKey * * @author Michael Eichberg */ -object DependencyStoreKey extends ProjectInformationKey[DependencyStore, Nothing] { +object DependencyStoreKey extends JavaProjectInformationKey[DependencyStore, Nothing] { - override def requirements(project: SomeProject): Seq[ProjectInformationKey[_ <: AnyRef, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[_ <: AnyRef, Nothing]] = Nil override def compute(project: SomeProject): DependencyStore = { DependencyStore(project.allClassFiles)(project.logContext) diff --git a/OPAL/de/src/main/scala/org/opalj/de/DependencyStoreWithoutSelfDependenciesKey.scala b/OPAL/de/src/main/scala/org/opalj/de/DependencyStoreWithoutSelfDependenciesKey.scala index 40803d43b5..d3d02167ed 100644 --- a/OPAL/de/src/main/scala/org/opalj/de/DependencyStoreWithoutSelfDependenciesKey.scala +++ b/OPAL/de/src/main/scala/org/opalj/de/DependencyStoreWithoutSelfDependenciesKey.scala @@ -2,8 +2,7 @@ package org.opalj package de -import org.opalj.br.analyses.SomeProject -import org.opalj.br.analyses.ProjectInformationKey +import org.opalj.br.analyses.{JavaProjectInformationKey, SomeProject} /** * Key that can be used to get a `DependencyStore` that contains all dependencies @@ -15,9 +14,9 @@ import org.opalj.br.analyses.ProjectInformationKey * @author Michael Eichberg */ object DependencyStoreWithoutSelfDependenciesKey - extends ProjectInformationKey[DependencyStore, Nothing] { + extends JavaProjectInformationKey[DependencyStore, Nothing] { - override def requirements(project: SomeProject): Seq[ProjectInformationKey[_ <: AnyRef, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[_ <: AnyRef, Nothing]] = Nil override def compute(project: SomeProject): DependencyStore = { def createDependencyProcessor(dp: DependencyProcessor) = { diff --git a/OPAL/ll/Readme.md b/OPAL/ll/Readme.md new file mode 100644 index 0000000000..c48c1c83aa --- /dev/null +++ b/OPAL/ll/Readme.md @@ -0,0 +1,4 @@ +# Overview +The ***LLVM*** (LL) module provides tools to work with the llvm compiler infrastructure. + + diff --git a/OPAL/ll/build.sbt b/OPAL/ll/build.sbt new file mode 100644 index 0000000000..b511e98651 --- /dev/null +++ b/OPAL/ll/build.sbt @@ -0,0 +1 @@ +// build settings reside in the opal root build.sbt file diff --git a/OPAL/ll/src/main/resources/reference.conf b/OPAL/ll/src/main/resources/reference.conf new file mode 100644 index 0000000000..d1459a04b7 --- /dev/null +++ b/OPAL/ll/src/main/resources/reference.conf @@ -0,0 +1,5 @@ +org.opalj { + ifds { + debug = false, + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala new file mode 100644 index 0000000000..f299f70a5b --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala @@ -0,0 +1,20 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import org.opalj.ll.llvm.{Module, Reader, value} + +class LLVMProject(val modules: Iterable[Module]) { + def functions: Iterable[value.Function] = + modules.flatMap(module => module.functions) + + def function(name: String): Option[value.Function] = + functions.find(_.name == name) +} + +object LLVMProject { + def apply(modules_paths: Iterable[String]): LLVMProject = { + val modules = modules_paths.map(path => Reader.readIR(path).get) + val project = new LLVMProject(modules) + project + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProjectKey.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProjectKey.scala new file mode 100644 index 0000000000..fbea8959be --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProjectKey.scala @@ -0,0 +1,15 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package ll + +import org.opalj.br.analyses.{JavaProjectInformationKey, SomeProject} +import org.opalj.si.ProjectInformationKey + +object LLVMProjectKey extends JavaProjectInformationKey[LLVMProject, Iterable[String]] { + override def requirements(project: SomeProject): Seq[ProjectInformationKey[SomeProject, Nothing, Nothing]] = Nil + + override def compute(project: SomeProject): LLVMProject = { + LLVMProject(project.getOrCreateProjectInformationKeyInitializationData(this, Iterable.empty)) + } + +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala new file mode 100644 index 0000000000..007042dcc1 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala @@ -0,0 +1,88 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses + +import org.opalj.br.analyses.{JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} +import org.opalj.br.fpcf.{JavaBasicFPCFEagerAnalysisScheduler, JavaFPCFAnalysisScheduler} +import org.opalj.fpcf._ +import org.opalj.ll.LLVMProjectKey +import org.opalj.ll.llvm.value.{Function, GlobalVariable, Store} +import org.opalj.si.FPCFAnalysis + +sealed trait SimplePurityPropertyMetaInformation extends PropertyMetaInformation { + final type Self = SimplePurity +} + +sealed trait SimplePurity extends SimplePurityPropertyMetaInformation with OrderedProperty { + def meet(other: SimplePurity): SimplePurity = { + (this, other) match { + case (Pure, Pure) => Pure + case (_, _) => Impure + } + } + + override def checkIsEqualOrBetterThan(e: Entity, other: SimplePurity): Unit = { + if (meet(other) != other) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") + } + } + + final def key: PropertyKey[SimplePurity] = SimplePurity.key +} + +case object Pure extends SimplePurity + +case object Impure extends SimplePurity + +object SimplePurity extends SimplePurityPropertyMetaInformation { + final val key: PropertyKey[SimplePurity] = PropertyKey.create( + "SimplePurity", + Impure + ) +} + +class SimplePurityAnalysis(val project: SomeProject) extends ProjectBasedAnalysis { + def analyzeSimplePurity(function: Function): ProperPropertyComputationResult = { + function + .basicBlocks + .flatMap(_.instructions) + .foreach { + case instruction: Store => + instruction.dst match { + case _: GlobalVariable => + return Result(function, Impure) + case _ => () + } + case _ => () + } + Result(function, Pure) + } +} + +trait SimplePurityAnalysisScheduler extends JavaFPCFAnalysisScheduler { + def derivedProperty: PropertyBounds = PropertyBounds.ub(SimplePurity) + + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(LLVMProjectKey) + + override def uses: Set[PropertyBounds] = Set.empty // TODO: check this later +} + +object EagerSimplePurityAnalysis + extends SimplePurityAnalysisScheduler + with JavaBasicFPCFEagerAnalysisScheduler { + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + + override def start( + project: SomeProject, + propertyStore: PropertyStore, + initData: InitializationData + ): FPCFAnalysis = { + val analysis = new SimplePurityAnalysis(project) + val llvm_project = project.get(LLVMProjectKey) + propertyStore.scheduleEagerComputationsForEntities(llvm_project.functions)( + analysis.analyzeSimplePurity + ) + analysis + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala new file mode 100644 index 0000000000..187e674415 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala @@ -0,0 +1,19 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds +import org.opalj.br.Method +import org.opalj.fpcf.ifds.Callable +import org.opalj.ll.llvm.value.Function + +abstract class NativeFunction extends Callable { + def name: String +} + +case class LLVMFunction(function: Function) extends NativeFunction { + override def name: String = function.name + override def signature: String = function.name // TODO: add signature +} + +case class JNIMethod(method: Method) extends NativeFunction { + override def name: String = method.name + override def signature: String = method.toString +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala new file mode 100644 index 0000000000..ded110d5fb --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala @@ -0,0 +1,100 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.ll.llvm.value.constant.{ConstantDataArray, GetElementPtrConst} +import org.opalj.ll.llvm.value.{Argument, Call, GetElementPtr, GlobalVariable, Load, Store, Value} +import org.opalj.ll.llvm.{PointerType, StructType} + +object JNICallUtil { + + /** + * Checks whether the call is a call to the JNI interface. + * This is done by the assumption that every such calls first parameter is a struct of type "struct.JNINativeInterface_" + */ + def isJNICall(call: Call): Boolean = call.calledFunctionType.params.headOption match { + case Some(firstParam) => + firstParam match { + case p1: PointerType => + p1.element match { + case p2: PointerType => + p2.element match { + case struct: StructType if struct.name == "struct.JNINativeInterface_" => + true + case other => false + } + case _ => false + } + case _ => false + } + case _ => false + } + + def resolve(call: Call)(implicit declaredMethods: DeclaredMethods): Set[_ <: NativeFunction] = resolveJNIFunction(call) match { + case Symbol("CallTypeMethod") => resolveMethodId(call.operand(2)) // methodID is the third parameter + case _ => Set() + } + + private def resolveJNIFunction(call: Call): Symbol = call.calledValue match { + case load: Load => + load.src match { + // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/functions.html has the indices + case gep: GetElementPtr if gep.isConstant => gep.constants.tail.head match { + case 31 => Symbol("GetObjectClass") + case 33 => Symbol("GetMethodId") + case 49 | 61 => Symbol("CallTypeMethod") // CallIntMethod | CallVoidMethod + case index => throw new IllegalArgumentException(s"unknown JNI function index ${index}") + } + case _ => throw new IllegalArgumentException("unknown JNI load src") + } + case _ => throw new IllegalArgumentException("unknown JNI call argument") + } + + private def resolveMethodId(methodId: Value)(implicit declaredMethods: DeclaredMethods): Set[JNIMethod] = { + val sources = methodId.asInstanceOf[Load].src.users.toSeq.filter(_.isInstanceOf[Store]).map(_.asInstanceOf[Store].src) + sources.filter(_.isInstanceOf[Call]).map(_.asInstanceOf[Call]).map(call => { + if (resolveJNIFunction(call) != Symbol("GetMethodId")) throw new IllegalArgumentException("unexpected call") + if (!resolveClassIsThis(call.operand(1))) // class is the second parameter + throw new IllegalArgumentException("unexpected class argument") + + val functionName = call.function.name + if (!functionName.startsWith("Java_")) { + throw new IllegalArgumentException("unexpected function name") + } + val className = call.function.name.substring(5).split("_").head + val name = resolveString(call.operand(2)) // name is the third parameter + val signature = resolveString(call.operand(3)) // signature is the third parameter + findJavaMethods(className, name, signature) + }).flatten.toSet + } + + private def resolveString(name: Value): String = name match { + case gep: GetElementPtrConst => gep.base match { + case global: GlobalVariable => global.initializer match { + case stringData: ConstantDataArray => stringData.asString + } + } + } + + private def resolveClassIsThis(clazz: Value): Boolean = { + val sources = clazz.asInstanceOf[Load].src.users.toSeq.filter(_.isInstanceOf[Store]).map(_.asInstanceOf[Store].src) + sources.filter(_.isInstanceOf[Call]).map(_.asInstanceOf[Call]).forall(call => { + if (resolveJNIFunction(call) != Symbol("GetObjectClass")) throw new IllegalArgumentException("unexpected call") + resolveObjectIsThis(call.operand(1)) // object is the second parameter + }) + } + + private def resolveObjectIsThis(obj: Value): Boolean = { + val sources = obj.asInstanceOf[Load].src.users.toSeq.filter(_.isInstanceOf[Store]).map(_.asInstanceOf[Store].src) + sources.forall(_.isInstanceOf[Argument]) && sources.forall(_.asInstanceOf[Argument].index == 1) + } + + private def findJavaMethods(className: String, name: String, signature: String)(implicit declaredMethods: DeclaredMethods): Set[JNIMethod] = { + declaredMethods.declaredMethods.filter(declaredMethod => { + val classType = declaredMethod.declaringClassType + (classType.simpleName == className && + declaredMethod.name == name && + declaredMethod.descriptor.toJVMDescriptor == signature) + }).map(_.definedMethod).map(JNIMethod(_)).toSet + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala new file mode 100644 index 0000000000..72ead9ee99 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -0,0 +1,61 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.br.analyses.{DeclaredMethodsKey, SomeProject} +import org.opalj.fpcf.ifds.ICFG +import org.opalj.ll.llvm.value.{Call, Function, Instruction, Ret, Terminator} + +class NativeForwardICFG(project: SomeProject) extends ICFG[NativeFunction, LLVMStatement] { + implicit val declaredMethods = project.get(DeclaredMethodsKey) + + /** + * Determines the statements at which the analysis starts. + * + * @param callable The analyzed callable. + * @return The statements at which the analysis starts. + */ + override def startStatements(callable: NativeFunction): Set[LLVMStatement] = callable match { + case LLVMFunction(function) => { + if (function.basicBlockCount == 0) + throw new IllegalArgumentException(s"${callable} does not contain any basic blocks and likely should not be in scope of the analysis") + Set(LLVMStatement(function.entryBlock.firstInstruction)) + } + } + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + override def nextStatements(statement: LLVMStatement): Set[LLVMStatement] = { + if (!statement.instruction.isTerminator) return Set(LLVMStatement(statement.instruction.next.get)) + statement.instruction.asInstanceOf[Instruction with Terminator].successors.map(LLVMStatement(_)).toSet + } + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[_ <: NativeFunction]] = { + statement.instruction match { + case call: Call => Some(resolveCallee(call)) + case _ => None + } + } + + override def isExitStatement(statement: LLVMStatement): Boolean = statement.instruction match { + case Ret(_) => true + case _ => false + } + + private def resolveCallee(call: Call): Set[_ <: NativeFunction] = + if (call.calledValue.isInstanceOf[Function]) + Set(LLVMFunction(call.calledValue.asInstanceOf[Function])) + else if (JNICallUtil.isJNICall(call)) + JNICallUtil.resolve(call) + else Set() +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala new file mode 100644 index 0000000000..ba67a2dee7 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ + +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.br.analyses.{JavaProjectInformationKeys, SomeProject} +import org.opalj.fpcf.ifds.{AbstractIFDSFact, IFDSAnalysis, IFDSAnalysisScheduler, IFDSProblem, IFDSPropertyMetaInformation} +import org.opalj.ll.LLVMProjectKey + +/** + * + * @param ifdsProblem + * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. + * @tparam IFDSFact + */ +class NativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( + project: SomeProject, + ifdsProblem: IFDSProblem[IFDSFact, NativeFunction, LLVMStatement], + propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] +) + extends IFDSAnalysis[IFDSFact, NativeFunction, LLVMStatement]()(project, ifdsProblem, propertyKey) + +abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends IFDSAnalysisScheduler[SomeProject, IFDSFact, NativeFunction, LLVMStatement] { + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(LLVMProjectKey) +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala new file mode 100644 index 0000000000..4453fd4ffc --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -0,0 +1,74 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{EOptionP, FinalEP, InterimEUBP, Property, PropertyKey, PropertyStore} +import org.opalj.fpcf.ifds.Dependees.Getter +import org.opalj.fpcf.ifds.{AbstractIFDSFact, IFDSProblem, IFDSProperty} +import org.opalj.ll.LLVMProjectKey +import org.opalj.si.PropertyStoreKey +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement + +abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact, JavaFact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, NativeFunction, LLVMStatement](new NativeForwardICFG(project)) { + final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + val llvmProject = project.get(LLVMProjectKey) + val javaPropertyKey: PropertyKey[Property] + + override def outsideAnalysisContext(callee: NativeFunction): Option[(LLVMStatement, LLVMStatement, Fact, Getter) => Set[Fact]] = callee match { + case LLVMFunction(function) => + function.basicBlockCount match { + case 0 => Some((_: LLVMStatement, _: LLVMStatement, in: Fact, _: Getter) => Set(in)) + case _ => None + } + case JNIMethod(method) => Some(handleJavaMethod(method)) + } + + private def handleJavaMethod(callee: Method)(call: LLVMStatement, successor: LLVMStatement, in: Fact, dependeesGetter: Getter): Set[Fact] = { + var result = Set.empty[Fact] + val entryFacts = javaCallFlow(call, callee, in) + for (entryFact <- entryFacts) { // ifds line 14 + val e = (callee, entryFact) + val exitFacts: Map[JavaStatement, Set[JavaFact]] = + dependeesGetter(e, javaPropertyKey).asInstanceOf[EOptionP[(JavaStatement, JavaFact), IFDSProperty[JavaStatement, JavaFact]]] match { + case ep: FinalEP[_, IFDSProperty[JavaStatement, JavaFact]] => + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[JavaStatement, JavaFact]] => + ep.ub.flows + case _ => + Map.empty + } + for { + (exitStatement, exitStatementFacts) <- exitFacts // ifds line 15.2 + exitStatementFact <- exitStatementFacts // ifds line 15.3 + } { + result ++= javaReturnFlow(exitStatement, exitStatementFact, call, in, successor) + } + } + result + } + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + protected def javaCallFlow( + call: LLVMStatement, + callee: Method, + in: Fact + ): Set[JavaFact] + + protected def javaReturnFlow( + exit: JavaStatement, + in: JavaFact, + call: LLVMStatement, + callFact: Fact, + successor: LLVMStatement + ): Set[Fact] +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Statement.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Statement.scala new file mode 100644 index 0000000000..4b64687250 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Statement.scala @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.fpcf.ifds.Statement +import org.opalj.ll.llvm.value.{BasicBlock, Instruction} + +/** + * A statement that is passed to the concrete analysis. + * + * @param instruction The LLVM instruction. + */ +case class LLVMStatement(instruction: Instruction) extends Statement[LLVMFunction, BasicBlock] { + def function: LLVMFunction = LLVMFunction(instruction.function) + def basicBlock: BasicBlock = instruction.parent + override def node: BasicBlock = basicBlock + override def callable: LLVMFunction = function + override def toString: String = s"${function.name}\n\t${instruction}\n\t${function}" +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala new file mode 100644 index 0000000000..e3061f0d9d --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -0,0 +1,249 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds.taint + +import org.opalj.br.Method +import org.opalj.br.analyses.{JavaProjectInformationKeys, SomeProject} +import org.opalj.fpcf._ +import org.opalj.fpcf.ifds.Dependees.Getter +import org.opalj.fpcf.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSProperty, IFDSPropertyMetaInformation} +import org.opalj.ll.LLVMProjectKey +import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement} +import org.opalj.ll.fpcf.properties.NativeTaint +import org.opalj.ll.llvm.value.Ret +import org.opalj.tac.Assignment +import org.opalj.tac.fpcf.analyses.ifds.taint._ +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.properties.{TACAI, Taint} + +import scala.reflect.ClassTag + +class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { + val llvmProject = p.get(LLVMProjectKey) + + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(Method, TaintFact)] = for { + m <- p.allMethodsWithBody + } yield m -> TaintNullFact + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Method): Boolean = + callee.name == "sanitize" + + /** + * We do not sanitize parameters. + */ + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact( + callee: Method, + call: JavaStatement, + in: TaintFact + ): Option[FlowFact] = + if (callee.name == "sink" && in == Variable(-2)) + Some(FlowFact(Seq(JavaMethod(call.method), JavaMethod(callee)))) + else None + + // Multilingual additions here + override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { + def handleNativeMethod(call: JavaStatement, successor: JavaStatement, in: TaintFact, dependeesGetter: Getter): Set[TaintFact] = { + // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names + val calleeName = callee.name.map(c => c match { + case c if isAlphaNumeric(c) => c + case '_' => "_1" + case ';' => "_2" + case '[' => "_3" + case c => s"_${c.toInt.toHexString.reverse.padTo(4, '0').reverse}" + }).mkString + val nativeFunctionName = "Java_"+callee.classFile.fqn+"_"+calleeName + val function = LLVMFunction(llvmProject.function(nativeFunctionName).get) + var result = Set.empty[TaintFact] + val entryFacts = nativeCallFlow(call, function, in, callee) + for (entryFact <- entryFacts) { // ifds line 14 + val e = (function, entryFact) + val exitFacts: Map[LLVMStatement, Set[NativeTaintFact]] = + dependeesGetter(e, NativeTaint.key).asInstanceOf[EOptionP[(LLVMStatement, NativeTaintFact), IFDSProperty[LLVMStatement, NativeTaintFact]]] match { + case ep: FinalEP[_, IFDSProperty[LLVMStatement, NativeTaintFact]] => + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[LLVMStatement, NativeTaintFact]] => + ep.ub.flows + case _ => + Map.empty + } + for { + (exitStatement, exitStatementFacts) <- exitFacts // ifds line 15.2 + exitStatementFact <- exitStatementFacts // ifds line 15.3 + } { + result ++= nativeReturnFlow(exitStatement, exitStatementFact, call, in, callee, successor) + } + } + result + } + + if (callee.isNative) { + Some(handleNativeMethod _) + } else { + super.outsideAnalysisContext(callee) + } + } + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + private def nativeCallFlow( + call: JavaStatement, + callee: LLVMFunction, + in: TaintFact, + nativeCallee: Method + ): Set[NativeTaintFact] = { + val callObject = JavaIFDSProblem.asCall(call.stmt) + val allParams = callObject.allParams + val allParamsWithIndices = allParams.zipWithIndex + in match { + // Taint formal parameter if actual parameter is tainted + case Variable(index) => + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) => + // TODO: this is passed + Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv + case _ => None // Nothing to do + }.toSet + + // Taint element of formal parameter if element of actual parameter is tainted + case ArrayElement(index, taintedIndex) => + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) => + Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv + case _ => None // Nothing to do + }.toSet + + case InstanceField(index, declaredClass, taintedField) => + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) => + Some(JavaInstanceField(paramIndex + 1, declaredClass, taintedField)) // TODO subtype check + case _ => None // Nothing to do + }.toSet + + case StaticField(classType, fieldName) => Set(JavaStaticField(classType, fieldName)) + + case TaintNullFact => Set(NativeTaintNullFact) + + case _ => Set() // Nothing to do + + } + } + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + private def nativeReturnFlow( + exit: LLVMStatement, + in: NativeTaintFact, + call: JavaStatement, + callFact: TaintFact, + nativeCallee: Method, + successor: JavaStatement + ): Set[TaintFact] = { + if (sanitizesReturnValue(nativeCallee)) return Set.empty + val callStatement = JavaIFDSProblem.asCall(call.stmt) + val allParams = callStatement.allParams + var flows: Set[TaintFact] = Set.empty + in match { + // Taint actual parameter if formal parameter is tainted + case JavaVariable(index) if index < 0 && index > -100 && JavaIFDSProblem.isRefTypeParam(nativeCallee, index) => + val param = allParams( + JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(Variable) + + // Taint element of actual parameter if element of formal parameter is tainted + case JavaArrayElement(index, taintedIndex) if index < 0 && index > -100 => + val param = allParams( + JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) + + case JavaInstanceField(index, declClass, taintedField) if index < 0 && index > -10 => + // Taint field of actual parameter if field of formal parameter is tainted + val param = + allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic)) + param.asVar.definedBy.foreach { defSite => + flows += InstanceField(defSite, declClass, taintedField) + } + + case JavaStaticField(objectType, fieldName) => flows += StaticField(objectType, fieldName) + + // Track the call chain to the sink back + case NativeFlowFact(flow) if !flow.contains(JavaMethod(call.method)) => + flows += FlowFact(JavaMethod(call.method) +: flow) + case NativeTaintNullFact => flows += TaintNullFact + case _ => + } + + // Propagate taints of the return value + exit.instruction match { + case ret: Ret => { + in match { + case NativeVariable(value) if ret.value.contains(value) && call.stmt.astID == Assignment.ASTID => + flows += Variable(call.index) + // TODO + /*case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) => + flows += ArrayElement(call.index, taintedIndex) + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) => + flows += InstanceField(call.index, declClass, taintedField)*/ + case NativeTaintNullFact => + val taints = createTaints(nativeCallee, call) + if (taints.nonEmpty) flows ++= taints + case _ => // Nothing to do + } + } + case _ => + } + flows + } + + private def isAlphaNumeric(char: Char): Boolean = { + char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z' || char >= '0' && char <= '9' + } +} + +class SimpleJavaForwardTaintAnalysis(project: SomeProject) + extends IFDSAnalysis()(project, new SimpleJavaForwardTaintProblem(project), Taint) + +object JavaForwardTaintAnalysisScheduler extends IFDSAnalysisScheduler[SomeProject, TaintFact, Method, JavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis(p) + override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(LLVMProjectKey) + override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.ub(NativeTaint)) + override implicit val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala new file mode 100644 index 0000000000..ea1d1c6e99 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -0,0 +1,60 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds.taint + +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{PropertyBounds, PropertyKey, PropertyStore} +import org.opalj.fpcf.ifds.IFDSPropertyMetaInformation +import org.opalj.ll.fpcf.analyses.ifds.{LLVMStatement, NativeFunction, NativeIFDSAnalysis, NativeIFDSAnalysisScheduler} +import org.opalj.ll.fpcf.properties.NativeTaint +import org.opalj.ll.llvm.value.Function +import org.opalj.tac.fpcf.properties.Taint + +import scala.reflect.ClassTag + +class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaintProblem(p) { + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(NativeFunction, NativeTaintFact)] = Seq.empty + override val javaPropertyKey: PropertyKey[Taint] = Taint.key + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: NativeFunction): Boolean = + callee.name == "sanitize" + + /** + * We do not sanitize parameters. + */ + override protected def sanitizesParameter(call: LLVMStatement, in: NativeTaintFact): Boolean = false + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + protected def createTaints(callee: Function, call: LLVMStatement): Set[NativeTaintFact] = + if (callee.name == "source") Set(NativeVariable(call.instruction)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + protected def createFlowFact( + callee: Function, + call: LLVMStatement, + in: Set[NativeTaintFact] + ): Option[NativeFlowFact] = + if (callee.name == "sink" && in.contains(JavaVariable(-2))) Some(NativeFlowFact(Seq(call.function))) + else None +} + +class SimpleNativeForwardTaintAnalysis(project: SomeProject) + extends NativeIFDSAnalysis(project, new SimpleNativeForwardTaintProblem(project), NativeTaint) + +object NativeForwardTaintAnalysisScheduler extends NativeIFDSAnalysisScheduler[NativeTaintFact] { + override def init(p: SomeProject, ps: PropertyStore) = new SimpleNativeForwardTaintAnalysis(p) + override def property: IFDSPropertyMetaInformation[LLVMStatement, NativeTaintFact] = NativeTaint + override val uses: Set[PropertyBounds] = Set() // ++ PropertyBounds.ub(Taint) TODO: we do not use the native taint yet + override implicit val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala new file mode 100644 index 0000000000..9913a1c00f --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -0,0 +1,218 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds.taint + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeFunction, NativeIFDSProblem} +import org.opalj.ll.fpcf.analyses.ifds.JNIMethod +import org.opalj.ll.llvm.value.{Add, Alloca, BitCast, Call, GetElementPtr, Load, PHI, Ret, Store, Sub} + +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, TaintProblem} +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem +import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.tac.fpcf.analyses.ifds.taint.StaticField +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact +import org.opalj.tac.fpcf.analyses.ifds.taint.Variable +import org.opalj.tac.ReturnValue + +abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeTaintFact, TaintFact](project) with TaintProblem[NativeFunction, LLVMStatement, NativeTaintFact] { + override def nullFact: NativeTaintFact = NativeTaintNullFact + + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param in The fact which holds before the execution of the `statement`. + * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be + * computed. Used for phi statements to distinguish the flow. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + override def normalFlow(statement: LLVMStatement, in: NativeTaintFact, predecessor: Option[LLVMStatement]): Set[NativeTaintFact] = statement.instruction match { + case _: Alloca => Set(in) + case store: Store => in match { + case NativeVariable(value) if value == store.src => store.dst match { + case dst: Alloca => Set(in, NativeVariable(dst)) + case gep: GetElementPtr if gep.isConstant => Set(in, NativeArrayElement(gep.base, gep.constants)) + } + case NativeArrayElement(base, indices) if store.src == base => Set(in, NativeArrayElement(store.dst, indices)) + case NativeVariable(value) if value == store.dst => Set() + case _ => Set(in) + } + case load: Load => in match { + case NativeVariable(value) if value == load.src => Set(in, NativeVariable(load)) + case NativeArrayElement(base, indices) => load.src match { + case gep: GetElementPtr if gep.isConstant && gep.base == base && gep.constants == indices => Set(in, NativeVariable(load)) + case _ => Set(in, NativeArrayElement(load, indices)) + } + case _ => Set(in) + } + case add: Add => in match { + case NativeVariable(value) if value == add.op1 || value == add.op2 => Set(in, NativeVariable(add)) + case _ => Set(in) + } + case sub: Sub => in match { + case NativeVariable(value) if value == sub.op1 || value == sub.op2 => Set(in, NativeVariable(sub)) + case _ => Set(in) + } + case gep: GetElementPtr => in match { + case NativeVariable(value) if value == gep.base => Set(in, NativeVariable(gep)) + case NativeArrayElement(base, indices) if base == gep.base && gep.isZero => Set(in, NativeArrayElement(gep, indices)) + case _ => Set(in) + } + case bitcast: BitCast => in match { + case NativeVariable(value) if value == bitcast.operand(0) => Set(in, NativeVariable(bitcast)) + case _ => Set(in) + } + case _ => Set(in) + } + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + override def callFlow(call: LLVMStatement, callee: NativeFunction, in: NativeTaintFact): Set[NativeTaintFact] = callee match { + case LLVMFunction(callee) => + in match { + // Taint formal parameter if actual parameter is tainted + case NativeVariable(value) => call.instruction.asInstanceOf[Call].indexOfArgument(value) match { + case Some(index) => Set(NativeVariable(callee.argument(index))) + case None => Set() + } + // TODO pass other java taints + case NativeTaintNullFact => Set(in) + case NativeArrayElement(base, indices) => call.instruction.asInstanceOf[Call].indexOfArgument(base) match { + case Some(index) => Set(NativeArrayElement(callee.argument(index), indices)) + case None => Set() + } + case _ => Set() // Nothing to do + } + case _ => throw new RuntimeException("this case should be handled by outsideAnalysisContext") + } + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + override def returnFlow(exit: LLVMStatement, in: NativeTaintFact, call: LLVMStatement, callFact: NativeTaintFact, successor: LLVMStatement): Set[NativeTaintFact] = { + val callee = exit.callable + var flows: Set[NativeTaintFact] = if (sanitizesReturnValue(callee)) Set.empty else in match { + case NativeVariable(value) => exit.instruction match { + case ret: Ret if ret.value.contains(value) => Set(NativeVariable(call.instruction)) + case _: Ret => Set() + case _ => Set() + } + case NativeTaintNullFact => Set(NativeTaintNullFact) + case NativeFlowFact(flow) if !flow.contains(call.function) => + Set(NativeFlowFact(call.function +: flow)) + case _ => Set() + } + if (exit.callable.name == "source") in match { + case NativeTaintNullFact => flows += NativeVariable(call.instruction) + } + if (exit.callable.name == "sink") in match { + case NativeVariable(value) if value == exit.callable.function.argument(0) => + flows += NativeFlowFact(Seq(call.callable, exit.callable)) + case _ => + } + flows + } + + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param in The facts, which hold before the `call`. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + override def callToReturnFlow(call: LLVMStatement, in: NativeTaintFact, successor: LLVMStatement): Set[NativeTaintFact] = Set(in) + + override def needsPredecessor(statement: LLVMStatement): Boolean = statement.instruction match { + case PHI(_) => true + case _ => false + } + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + override protected def javaCallFlow( + call: LLVMStatement, + callee: Method, + in: NativeTaintFact + ): Set[TaintFact] = + in match { + // Taint formal parameter if actual parameter is tainted + case NativeVariable(value) => call.instruction.asInstanceOf[Call].indexOfArgument(value) match { + case Some(index) => Set(Variable(JavaIFDSProblem.switchParamAndVariableIndex( + index - 2, + callee.isStatic + ))) + case None => Set() + } + // TODO pass other java taints + case NativeTaintNullFact => Set(TaintNullFact) + case _ => Set() // Nothing to do + } + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + override protected def javaReturnFlow( + exit: JavaStatement, + in: TaintFact, + call: LLVMStatement, + callFact: NativeTaintFact, + successor: LLVMStatement + ): Set[NativeTaintFact] = { + val callee = exit.callable + if (sanitizesReturnValue(JNIMethod(callee))) return Set.empty + var flows: Set[NativeTaintFact] = Set.empty + in match { + case StaticField(classType, fieldName) => flows += JavaStaticField(classType, fieldName) + + // Track the call chain to the sink back + case FlowFact(flow) if !flow.contains(call.function) => + flows += NativeFlowFact(call.function +: flow) + case _ => + } + + // Propagate taints of the return value + if (exit.stmt.astID == ReturnValue.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + in match { + case Variable(index) if returnValueDefinedBy.contains(index) => + flows += NativeVariable(call.instruction) + case _ => // Nothing to do + } + } + flows + } +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala new file mode 100644 index 0000000000..8a4bbbdb03 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala @@ -0,0 +1,58 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds.taint + +import org.opalj.br.ObjectType +import org.opalj.fpcf.ifds.{AbstractIFDSNullFact, Callable, AbstractIFDSFact} +import org.opalj.ll.llvm.value.Value + +trait NativeTaintFact extends AbstractIFDSFact + +object NativeTaintNullFact extends NativeTaintFact with AbstractIFDSNullFact + +/** + * A tainted variable. + * + * @param index The variable's definition site. + */ +case class JavaVariable(index: Int) extends NativeTaintFact +case class NativeVariable(value: Value) extends NativeTaintFact + +/** + * A tainted array element. + * + * @param index The array's definition site. + * @param element The index of the tainted element in the array. + */ +case class JavaArrayElement(index: Int, element: Int) extends NativeTaintFact +case class NativeArrayElement(base: Value, indices: Iterable[Long]) extends NativeTaintFact + +/** + * A tainted static field. + * + * @param classType The field's class. + * @param fieldName The field's name. + */ +case class JavaStaticField(classType: ObjectType, fieldName: String) extends NativeTaintFact + +/** + * A tainted instance field. + * + * @param index The definition site of the field's value. + * @param classType The field's type. + * @param fieldName The field's value. + */ +case class JavaInstanceField(index: Int, classType: ObjectType, fieldName: String) extends NativeTaintFact + +/** + * A path of method calls, originating from the analyzed method, over which a tainted variable + * reaches the sink. + * + * @param flow A sequence of method calls, originating from but not including this method. + */ +case class NativeFlowFact(flow: Seq[Callable]) extends NativeTaintFact { + override val hashCode: Int = { + var r = 1 + flow.foreach(f => r = (r + f.hashCode()) * 31) + r + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala new file mode 100644 index 0000000000..4affcf8790 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.properties + +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.ifds.{IFDSProperty, IFDSPropertyMetaInformation} +import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement +import org.opalj.ll.fpcf.analyses.ifds.taint.NativeTaintFact + +case class NativeTaint(flows: Map[LLVMStatement, Set[NativeTaintFact]], debugData: Map[LLVMStatement, Set[NativeTaintFact]] = Map.empty) extends IFDSProperty[LLVMStatement, NativeTaintFact] { + + override type Self = NativeTaint + override def create(result: Map[LLVMStatement, Set[NativeTaintFact]]): IFDSProperty[LLVMStatement, NativeTaintFact] = new NativeTaint(result) + override def create(result: Map[LLVMStatement, Set[NativeTaintFact]], debugData: Map[LLVMStatement, Set[NativeTaintFact]]): IFDSProperty[LLVMStatement, NativeTaintFact] = new NativeTaint(result, debugData) + + override def key: PropertyKey[NativeTaint] = NativeTaint.key +} + +object NativeTaint extends IFDSPropertyMetaInformation[LLVMStatement, NativeTaintFact] { + + override type Self = NativeTaint + override def create(result: Map[LLVMStatement, Set[NativeTaintFact]]): IFDSProperty[LLVMStatement, NativeTaintFact] = new NativeTaint(result) + override def create(result: Map[LLVMStatement, Set[NativeTaintFact]], debugData: Map[LLVMStatement, Set[NativeTaintFact]]): IFDSProperty[LLVMStatement, NativeTaintFact] = new NativeTaint(result, debugData) + + val key: PropertyKey[NativeTaint] = PropertyKey.create("NativeTaint", new NativeTaint(Map.empty)) +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala new file mode 100644 index 0000000000..24a7b87d6d --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala @@ -0,0 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm + +import org.bytedeco.llvm.LLVM.{LLVMModuleRef, LLVMValueRef} +import org.bytedeco.llvm.global.LLVM.{LLVMDisposeMessage, LLVMGetFirstFunction, LLVMGetNamedFunction, LLVMGetNextFunction, LLVMPrintModuleToString} +import org.opalj.ll.llvm.value.{Value, Function} + +case class Module(ref: LLVMModuleRef) { + def functions: FunctionIterator = { + new FunctionIterator(LLVMGetFirstFunction(ref)) + } + + def repr: String = { + val bytePointer = LLVMPrintModuleToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } + + def function(name: String): Function = + Value(LLVMGetNamedFunction(ref, name)) match { + case None => + throw new IllegalArgumentException("Unknown function '"+name+"'") + case Some(function: Function) => function + case Some(_) => throw new IllegalStateException("Expected LLVMGetNamedFunction to return a Function ref") + } +} + +class FunctionIterator(var ref: LLVMValueRef) extends Iterator[Function] { + override def hasNext: Boolean = ref != null + + override def next(): Function = { + val function = Function(ref) + this.ref = LLVMGetNextFunction(ref) + function + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Reader.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Reader.scala new file mode 100644 index 0000000000..f84d937233 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Reader.scala @@ -0,0 +1,33 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ + +package org.opalj.ll.llvm + +import org.bytedeco.javacpp.BytePointer +import org.bytedeco.llvm.LLVM.{LLVMContextRef, LLVMMemoryBufferRef, LLVMModuleRef} +import org.bytedeco.llvm.global.LLVM.{ + LLVMContextCreate, + LLVMCreateMemoryBufferWithContentsOfFile, + LLVMDisposeMessage, + LLVMParseIRInContext +} + +object Reader { + def readIR(path: String): Option[Module] = { + val file_buffer: LLVMMemoryBufferRef = new LLVMMemoryBufferRef() + val path_pointer: BytePointer = new BytePointer(path) + val out_message: BytePointer = new BytePointer() + if (LLVMCreateMemoryBufferWithContentsOfFile(path_pointer, file_buffer, out_message) != 0) { + System.err.println("Failed to load file: "+out_message.getString) + LLVMDisposeMessage(out_message) + return None + } + val context: LLVMContextRef = LLVMContextCreate() + val module: LLVMModuleRef = new LLVMModuleRef() + if (LLVMParseIRInContext(context, file_buffer, module, out_message) != 0) { + println("Failed to parse file: "+out_message) + LLVMDisposeMessage(out_message) + return None + } + Some(Module(module)) + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala new file mode 100644 index 0000000000..f21753bb24 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala @@ -0,0 +1,116 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm + +import org.bytedeco.javacpp.PointerPointer +import org.bytedeco.llvm.LLVM.LLVMTypeRef +import org.bytedeco.llvm.global.LLVM._ + +object Type { + def apply(ref: LLVMTypeRef): Type = { + LLVMGetTypeKind(ref) match { + case LLVMVoidTypeKind => VoidType(ref) + case LLVMHalfTypeKind => HalfType(ref) + case LLVMFloatTypeKind => FloatType(ref) + case LLVMDoubleTypeKind => DoubleType(ref) + case LLVMX86_FP80TypeKind => X86_FP80Type(ref) + case LLVMFP128TypeKind => FP128Type(ref) + case LLVMPPC_FP128TypeKind => PPC_FP128Type(ref) + case LLVMLabelTypeKind => LabelType(ref) + case LLVMIntegerTypeKind => IntegerType(ref) + case LLVMFunctionTypeKind => FunctionType(ref) + case LLVMStructTypeKind => StructType(ref) + case LLVMArrayTypeKind => ArrayType(ref) + case LLVMPointerTypeKind => PointerType(ref) + case LLVMVectorTypeKind => VectorType(ref) + case LLVMMetadataTypeKind => MetadataType(ref) + case LLVMX86_MMXTypeKind => X86_MMXType(ref) + case LLVMTokenTypeKind => TokenType(ref) + case LLVMScalableVectorTypeKind => ScalableVectorType(ref) + case LLVMBFloatTypeKind => FloatType(ref) + case typeKind => throw new IllegalArgumentException("unknown type kind: "+typeKind) + } + } +} + +sealed abstract class Type(ref: LLVMTypeRef) { + def repr: String = { + val bytePointer = LLVMPrintTypeToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } + + override def toString: String = s"Type(${repr})" + + def isSized: Boolean = intToBool(LLVMTypeIsSized(ref)) +} + +trait SequentialType { + val ref: LLVMTypeRef + + def element: Type = Type(LLVMGetElementType(ref)) +} + +/** type with no size */ +case class VoidType(ref: LLVMTypeRef) extends Type(ref) +/** 16 bit floating point type */ +case class HalfType(ref: LLVMTypeRef) extends Type(ref) +/** 32 bit floating point type */ +case class FloatType(ref: LLVMTypeRef) extends Type(ref) +/** 64 bit floating point type */ +case class DoubleType(ref: LLVMTypeRef) extends Type(ref) +/** 80 bit floating point type (X87) */ +case class X86_FP80Type(ref: LLVMTypeRef) extends Type(ref) +/** 128 bit floating point type (112-bit mantissa) */ +case class FP128Type(ref: LLVMTypeRef) extends Type(ref) +/** 128 bit floating point type (two 64-bits) */ +case class PPC_FP128Type(ref: LLVMTypeRef) extends Type(ref) +/** Labels */ +case class LabelType(ref: LLVMTypeRef) extends Type(ref) +/** Arbitrary bit width integers */ +case class IntegerType(ref: LLVMTypeRef) extends Type(ref) +/** Functions */ +case class FunctionType(ref: LLVMTypeRef) extends Type(ref) { + def returnType: Type = Type(LLVMGetReturnType(ref)) + def isVarArg: Boolean = intToBool(LLVMIsFunctionVarArg(ref)) + + def paramCount: Int = LLVMCountParamTypes(ref) + def params: Iterable[Type] = { + val result = new PointerPointer[LLVMTypeRef](paramCount.toLong) + LLVMGetParamTypes(ref, result) + (0.toLong until paramCount.toLong).map(result.get(_)).map(p => Type(new LLVMTypeRef(p))) + } +} +/** Structures */ +case class StructType(ref: LLVMTypeRef) extends Type(ref) { + def name: String = LLVMGetStructName(ref).getString + def elementCount: Int = LLVMCountStructElementTypes(ref) + def elementAtIndex(i: Int) = { + assert(i < elementCount) + Type(LLVMStructGetTypeAtIndex(ref, i)) + } + def elements: Iterable[Type] = (0 until elementCount).map(elementAtIndex(_)) + def isPacked: Boolean = intToBool(LLVMIsPackedStruct(ref)) + def isOpaque: Boolean = intToBool(LLVMIsOpaqueStruct(ref)) + def isLiteral: Boolean = intToBool(LLVMIsLiteralStruct(ref)) +} +/** Arrays */ +case class ArrayType(ref: LLVMTypeRef) extends Type(ref) with SequentialType { + def length: Int = LLVMGetArrayLength(ref) +} +/** Pointers */ +case class PointerType(ref: LLVMTypeRef) extends Type(ref) with SequentialType +/** Fixed width SIMD vector type */ +case class VectorType(ref: LLVMTypeRef) extends Type(ref) with SequentialType { + def size: Int = LLVMGetVectorSize(ref) +} +/** Metadata */ +case class MetadataType(ref: LLVMTypeRef) extends Type(ref) +/** X86 MMX */ +case class X86_MMXType(ref: LLVMTypeRef) extends Type(ref) +/** Tokens */ +case class TokenType(ref: LLVMTypeRef) extends Type(ref) +/** Scalable SIMD vector type */ +case class ScalableVectorType(ref: LLVMTypeRef) extends Type(ref) +/** 16 bit brain floating point type */ +case class BFloatType(ref: LLVMTypeRef) extends Type(ref) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala new file mode 100644 index 0000000000..253baa543b --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala @@ -0,0 +1,10 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +package object llvm { + def intToBool(i: Int): Boolean = i match { + case 0 => false + case 1 => true + case _ => throw new IllegalArgumentException(s"${i} is not a valid Boolean") + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Argument.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Argument.scala new file mode 100644 index 0000000000..9c0521a754 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Argument.scala @@ -0,0 +1,16 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM.{LLVMGetParamParent, LLVMGetValueKind, LLVMArgumentValueKind} +import org.opalj.ll.llvm.value + +case class Argument(ref: LLVMValueRef, index: Int) extends Value(ref) { + assert(LLVMGetValueKind(ref) == LLVMArgumentValueKind, "ref has to be an argument") + + def parent(): value.Function = value.Function(LLVMGetParamParent(ref)) +} + +object Argument { + def apply(ref: LLVMValueRef): Argument = value.Function(LLVMGetParamParent(ref)).arguments.find(_.ref == ref).get +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala new file mode 100644 index 0000000000..a6b3cf7bf2 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala @@ -0,0 +1,69 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value + +import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} +import org.bytedeco.llvm.global.LLVM._ +import org.opalj.graphs.Node + +case class BasicBlock(block_ref: LLVMBasicBlockRef) + extends Value(LLVMBasicBlockAsValue(block_ref)) + with Node { + def parent = Function(LLVMGetBasicBlockParent(block_ref)) + def instructions: InstructionIterator = new InstructionIterator(LLVMGetFirstInstruction(block_ref)) + def firstInstruction: Instruction = Instruction(LLVMGetFirstInstruction(block_ref)) + def lastInstruction: Instruction = Instruction(LLVMGetLastInstruction(block_ref)) + + def terminator: Option[Instruction with Terminator] = { + OptionalInstruction(LLVMGetBasicBlockTerminator(block_ref)) match { + case Some(terminator) => { + assert(terminator.isTerminator) + Some(terminator.asInstanceOf[Instruction with Terminator]) + } + case None => None + } + } + + def blockName(): String = LLVMGetBasicBlockName(block_ref).getString + + /** + * Returns a human readable representation (HRR) of this node. + */ + override def toHRR: Option[String] = { + Some(name) //TODO: maybe add more info + } + + /** + * An identifier that uniquely identifies this node in the graph to which this + * node belongs. By default two nodes are considered equal if they have the same + * unique id. + */ + override def nodeId: Int = { + block_ref.hashCode + } + + /** + * Returns `true` if this node has successor nodes. + */ + override def hasSuccessors: Boolean = terminator match { + case Some(t) => t.hasSuccessors + case None => false + } + + /** + * Applies the given function for each successor node. + */ + override def foreachSuccessor(f: Node => Unit): Unit = terminator match { + case Some(t) => t.foreachSuccessor(f) + case None => + } +} + +class InstructionIterator(var ref: LLVMValueRef) extends Iterator[Instruction] { + override def hasNext: Boolean = LLVMGetNextInstruction(ref) != null + + override def next(): Instruction = { + val instruction = Instruction(ref) + this.ref = LLVMGetNextInstruction(ref) + instruction + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala new file mode 100644 index 0000000000..16b13deaed --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala @@ -0,0 +1,68 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value + +import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} +import org.bytedeco.llvm.global.LLVM._ +import org.opalj.io.writeAndOpen + +case class Function(ref: LLVMValueRef) extends Value(ref) { + assert(LLVMGetValueKind(ref) == LLVMFunctionValueKind, "ref has to be a function") + + def basicBlocks: BasicBlockIterator = { + new BasicBlockIterator(LLVMGetFirstBasicBlock(ref)) + } + def basicBlockCount: Int = LLVMCountBasicBlocks(ref) + + def arguments: ArgumentIterator = { + new ArgumentIterator(LLVMGetFirstParam(ref)) + } + def argumentCount: Int = LLVMCountParams(ref) + def argument(index: Int): Argument = { + assert(index < argumentCount) + Argument(LLVMGetParam(ref, index), index) + } + + def entryBlock: BasicBlock = { + if (basicBlockCount == 0) throw new IllegalStateException("this function does not contain any basic block and may not be defined") + BasicBlock(LLVMGetEntryBasicBlock(ref)) + } + + def viewCFG(): Unit = { + val cfg_dot = org.opalj.graphs.toDot(Set(entryBlock)) + writeAndOpen(cfg_dot, name+"-CFG", ".gv") + } + + def viewLLVMCFG(include_content: Boolean = true): Unit = { + if (include_content) { + LLVMViewFunctionCFG(ref) + } else { + LLVMViewFunctionCFGOnly(ref) + } + } + + override def toString: String = { + s"Function(${name}(${arguments.map(_.typ.repr).mkString(", ")}))" + } +} + +class BasicBlockIterator(var ref: LLVMBasicBlockRef) extends Iterator[BasicBlock] { + override def hasNext: Boolean = ref != null + + override def next(): BasicBlock = { + val basicBlock = BasicBlock(ref) + this.ref = LLVMGetNextBasicBlock(ref) + basicBlock + } +} + +class ArgumentIterator(var ref: LLVMValueRef) extends Iterator[Argument] { + override def hasNext: Boolean = ref != null + var index = 0 + + override def next(): Argument = { + val argument = Argument(ref, index) + this.ref = LLVMGetNextParam(ref) + index += 1 + argument + } +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/GlobalVariable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/GlobalVariable.scala new file mode 100644 index 0000000000..5939f33590 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/GlobalVariable.scala @@ -0,0 +1,9 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM.LLVMGetInitializer + +case class GlobalVariable(ref: LLVMValueRef) extends Value(ref: LLVMValueRef) { + def initializer: Value = Value(LLVMGetInitializer(ref)).get +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala new file mode 100644 index 0000000000..582ee78b35 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala @@ -0,0 +1,213 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM._ +import org.opalj.ll.llvm.value.constant.ConstantIntValue +import org.opalj.ll.llvm.{FunctionType, Type} + +object OptionalInstruction { + def apply(ref: LLVMValueRef): Option[Instruction] = { + if (ref.isNull) return None + Some(Instruction(ref)) + } +} + +object Instruction { + def apply(ref: LLVMValueRef): Instruction = { + assert(ref != null && !ref.isNull(), "ref may not be null") + assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") + LLVMGetInstructionOpcode(ref) match { + case LLVMRet => Ret(ref) + case LLVMBr => Br(ref) + case LLVMSwitch => Switch(ref) + case LLVMIndirectBr => IndirectBr(ref) + case LLVMInvoke => Invoke(ref) + case LLVMUnreachable => Unreachable(ref) + case LLVMCallBr => CallBr(ref) + case LLVMFNeg => FNeg(ref) + case LLVMAdd => Add(ref) + case LLVMFAdd => FAdd(ref) + case LLVMSub => Sub(ref) + case LLVMFSub => FSub(ref) + case LLVMMul => Mul(ref) + case LLVMFMul => FMul(ref) + case LLVMUDiv => UDiv(ref) + case LLVMSDiv => SDiv(ref) + case LLVMFDiv => FDiv(ref) + case LLVMURem => URem(ref) + case LLVMSRem => SRem(ref) + case LLVMFRem => FRem(ref) + case LLVMShl => Shl(ref) + case LLVMLShr => LShr(ref) + case LLVMAShr => AShr(ref) + case LLVMAnd => And(ref) + case LLVMOr => Or(ref) + case LLVMXor => Xor(ref) + case LLVMAlloca => Alloca(ref) + case LLVMLoad => Load(ref) + case LLVMStore => Store(ref) + case LLVMGetElementPtr => GetElementPtr(ref) + case LLVMTrunc => Trunc(ref) + case LLVMZExt => ZExt(ref) + case LLVMSExt => SExt(ref) + case LLVMFPToUI => FPToUI(ref) + case LLVMFPToSI => FPToSI(ref) + case LLVMUIToFP => UIToFP(ref) + case LLVMSIToFP => SIToFP(ref) + case LLVMFPTrunc => FPTrunc(ref) + case LLVMFPExt => FPExt(ref) + case LLVMPtrToInt => PtrToInt(ref) + case LLVMIntToPtr => IntToPtr(ref) + case LLVMBitCast => BitCast(ref) + case LLVMAddrSpaceCast => AddrSpaceCast(ref) + case LLVMICmp => ICmp(ref) + case LLVMFCmp => FCmp(ref) + case LLVMPHI => PHI(ref) + case LLVMCall => Call(ref) + case LLVMSelect => Select(ref) + case LLVMUserOp1 => UserOp1(ref) + case LLVMUserOp2 => UserOp2(ref) + case LLVMVAArg => VAArg(ref) + case LLVMExtractElement => ExtractElement(ref) + case LLVMInsertElement => InsertElement(ref) + case LLVMShuffleVector => ShuffleVector(ref) + case LLVMExtractValue => ExtractValue(ref) + case LLVMInsertValue => InsertValue(ref) + case LLVMFreeze => Freeze(ref) + case LLVMFence => Fence(ref) + case LLVMAtomicCmpXchg => AtomicCmpXchg(ref) + case LLVMAtomicRMW => AtomicRMW(ref) + case LLVMResume => Resume(ref) + case LLVMLandingPad => LandingPad(ref) + case LLVMCleanupRet => CleanupRet(ref) + case LLVMCatchRet => CatchRet(ref) + case LLVMCatchPad => CatchPad(ref) + case LLVMCleanupPad => CleanupPad(ref) + case LLVMCatchSwitch => CatchSwitch(ref) + case opCode => throw new IllegalArgumentException("unknown instruction opcode: "+opCode) + } + } +} + +trait Terminator { + val ref: LLVMValueRef + def numSuccessors: Int = LLVMGetNumSuccessors(ref) + def hasSuccessors: Boolean = numSuccessors > 0 + def getSuccessor(i: Int) = BasicBlock(LLVMGetSuccessor(ref, i)) + def foreachSuccessor(f: BasicBlock => Unit): Unit = + (0 to numSuccessors - 1).foreach(i => f(getSuccessor(i))) + def successors: Seq[Instruction] = + (0 to numSuccessors - 1).map(i => getSuccessor(i).firstInstruction) +} + +sealed abstract class Instruction(ref: LLVMValueRef) extends User(ref) { + def isTerminator: Boolean = LLVMIsATerminatorInst(ref) != null + def parent: BasicBlock = BasicBlock(LLVMGetInstructionParent(ref)) + def function: Function = parent.parent + def next: Option[Instruction] = OptionalInstruction(LLVMGetNextInstruction(ref)) + + override def toString: String = { + s"${this.getClass.getSimpleName}(${repr})" + } +} + +case class Ret(ref: LLVMValueRef) extends Instruction(ref) with Terminator { + def value: Option[Value] = if (numOperands == 0 /* return void */ ) None else Some(operand(0)) +} +case class Br(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class Switch(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class IndirectBr(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class Invoke(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class Unreachable(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class CallBr(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class FNeg(ref: LLVMValueRef) extends Instruction(ref) +case class Add(ref: LLVMValueRef) extends Instruction(ref) { + def op1: Value = operand(0) + def op2: Value = operand(1) +} +case class FAdd(ref: LLVMValueRef) extends Instruction(ref) +case class Sub(ref: LLVMValueRef) extends Instruction(ref) { + def op1: Value = operand(0) + def op2: Value = operand(1) +} +case class FSub(ref: LLVMValueRef) extends Instruction(ref) +case class Mul(ref: LLVMValueRef) extends Instruction(ref) +case class FMul(ref: LLVMValueRef) extends Instruction(ref) +case class UDiv(ref: LLVMValueRef) extends Instruction(ref) +case class SDiv(ref: LLVMValueRef) extends Instruction(ref) +case class FDiv(ref: LLVMValueRef) extends Instruction(ref) +case class URem(ref: LLVMValueRef) extends Instruction(ref) +case class SRem(ref: LLVMValueRef) extends Instruction(ref) +case class FRem(ref: LLVMValueRef) extends Instruction(ref) +case class Shl(ref: LLVMValueRef) extends Instruction(ref) +case class LShr(ref: LLVMValueRef) extends Instruction(ref) +case class AShr(ref: LLVMValueRef) extends Instruction(ref) +case class And(ref: LLVMValueRef) extends Instruction(ref) +case class Or(ref: LLVMValueRef) extends Instruction(ref) +case class Xor(ref: LLVMValueRef) extends Instruction(ref) +case class Alloca(ref: LLVMValueRef) extends Instruction(ref) { + def allocatedType: Type = Type(LLVMGetAllocatedType(ref)) +} +case class Load(ref: LLVMValueRef) extends Instruction(ref) { + def src: Value = operand(0) +} +case class Store(ref: LLVMValueRef) extends Instruction(ref) { + def src: Value = operand(0) + def dst: Value = operand(1) +} +case class GetElementPtr(ref: LLVMValueRef) extends Instruction(ref) { + def base: Value = operand(0) + def isConstant = (1 until numOperands).forall(operand(_).isInstanceOf[ConstantIntValue]) + def constants = (1 until numOperands).map(operand(_).asInstanceOf[ConstantIntValue].signExtendedValue) + def isZero = isConstant && constants.forall(_ == 0) + + def numIndices: Int = LLVMGetNumIndices(ref) + def indices: Iterable[Int] = LLVMGetIndices(ref).asBuffer().array() +} +case class Trunc(ref: LLVMValueRef) extends Instruction(ref) +case class ZExt(ref: LLVMValueRef) extends Instruction(ref) +case class SExt(ref: LLVMValueRef) extends Instruction(ref) +case class FPToUI(ref: LLVMValueRef) extends Instruction(ref) +case class FPToSI(ref: LLVMValueRef) extends Instruction(ref) +case class UIToFP(ref: LLVMValueRef) extends Instruction(ref) +case class SIToFP(ref: LLVMValueRef) extends Instruction(ref) +case class FPTrunc(ref: LLVMValueRef) extends Instruction(ref) +case class FPExt(ref: LLVMValueRef) extends Instruction(ref) +case class PtrToInt(ref: LLVMValueRef) extends Instruction(ref) +case class IntToPtr(ref: LLVMValueRef) extends Instruction(ref) +case class BitCast(ref: LLVMValueRef) extends Instruction(ref) +case class AddrSpaceCast(ref: LLVMValueRef) extends Instruction(ref) +case class ICmp(ref: LLVMValueRef) extends Instruction(ref) +case class FCmp(ref: LLVMValueRef) extends Instruction(ref) +case class PHI(ref: LLVMValueRef) extends Instruction(ref) +case class Call(ref: LLVMValueRef) extends Instruction(ref) { + def calledValue: Value = Value(LLVMGetCalledValue(ref)).get // corresponds to last operand + def calledFunctionType: FunctionType = Type(LLVMGetCalledFunctionType(ref)).asInstanceOf[FunctionType] + def indexOfArgument(argument: Value): Option[Int] = { + for (i <- 0 until numOperands) + if (operand(i) == argument) return Some(i) + None + } + def numArgOperands: Int = LLVMGetNumArgOperands(ref) +} +case class Select(ref: LLVMValueRef) extends Instruction(ref) +case class UserOp1(ref: LLVMValueRef) extends Instruction(ref) +case class UserOp2(ref: LLVMValueRef) extends Instruction(ref) +case class VAArg(ref: LLVMValueRef) extends Instruction(ref) +case class ExtractElement(ref: LLVMValueRef) extends Instruction(ref) +case class InsertElement(ref: LLVMValueRef) extends Instruction(ref) +case class ShuffleVector(ref: LLVMValueRef) extends Instruction(ref) +case class ExtractValue(ref: LLVMValueRef) extends Instruction(ref) +case class InsertValue(ref: LLVMValueRef) extends Instruction(ref) +case class Freeze(ref: LLVMValueRef) extends Instruction(ref) +case class Fence(ref: LLVMValueRef) extends Instruction(ref) +case class AtomicCmpXchg(ref: LLVMValueRef) extends Instruction(ref) +case class AtomicRMW(ref: LLVMValueRef) extends Instruction(ref) +case class Resume(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class LandingPad(ref: LLVMValueRef) extends Instruction(ref) +case class CleanupRet(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class CatchRet(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class CatchPad(ref: LLVMValueRef) extends Instruction(ref) +case class CleanupPad(ref: LLVMValueRef) extends Instruction(ref) +case class CatchSwitch(ref: LLVMValueRef) extends Instruction(ref) with Terminator diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Use.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Use.scala new file mode 100644 index 0000000000..4cf208eb0f --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Use.scala @@ -0,0 +1,10 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value + +import org.bytedeco.llvm.LLVM.LLVMUseRef +import org.bytedeco.llvm.global.LLVM.{LLVMGetUsedValue, LLVMGetUser} + +case class Use(ref: LLVMUseRef) { + def value: Value = Value(LLVMGetUsedValue(ref)).get + def user: Value = Value(LLVMGetUser(ref)).get +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/User.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/User.scala new file mode 100644 index 0000000000..ec9bc7fd60 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/User.scala @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm +package value + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM._ + +class User(ref: LLVMValueRef) extends Value(ref) { + def numOperands: Int = LLVMGetNumOperands(ref) + def operand(index: Int): Value = { + assert(index < numOperands) + Value(LLVMGetOperand(ref, index)).get + } + def operandUse(index: Int): UsesIterator = { + assert(index < numOperands) + new UsesIterator(LLVMGetOperandUse(ref, index)) + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala new file mode 100644 index 0000000000..3e347a5e32 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala @@ -0,0 +1,79 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm +package value + +import org.bytedeco.llvm.LLVM.{LLVMUseRef, LLVMValueRef} +import org.bytedeco.llvm.global.LLVM._ +import org.opalj.ll.llvm.value.constant.{ConstantDataArray, ConstantDataVector, ConstantExpression, ConstantIntValue} + +class Value(ref: LLVMValueRef) { + def repr: String = { + val bytePointer = LLVMPrintValueToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } + + def name: String = { + LLVMGetValueName(ref).getString + } + + def typ: Type = Type(LLVMTypeOf(ref)) // because type is a keyword + + val address = ref.address + override def equals(other: Any): Boolean = + other.isInstanceOf[Value] && address == other.asInstanceOf[Value].address + + override def toString: String = { + s"${getClass.getSimpleName}(${repr})" + } + + def uses: UsesIterator = new UsesIterator(LLVMGetFirstUse(ref)) + def users: Iterator[Value] = uses.map(_.user) +} + +object Value { + def apply(ref: LLVMValueRef): Option[Value] = { + if (ref == null) return None + if (ref.isNull) return None + Some(LLVMGetValueKind(ref) match { + case LLVMArgumentValueKind => Argument(ref) + case LLVMBasicBlockValueKind => BasicBlock(LLVMValueAsBasicBlock(ref)) + //LLVMMemoryUseValueKind + //LLVMMemoryDefValueKind + //LLVMMemoryPhiValueKind + case LLVMFunctionValueKind => Function(ref) + //LLVMGlobalAliasValueKind + //LLVMGlobalIFuncValueKind + case LLVMGlobalVariableValueKind => GlobalVariable(ref) + //LLVMBlockAddressValueKind + case LLVMConstantExprValueKind => ConstantExpression(ref) + //LLVMConstantArrayValueKind + //LLVMConstantStructValueKind + //LLVMConstantVectorValueKind + //LLVMUndefValueValueKind + //LLVMConstantAggregateZeroValueKind + case LLVMConstantDataArrayValueKind => ConstantDataArray(ref) + case LLVMConstantDataVectorValueKind => ConstantDataVector(ref) + case LLVMConstantIntValueKind => ConstantIntValue(ref) + //LLVMConstantFPValueKind + //LLVMConstantPointerNullValueKind + //LLVMConstantTokenNoneValueKind + //LLVMMetadataAsValueValueKind + //LLVMInlineAsmValueKind + case LLVMInstructionValueKind => Instruction(ref) + //LLVMPoisonValueValueKind + case valueKind => throw new IllegalArgumentException("unknown valueKind: "+valueKind) + }) + } +} + +class UsesIterator(var ref: LLVMUseRef) extends Iterator[Use] { + override def hasNext: Boolean = ref != null + + override def next(): Use = { + val use = value.Use(ref) + this.ref = LLVMGetNextUse(ref) + use + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantDataSequential.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantDataSequential.scala new file mode 100644 index 0000000000..59c1e63ffc --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantDataSequential.scala @@ -0,0 +1,16 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ + +package org.opalj.ll.llvm +package value +package constant + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM.LLVMGetAsString +import org.bytedeco.javacpp.SizeTPointer + +abstract class ConstantDataSequential(ref: LLVMValueRef) extends User(ref) { + def asString: String = LLVMGetAsString(ref, new SizeTPointer(1)).getString +} + +case class ConstantDataArray(ref: LLVMValueRef) extends ConstantDataSequential(ref) +case class ConstantDataVector(ref: LLVMValueRef) extends ConstantDataSequential(ref) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala new file mode 100644 index 0000000000..2da9696b8c --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala @@ -0,0 +1,159 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value +package constant + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM._ +import org.opalj.ll.llvm.value.User + +object ConstantExpression { + def apply(ref: LLVMValueRef): ConstantExpression = { + assert(ref != null && !ref.isNull(), "ref may not be null") + assert(LLVMGetValueKind(ref) == LLVMConstantExprValueKind, "ref has to be an instruction") + LLVMGetConstOpcode(ref) match { + // case LLVMRet => RetConst(ref) + // case LLVMBr => BrConst(ref) + // case LLVMSwitch => SwitchConst(ref) + // case LLVMIndirectBr => IndirectBrConst(ref) + // case LLVMInvoke => InvokeConst(ref) + // case LLVMUnreachable => UnreachableConst(ref) + // case LLVMCallBr => CallBrConst(ref) + // case LLVMFNeg => FNegConst(ref) + // case LLVMAdd => AddConst(ref) + // case LLVMFAdd => FAddConst(ref) + // case LLVMSub => SubConst(ref) + // case LLVMFSub => FSubConst(ref) + // case LLVMMul => MulConst(ref) + // case LLVMFMul => FMulConst(ref) + // case LLVMUDiv => UDivConst(ref) + // case LLVMSDiv => SDivConst(ref) + // case LLVMFDiv => FDivConst(ref) + // case LLVMURem => URemConst(ref) + // case LLVMSRem => SRemConst(ref) + // case LLVMFRem => FRemConst(ref) + // case LLVMShl => ShlConst(ref) + // case LLVMLShr => LShrConst(ref) + // case LLVMAShr => AShrConst(ref) + // case LLVMAnd => AndConst(ref) + // case LLVMOr => OrConst(ref) + // case LLVMXor => XorConst(ref) + // case LLVMAlloca => AllocaConst(ref) + // case LLVMLoad => LoadConst(ref) + // case LLVMStore => StoreConst(ref) + case LLVMGetElementPtr => GetElementPtrConst(ref) + // case LLVMTrunc => TruncConst(ref) + // case LLVMZExt => ZExtConst(ref) + // case LLVMSExt => SExtConst(ref) + // case LLVMFPToUI => FPToUIConst(ref) + // case LLVMFPToSI => FPToSIConst(ref) + // case LLVMUIToFP => UIToFPConst(ref) + // case LLVMSIToFP => SIToFPConst(ref) + // case LLVMFPTrunc => FPTruncConst(ref) + // case LLVMFPExt => FPExtConst(ref) + // case LLVMPtrToInt => PtrToIntConst(ref) + // case LLVMIntToPtr => IntToPtrConst(ref) + // case LLVMBitCast => BitCastConst(ref) + // case LLVMAddrSpaceCast => AddrSpaceCastConst(ref) + // case LLVMICmp => ICmpConst(ref) + // case LLVMFCmp => FCmpConst(ref) + // case LLVMPHI => PHIConst(ref) + // case LLVMCall => CallConst(ref) + // case LLVMSelect => SelectConst(ref) + // case LLVMUserOp1 => UserOp1Const(ref) + // case LLVMUserOp2 => UserOp2Const(ref) + // case LLVMVAArg => VAArgConst(ref) + // case LLVMExtractElement => ExtractElementConst(ref) + // case LLVMInsertElement => InsertElementConst(ref) + // case LLVMShuffleVector => ShuffleVectorConst(ref) + // case LLVMExtractValue => ExtractValueConst(ref) + // case LLVMInsertValue => InsertValueConst(ref) + // case LLVMFreeze => FreezeConst(ref) + // case LLVMFence => FenceConst(ref) + // case LLVMAtomicCmpXchg => AtomicCmpXchgConst(ref) + // case LLVMAtomicRMW => AtomicRMWConst(ref) + // case LLVMResume => ResumeConst(ref) + // case LLVMLandingPad => LandingPadConst(ref) + // case LLVMCleanupRet => CleanupRetConst(ref) + // case LLVMCatchRet => CatchRetConst(ref) + // case LLVMCatchPad => CatchPadConst(ref) + // case LLVMCleanupPad => CleanupPadConst(ref) + // case LLVMCatchSwitch => CatchSwitchConst(ref) + case opCode => throw new IllegalArgumentException("unknown instruction opcode: "+opCode) + } + } +} + +sealed abstract class ConstantExpression(ref: LLVMValueRef) extends User(ref) + +// // case class RetConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class BrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SwitchConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class IndirectBrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class InvokeConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class UnreachableConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CallBrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FNegConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AddConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FAddConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SubConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FSubConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class MulConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FMulConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class UDivConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SDivConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FDivConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class URemConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SRemConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FRemConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ShlConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class LShrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AShrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AndConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class OrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class XorConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AllocaConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class LoadConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class StoreConst(ref: LLVMValueRef) extends ConstantExpression(ref) +case class GetElementPtrConst(ref: LLVMValueRef) extends ConstantExpression(ref) { + def base: Value = operand(0) + def isConstant = (1 until numOperands).forall(operand(_).isInstanceOf[ConstantIntValue]) + def constants = (1 until numOperands).map(operand(_).asInstanceOf[ConstantIntValue].signExtendedValue) + def isZero = isConstant && constants.forall(_ == 0) +} +// case class TruncConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ZExtConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SExtConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FPToUIConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FPToSIConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class UIToFPConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SIToFPConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FPTruncConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FPExtConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class PtrToIntConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class IntToPtrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class BitCastConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AddrSpaceCastConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ICmpConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FCmpConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class PHIConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CallConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SelectConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class UserOp1Const(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class UserOp2Const(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class VAArgConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ExtractElementConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class InsertElementConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ShuffleVectorConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ExtractValueConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class InsertValueConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FreezeConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FenceConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AtomicCmpXchgConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AtomicRMWConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ResumeConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class LandingPadConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CleanupRetConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CatchRetConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CatchPadConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CleanupPadConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CatchSwitchConst(ref: LLVMValueRef) extends ConstantExpression(ref) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ScalarConstants.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ScalarConstants.scala new file mode 100644 index 0000000000..6d74d224a6 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ScalarConstants.scala @@ -0,0 +1,12 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value +package constant + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM.{LLVMConstIntGetSExtValue, LLVMConstIntGetZExtValue} +import org.opalj.ll.llvm.value.User + +case class ConstantIntValue(ref: LLVMValueRef) extends User(ref) { + def zeroExtendedValue: Long = LLVMConstIntGetZExtValue(ref) + def signExtendedValue: Long = LLVMConstIntGetSExtValue(ref) +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/package.scala b/OPAL/ll/src/main/scala/org/opalj/ll/package.scala new file mode 100644 index 0000000000..c910eb56ee --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/package.scala @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory +import org.opalj.log.LogContext +import org.opalj.log.GlobalLogContext +import org.opalj.log.OPALLogger.info + +package object ll { + + final val FrameworkName = "OPAL LLVM" + + { + implicit val logContext: LogContext = GlobalLogContext + try { + assert(false) // <= test whether assertions are turned on or off... + info(FrameworkName, "Production Build") + } catch { + case _: AssertionError => info(FrameworkName, "Development Build with Assertions") + } + } + + // We want to make sure that the class loader is used which potentially can + // find the config files; the libraries (e.g., Typesafe Config) may have + // been loaded using the parent class loader and, hence, may not be able to + // find the config files at all. + val BaseConfig: Config = ConfigFactory.load(this.getClass.getClassLoader) + + final val ConfigKeyPrefix = "org.opalj.ll." +} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyKind.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyKind.scala index 497459e79f..ac8d388950 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyKind.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyKind.scala @@ -8,7 +8,7 @@ package org.opalj.fpcf * are generally only to be used if lower bounds cannot be computed or a very extensive and * are never of interest to any potential client. E.g., in case of an IFDS analysis, * computing the lower bound is not meaningful; in case of a call graph analysis, the lower - * bound is usually either prohibitively expensive or is not usefull to any analysis. + * bound is usually either prohibitively expensive or is not useful to any analysis. */ trait PropertyKind extends Any /* we now have a universal trait */ { diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyStoreContext.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyStoreContext.scala index 05cf265116..590483c615 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyStoreContext.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyStoreContext.scala @@ -18,7 +18,7 @@ class PropertyStoreContext[+T <: AnyRef] private (val key: Class[_], val data: T object PropertyStoreContext { - def apply[T <: AnyRef](key: Class[T], data: T): PropertyStoreContext[T] = { + def apply[T <: AnyRef, R <: T](key: Class[R], data: T): PropertyStoreContext[T] = { new PropertyStoreContext(key, data) } diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/AbstractIFDSFact.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/AbstractIFDSFact.scala new file mode 100644 index 0000000000..f5eecdae0f --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/AbstractIFDSFact.scala @@ -0,0 +1,12 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +/** + * The supertype of all IFDS facts, may implement "subsumes" to enable subsuming. + */ +trait AbstractIFDSFact + +/** + * The super type of all null facts. + */ +trait AbstractIFDSNullFact extends AbstractIFDSFact diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/Callable.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/Callable.scala new file mode 100644 index 0000000000..5cd097ae03 --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/Callable.scala @@ -0,0 +1,14 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +abstract class Callable { + /** + * The name of the Callable + */ + def name: String + + /** + * The full name of the Callable including its signature + */ + def signature: String +} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/DataFlowAnalysis.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/DataFlowAnalysis.scala new file mode 100644 index 0000000000..ec61bcd9dc --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/DataFlowAnalysis.scala @@ -0,0 +1,45 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import scala.collection.mutable + +abstract class DataFlowAnalysis[Facts >: Null <: AnyRef, C <: AnyRef, S <: Statement[_ <: C, _]] { + def icfg: ICFG[C, S] + def entryFacts: Facts + def transferFunction(facts: Facts, statement: S, successor: S): Facts + def join(left: Facts, right: Facts): Facts + + def perform(callable: C): Map[S, Facts] = { + var facts = Map.empty[S, Facts] + val workList = new mutable.Queue[S]() + + for (entryStatement <- icfg.startStatements(callable)) { + facts = facts.updated(entryStatement, entryFacts) + workList.enqueue(entryStatement) + } + + while (workList.nonEmpty) { + val statement = workList.dequeue() + val inFacts = facts.get(statement).get + + for (successor <- icfg.nextStatements(statement)) { + val newOutFacts = transferFunction(inFacts, statement, successor) + facts.get(successor) match { + case None => { + facts = facts.updated(successor, newOutFacts) + workList.enqueue(successor) + } + case Some(existingOutFacts) => { + val outFacts = join(existingOutFacts, newOutFacts) + if (outFacts ne existingOutFacts) { + facts = facts.updated(successor, outFacts) + workList.enqueue(successor) + } + } + } + } + } + + facts + } +} \ No newline at end of file diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/ICFG.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/ICFG.scala new file mode 100644 index 0000000000..98cf5b2350 --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/ICFG.scala @@ -0,0 +1,39 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import scala.collection.{Set => SomeSet} + +abstract class ICFG[C <: AnyRef, S <: Statement[_ <: C, _]] { + /** + * Determines the statements at which the analysis starts. + * + * @param callable The analyzed callable. + * @return The statements at which the analysis starts. + */ + def startStatements(callable: C): Set[S] + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + def nextStatements(statement: S): Set[S] + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + def getCalleesIfCallStatement(statement: S): Option[SomeSet[_ <: C]] + + /** + * Determines whether the statement is an exit statement. + * + * @param statement The source statement. + * @return Whether the statement flow may exit its callable (function/method) + */ + def isExitStatement(statement: S): Boolean +} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDS.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDS.scala new file mode 100644 index 0000000000..8a6203e663 --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDS.scala @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +object IFDS { + + /** + * Merges two maps that have sets as values. + * + * @param map1 The first map. + * @param map2 The second map. + * @return A map containing the keys of both maps. Each key is mapped to the union of both maps' + * values. + */ + def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { + var result = map1 + for ((key, values) <- map2) { + result.get(key) match { + case Some(resultValues) => + if (resultValues.size > values.size) + result = result.updated(key, resultValues ++ values) + else + result = result.updated(key, values ++ resultValues) + case None => + result = result.updated(key, values) + } + } + result + } +} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDSAnalysis.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDSAnalysis.scala new file mode 100644 index 0000000000..4e5762748c --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDSAnalysis.scala @@ -0,0 +1,463 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import org.opalj.fpcf._ +import org.opalj.fpcf.ifds.Dependees.Getter +import org.opalj.fpcf.scheduling.FPCFLazyAnalysisScheduler +import org.opalj.si.{FPCFAnalysis, MetaProject} + +import scala.collection.{mutable, Set => SomeSet} + +case class Dependees[Work]() { + case class Dependee(eOptionP: SomeEOptionP, worklist: Set[Work] = Set.empty) + var dependees = Map.empty[SomeEPK, Dependee] + def get(entity: Entity, propertyKey: PropertyKey[Property])(implicit propertyStore: PropertyStore, work: Work): SomeEOptionP = { + val epk = EPK(entity, propertyKey) + val dependee = dependees.get(epk) match { + case Some(dependee) => Dependee(dependee.eOptionP, dependee.worklist + work) + case None => Dependee(propertyStore(epk), Set(work)) + } + if (!dependee.eOptionP.isFinal) dependees += epk -> dependee + dependee.eOptionP + } + + def forResult: Set[SomeEOptionP] = { + dependees.values.map(_.eOptionP).toSet + } + def takeWork(epk: SomeEPK): Set[Work] = { + val dependee = dependees(epk) + dependees -= epk + dependee.worklist + } + + def getter()(implicit propertyStore: PropertyStore, work: Work): Getter = + (entity: Entity, propertyKey: PropertyKey[Property]) => get(entity, propertyKey) +} + +object Dependees { + type Getter = (Entity, PropertyKey[Property]) => SomeEOptionP +} + +/** + * Keeps book of the path edges. + * An entry of (statement, fact) means an edge (s0, source fact) -> (statement, fact) exists, + * that is the fact reaches the statement as an input. + * Source fact is the fact within the analysis entity. + */ +case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C <: AnyRef](subsumes: (Set[IFDSFact], IFDSFact) => Boolean) { + var edges = Map.empty[S, Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] + + /** + * Add the edge (s0, source fact) -> (statement, fact) to the path edges. + * Optionally give a predecessor for the statement. This is used for phi statements + * to distinguish the input flow and merge the facts later. + * @param statement the destination statement of the edge + * @param predecessor the predecessor of the statement. + * @return whether the edge was new + */ + def add(statement: S, fact: IFDSFact, predecessor: Option[S] = None): Boolean = { + edges.get(statement) match { + case None => + predecessor match { + case Some(predecessor) => + edges = edges.updated(statement, Right(Map(predecessor -> Set(fact)))) + case None => + edges = edges.updated(statement, Left(Set(fact))) + } + true + case Some(Left(existingFacts)) => + if (predecessor.isDefined) throw new IllegalArgumentException(s"${statement} does not accept a predecessor") + if (isNew(existingFacts, fact)) { + edges = edges.updated(statement, Left(existingFacts + fact)) + true + } else false + case Some(Right(existingFacts)) => + predecessor match { + case None => throw new IllegalArgumentException(s"${statement} requires a predecessor") + case Some(predecessor) => existingFacts.get(statement) match { + case Some(existingPredecessorFacts) => { + if (isNew(existingPredecessorFacts, fact)) { + edges = edges.updated(statement, Right(existingFacts.updated(predecessor, existingPredecessorFacts + fact))) + true + } else false + } + case None => { + edges = edges.updated(statement, Right(existingFacts.updated(predecessor, Set(fact)))) + true + } + } + } + } + } + + private def isNew(existingFacts: Set[IFDSFact], newFact: IFDSFact): Boolean = { + !existingFacts.contains(newFact) && !subsumes(existingFacts, newFact) + } + + /** + * @param statement + * @return The edges reaching statement if any. In case the statement minds about predecessors it is a map with an entry for each predecessor + */ + def get(statement: S): Option[Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] = edges.get(statement) + + def debugData: Map[S, Set[IFDSFact]] = edges.foldLeft(Map.empty[S, Set[IFDSFact]])((result, elem) => { + val facts: Set[IFDSFact] = elem._2 match { + case Right(facts) => facts.foldLeft(Set.empty[IFDSFact])(_ ++ _._2) + case Left(facts) => facts + } + result.updated(elem._1, result.getOrElse(elem._1, Set.empty) ++ facts) + }) +} + +/** + * The state of the analysis. For each method and source fact, there is a separate state. + * + * @param source The callable and input fact for which the callable is analyzed. + * @param subsumes The subsuming function, return whether a new fact is subsume by the existing ones + */ +protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _], Work]( + val source: (C, IFDSFact), + subsumes: (Set[IFDSFact], IFDSFact) => Boolean +) { + val dependees: Dependees[Work] = Dependees[Work]() + val pathEdges: PathEdges[IFDSFact, S, C] = PathEdges[IFDSFact, S, C](subsumes) + var endSummaries: Set[(S, IFDSFact)] = Set.empty[(S, IFDSFact)] + var selfDependees: Set[Work] = Set.empty[Work] +} + +/** + * Contains int variables, which count, how many times some method was called. + */ +class Statistics { + var normalFlow = 0 + var callFlow = 0 + var returnFlow = 0 + var callToReturnFlow = 0 + var subsumeTries = 0 + var subsumptions = 0 +} + +protected class ProjectFPCFAnalysis(val project: MetaProject) extends FPCFAnalysis + +/** + * + * @param ifdsProblem + * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. + * @tparam IFDSFact + */ +class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]]( + implicit + project: MetaProject, + val ifdsProblem: IFDSProblem[IFDSFact, C, S], + val propertyKey: IFDSPropertyMetaInformation[S, IFDSFact] +) extends ProjectFPCFAnalysis(project) { + type Work = (S, IFDSFact, Option[S]) // statement, fact, predecessor + type Worklist = mutable.Queue[Work] + type State = IFDSState[IFDSFact, C, S, Work] + + implicit var statistics = new Statistics + val icfg = ifdsProblem.icfg + + /** + * Performs an IFDS analysis for a method-fact-pair. + * + * @param entity The method-fact-pair, that will be analyzed. + * @return An IFDS property mapping from exit statements to the facts valid after these exit + * statements. Returns an interim result, if the TAC or call graph of this method or the + * IFDS analysis for a callee is still pending. + */ + def performAnalysis(entity: (C, IFDSFact)): ProperPropertyComputationResult = { + val (function, sourceFact) = entity + + // Start processing at the start of the icfg with the given source fact + implicit val state: State = new IFDSState[IFDSFact, C, S, Work](entity, subsumes) + implicit val queue: Worklist = mutable.Queue + .empty[Work] + icfg.startStatements(function).foreach { start => + state.pathEdges.add(start, sourceFact) // ifds line 2 + queue.enqueue((start, sourceFact, None)) // ifds line 3 + } + process() + createResult() + } + + /** + * Creates the current (intermediate) result for the analysis. + * + * @return A result containing a map, which maps each exit statement to the facts valid after + * the statement, based on the current results. If the analysis is still waiting for its + * method's TAC or call graph or the result of another method-fact-pair, an interim + * result will be returned. + * + */ + private def createResult()(implicit state: State): ProperPropertyComputationResult = { + val propertyValue = createPropertyValue() + val dependees = state.dependees.forResult + if (dependees.isEmpty) Result(state.source, propertyValue) + else InterimResult.forUB(state.source, propertyValue, dependees, propertyUpdate) + } + + /** + * Creates an IFDSProperty containing the result of this analysis. + * + * @param result Maps each exit statement to the facts, which hold after the exit statement. + * @return An IFDSProperty containing the `result`. + */ + private def createPropertyValue()(implicit state: State): IFDSProperty[S, IFDSFact] = + if (project.config.getBoolean(ConfigKeyPrefix+"debug")) + propertyKey.create(collectResult, state.pathEdges.debugData) + else + propertyKey.create(collectResult) + + /** + * Collects the facts valid at all exit nodes based on the current results. + * + * @return A map, mapping from each exit statement to the facts, which flow into exit statement. + */ + private def collectResult(implicit state: State): Map[S, Set[IFDSFact]] = { + state.endSummaries.foldLeft(Map.empty[S, Set[IFDSFact]])((result, entry) => + result.updated(entry._1, result.getOrElse(entry._1, Set.empty[IFDSFact]) + entry._2)) + } + + /** + * Called, when there is an updated result for a tac, call graph or another method-fact-pair, + * for which the current analysis is waiting. Re-analyzes the relevant parts of this method and + * returns the new analysis result. + * + * @param eps The new property value. + * @return The new (interim) result of this analysis. + */ + private def propertyUpdate( + eps: SomeEPS + )(implicit state: State): ProperPropertyComputationResult = { + implicit val queue: mutable.Queue[Work] = mutable.Queue() + state.dependees.takeWork(eps.toEPK).foreach(queue.enqueue(_)) + process() + createResult() + } + + /** + * Analyzes a queue of BasicBlocks. + * + * @param worklist + */ + private def process()(implicit state: State, worklist: Worklist): Unit = { + while (worklist.nonEmpty) { // ifds line 10 + implicit val work = worklist.dequeue() // ifds line 11 + val (statement, in, predecessor) = work + icfg.getCalleesIfCallStatement(statement) match { + case Some(callees) => handleCall(statement, callees, in) // ifds line 13 + case None => { + if (icfg.isExitStatement(statement)) handleExit(statement, in) // ifds line 21 + // in case of exceptions exit statements may also have some normal flow so no else here + handleOther(statement, in, predecessor) // ifds line 33 + } + } + } + } + + /** + * Processes a statement with a call. + * + * @param call The call statement. + * @param callees All possible callees. + * @param in The facts, which hold before the call statement. + * instead of the facts returned by callToStartFacts. + * @return A map, mapping from each successor statement of the `call` to the facts, which hold + * at their start. + */ + private def handleCall( + call: S, + callees: SomeSet[_ <: C], + in: IFDSFact + )( + implicit + state: State, + worklist: Worklist, + work: Work + ): Unit = { + val successors = icfg.nextStatements(call) + for (callee <- callees) { + ifdsProblem.outsideAnalysisContext(callee) match { + case Some(outsideAnalysisHandler) => + // Let the concrete analysis decide what to do. + for { + successor <- successors + out <- outsideAnalysisHandler(call, successor, in, state.dependees.getter()) // ifds line 17 (only summary edges) + } { + propagate(successor, out, call) // ifds line 18 + } + case None => + for { + successor <- successors + out <- concreteCallFlow(call, callee, in, successor) // ifds line 17 (only summary edges) + } { + propagate(successor, out, call) // ifds line 18 + } + } + } + for { + successor <- successors + out <- callToReturnFlow(call, in, successor) // ifds line 17 (without summary edge propagation) + } { + propagate(successor, out, call) // ifds line 18 + } + } + + private def concreteCallFlow(call: S, callee: C, in: IFDSFact, successor: S)(implicit state: State, work: Work): Set[IFDSFact] = { + var result = Set.empty[IFDSFact] + val entryFacts = callFlow(call, callee, in) + for (entryFact <- entryFacts) { // ifds line 14 + val e = (callee, entryFact) + val exitFacts: Map[S, Set[IFDSFact]] = if (e == state.source) { + // handle self dependency on our own because property store can't handle it + state.selfDependees += work + collectResult(state) + } else { + // handle all other dependencies using property store + val callFlows = state.dependees.get(e, propertyKey.key).asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] + callFlows match { + case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] => + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] => + ep.ub.flows + case _ => + Map.empty + } + } + for { + (exitStatement, exitStatementFacts) <- exitFacts // ifds line 15.2 + exitStatementFact <- exitStatementFacts // ifds line 15.3 + } { + result ++= returnFlow(exitStatement, exitStatementFact, call, in, successor) + } + } + result + } + + private def handleExit(statement: S, in: IFDSFact)(implicit state: State, worklist: Worklist): Unit = { + val newEdge = (statement, in) + if (!state.endSummaries.contains(newEdge)) { + state.endSummaries += ((statement, in)) // ifds line 21.1 + state.selfDependees.foreach(selfDependee => + worklist.enqueue(selfDependee)) + } + // ifds lines 22 - 31 are handled by the dependency propagation of the property store + // except for self dependencies which are handled above + } + + private def handleOther(statement: S, in: IFDSFact, predecessor: Option[S])(implicit state: State, worklist: Worklist): Unit = { + for { // ifds line 34 + successor <- icfg.nextStatements(statement) + out <- normalFlow(statement, in, predecessor) + } { + propagate(successor, out, statement) // ifds line 35 + } + } + + private def propagate(successor: S, out: IFDSFact, predecessor: S)(implicit state: State, worklist: Worklist): Unit = { + val predecessorOption = if (ifdsProblem.needsPredecessor(successor)) Some(predecessor) else None + + // ifds line 9 + if (state.pathEdges.add(successor, out, predecessorOption)) { + worklist.enqueue((successor, out, predecessorOption)) + } + } + + /** + * ifds flow function + * @param statement n + * @param in d2 + * @param predecessor pi + */ + private def normalFlow(statement: S, in: IFDSFact, predecessor: Option[S]): Set[IFDSFact] = { + statistics.normalFlow += 1 + addNullFactIfConfigured(in, ifdsProblem.normalFlow(statement, in, predecessor)) + } + + /** + * ifds passArgs function + * @param call n + * @param callee + * @param in d2 + * @return + */ + private def callFlow(call: S, callee: C, in: IFDSFact): Set[IFDSFact] = { + statistics.callFlow += 1 + addNullFactIfConfigured(in, ifdsProblem.callFlow(call, callee, in)) + } + + /** + * ifds returnVal function + * @param exit n + * @param in d2 + * @param call c + * @param callFact d4 + * @return + */ + private def returnFlow(exit: S, in: IFDSFact, call: S, callFact: IFDSFact, successor: S): Set[IFDSFact] = { + statistics.returnFlow += 1 + addNullFactIfConfigured(in, ifdsProblem.returnFlow(exit, in, call, callFact, successor)) + } + + /** + * ifds callFlow function + * @param call n + * @param in d2 + * @return + */ + private def callToReturnFlow(call: S, in: IFDSFact, successor: S): Set[IFDSFact] = { + statistics.callToReturnFlow += 1 + addNullFactIfConfigured(in, ifdsProblem.callToReturnFlow(call, in, successor)) + } + + private def addNullFactIfConfigured(in: IFDSFact, out: Set[IFDSFact]): Set[IFDSFact] = { + if (ifdsProblem.automaticallyPropagateNullFactInFlowFunctions && in == ifdsProblem.nullFact) + out + ifdsProblem.nullFact + else out + } + + protected def subsumes(existingFact: IFDSFact, newFact: IFDSFact): Boolean = false + + private def subsumes(existingFacts: Set[IFDSFact], newFact: IFDSFact): Boolean = { + statistics.subsumeTries += 1 + if (ifdsProblem.subsumeFacts && existingFacts.exists(subsumes(_, newFact))) { + statistics.subsumptions += 1 + true + } else false + } +} + +abstract class IFDSAnalysisScheduler[P <: MetaProject, IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]] + extends FPCFLazyAnalysisScheduler[P] { + final override type InitializationData = IFDSAnalysis[IFDSFact, C, S] + def property: IFDSPropertyMetaInformation[S, IFDSFact] + final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) + override def beforeSchedule(p: P, ps: PropertyStore): Unit = {} + + /** + * Registers the analysis as a lazy computation, that is, the method + * will call `ProperytStore.scheduleLazyComputation`. + */ + override def register( + p: P, + ps: PropertyStore, + analysis: IFDSAnalysis[IFDSFact, C, S] + ): FPCFAnalysis = { + ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) + analysis + } + + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { + val ifdsAnalysis = analysis.asInstanceOf[IFDSAnalysis[IFDSFact, C, S]] + for (e <- ifdsAnalysis.ifdsProblem.entryPoints) { + ps.force(e, ifdsAnalysis.propertyKey.key) + } + } + + override def afterPhaseCompletion( + p: P, + ps: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} +} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDSProblem.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDSProblem.scala new file mode 100644 index 0000000000..3010d41399 --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDSProblem.scala @@ -0,0 +1,122 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import org.opalj.fpcf.ifds.Dependees.Getter + +/** + * A framework for IFDS analyses. + * + * @tparam IFDSFact The type of flow facts, which are tracked by the concrete analysis. + * @author Dominik Helm + * @author Mario Trageser + * @author Marc Clement + */ +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]](val icfg: ICFG[C, S]) { + type Work = (S, IFDSFact, Option[S]) + + /** + * The null fact of this analysis. + */ + def nullFact: IFDSFact + + /** + * @return Whether the null Fact is automatically added to the result of every flow function where it is passed into + */ + def automaticallyPropagateNullFactInFlowFunctions: Boolean = true + + /** + * @return Whether to try to subsume new facts under existing facts and save graph edges + */ + def subsumeFacts: Boolean = false + + /** + * The entry points of this analysis. + */ + def entryPoints: Seq[(C, IFDSFact)] + + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param in The fact which holds before the execution of the `statement`. + * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be + * computed. Used for phi statements to distinguish the flow. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + def normalFlow( + statement: S, + in: IFDSFact, + predecessor: Option[S] + ): Set[IFDSFact] + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + def callFlow( + call: S, + callee: C, + in: IFDSFact + ): Set[IFDSFact] + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + def returnFlow( + exit: S, + in: IFDSFact, + call: S, + callFact: IFDSFact, + successor: S + ): Set[IFDSFact] + + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param in The facts, which hold before the `call`. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + def callToReturnFlow( + call: S, + in: IFDSFact, + successor: S + ): Set[IFDSFact] + + def needsPredecessor(statement: S): Boolean + + type OutsideAnalysisContextHandler = ((S, S, IFDSFact, Getter) => Set[IFDSFact]) { + def apply(call: S, successor: S, in: IFDSFact, dependeesGetter: Getter): Set[IFDSFact] + } + + /** + * Checks, if a callee is outside this analysis' context. + * By default, native methods are not inside the analysis context. + * For callees outside this analysis' context the returned handler is called + * to compute the summary edge for the call instead of analyzing the callee. + * + * @param callee The method called by `call`. + * @return The handler function. It receives + * the statement which invoked the call, + * the successor statement, which will be executed after the call and + * the set of input facts which hold before the `call`. + * It returns facts, which hold after the call, excluding the call to return flow. + */ + def outsideAnalysisContext(callee: C): Option[OutsideAnalysisContextHandler] +} \ No newline at end of file diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDSProperty.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDSProperty.scala new file mode 100644 index 0000000000..740043ec5a --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/IFDSProperty.scala @@ -0,0 +1,43 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyMetaInformation + +trait IFDSPropertyMetaInformation[S, IFDSFact <: AbstractIFDSFact] extends PropertyMetaInformation { + /** + * Creates an IFDSProperty containing the result of this analysis. + * + * @param result Maps each exit statement to the facts, which hold after the exit statement. + * @return An IFDSProperty containing the `result`. + */ + def create(result: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] + def create(result: Map[S, Set[IFDSFact]], debugData: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] +} + +abstract class IFDSProperty[S, IFDSFact <: AbstractIFDSFact] + extends Property + with IFDSPropertyMetaInformation[S, IFDSFact] { + + /** + * Maps exit statements to the data flow facts which hold before them. + */ + def flows: Map[S, Set[IFDSFact]] + + /** + * Maps all statements to the data flow facts which hold before them if debug setting is enabled. + */ + def debugData: Map[S, Set[IFDSFact]] + + override def equals(other: Any): Boolean = other match { + case that: IFDSProperty[S @unchecked, IFDSFact @unchecked] => + // We cached the "hashCode" to make the following comparison more efficient; + // note that all properties are eventually added to some set and therefore + // the hashCode is required anyway! + (this eq that) || (this.hashCode == that.hashCode && this.flows == that.flows) + case _ => + false + } + + override lazy val hashCode: Int = flows.hashCode() +} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/Statement.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/Statement.scala new file mode 100644 index 0000000000..6a37aa9226 --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/Statement.scala @@ -0,0 +1,7 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +abstract class Statement[C, Node] { + def node: Node + def callable: C +} diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/package.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/package.scala new file mode 100644 index 0000000000..bfdaa95b8f --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/ifds/package.scala @@ -0,0 +1,30 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf + +import com.typesafe.config.{Config, ConfigFactory} +import org.opalj.log.LogContext +import org.opalj.log.GlobalLogContext +import org.opalj.log.OPALLogger.info + +package object ifds { + + final val FrameworkName = "OPAL IFDS" + + { + implicit val logContext: LogContext = GlobalLogContext + try { + assert(false) // <= test whether assertions are turned on or off... + info(FrameworkName, "Production Build") + } catch { + case _: AssertionError => info(FrameworkName, "Development Build with Assertions") + } + } + + // We want to make sure that the class loader is used which potentially can + // find the config files; the libraries (e.g., Typesafe Config) may have + // been loaded using the parent class loader and, hence, may not be able to + // find the config files at all. + val BaseConfig: Config = ConfigFactory.load(this.getClass.getClassLoader) + + final val ConfigKeyPrefix = "org.opalj.ifds." +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysesManager.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysesManager.scala similarity index 88% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysesManager.scala rename to OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysesManager.scala index 4b2355bec1..fd541bd736 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysesManager.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysesManager.scala @@ -1,19 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf +package org.opalj.fpcf.scheduling import com.typesafe.config.Config - +import org.opalj.fpcf._ import org.opalj.log.LogContext import org.opalj.log.OPALLogger.debug +import org.opalj.si.{FPCFAnalysis, MetaProject, PropertyStoreKey} import org.opalj.util.PerformanceEvaluation._ -import org.opalj.br.analyses.SomeProject -import org.opalj.fpcf.AnalysisScenario -import org.opalj.fpcf.ComputationSpecification -import org.opalj.fpcf.PropertyKey -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.Schedule /** * Enables the execution of a set of analyses. @@ -23,7 +16,7 @@ import org.opalj.fpcf.Schedule * @author Michael Reif * @author Michael Eichberg */ -class FPCFAnalysesManager private[fpcf] (val project: SomeProject) { +class FPCFAnalysesManager private[fpcf] (val project: MetaProject) { // caching (by means of using local fields) is not necessary private[this] implicit final def logContext: LogContext = project.logContext diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysesManagerKey.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysesManagerKey.scala new file mode 100644 index 0000000000..ae064d1708 --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysesManagerKey.scala @@ -0,0 +1,20 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.scheduling + +import org.opalj.si.{MetaProject, ProjectInformationKey, ProjectInformationKeys, PropertyStoreKey} + +/** + * The ''key'' object to get the [[FPCFAnalysesManager]]. + * + * @example + * To get an instance of the [[FPCFAnalysesManager]] pass this key to a + * project's `get` method. + * @author Michael Reif + */ +object FPCFAnalysesManagerKey extends ProjectInformationKey[MetaProject, FPCFAnalysesManager, Nothing] { + + override def requirements(project: MetaProject): ProjectInformationKeys[MetaProject] = List(PropertyStoreKey) + + override def compute(project: MetaProject): FPCFAnalysesManager = new FPCFAnalysesManager(project) + +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysesRegistry.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysesRegistry.scala similarity index 82% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysesRegistry.scala rename to OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysesRegistry.scala index 997d460fbe..d405ac0dfb 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysesRegistry.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysesRegistry.scala @@ -1,17 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf +package org.opalj.fpcf.scheduling -import scala.reflect.runtime.universe.runtimeMirror - -import com.typesafe.config.ConfigFactory -import com.typesafe.config.ConfigObject +import com.typesafe.config.{ConfigFactory, ConfigObject} +import org.opalj.log.OPALLogger.{error, info} +import org.opalj.log.{GlobalLogContext, LogContext} -import org.opalj.log.GlobalLogContext -import org.opalj.log.LogContext -import org.opalj.log.OPALLogger.error -import org.opalj.log.OPALLogger.info +import scala.reflect.runtime.universe.runtimeMirror /** * Registry for all factories for analyses that are implemented using the fixpoint computations @@ -23,18 +17,17 @@ import org.opalj.log.OPALLogger.info * * @note Analysis schedules can be computed using the `PropertiesComputationsScheduler`. * - * ==Thread Safety== - * The registry is thread safe. - * + * ==Thread Safety== + * The registry is thread safe. * @author Michael Reif * @author Michael Eichberg */ object FPCFAnalysesRegistry { - private[this] implicit def logContext: LogContext = GlobalLogContext + implicit private[this] def logContext: LogContext = GlobalLogContext - private[this] var idToEagerScheduler: Map[String, FPCFEagerAnalysisScheduler] = Map.empty - private[this] var idToLazyScheduler: Map[String, FPCFLazyAnalysisScheduler] = Map.empty + private[this] var idToEagerScheduler: Map[String, FPCFEagerAnalysisScheduler[_]] = Map.empty + private[this] var idToLazyScheduler: Map[String, FPCFLazyAnalysisScheduler[_]] = Map.empty private[this] var idToDescription: Map[String, String] = Map.empty /** @@ -54,10 +47,12 @@ object FPCFAnalysesRegistry { ): Unit = this.synchronized { val analysisRunner = resolveAnalysisRunner(analysisFactory) if (analysisRunner.nonEmpty) { - if (lazyFactory) idToLazyScheduler += - ((analysisID, analysisRunner.get.asInstanceOf[FPCFLazyAnalysisScheduler])) - else idToEagerScheduler += - ((analysisID, analysisRunner.get.asInstanceOf[FPCFEagerAnalysisScheduler])) + if (lazyFactory) + idToLazyScheduler += + ((analysisID, analysisRunner.get.asInstanceOf[FPCFLazyAnalysisScheduler[_]])) + else + idToEagerScheduler += + ((analysisID, analysisRunner.get.asInstanceOf[FPCFEagerAnalysisScheduler[_]])) idToDescription += ((analysisID, analysisDescription)) val analysisType = if (lazyFactory) "lazy" else "eager" @@ -69,12 +64,12 @@ object FPCFAnalysesRegistry { } } - private[this] def resolveAnalysisRunner(fqn: String): Option[FPCFAnalysisScheduler] = { + private[this] def resolveAnalysisRunner(fqn: String): Option[FPCFAnalysisScheduler[_]] = { val mirror = runtimeMirror(getClass.getClassLoader) try { val module = mirror.staticModule(fqn) import mirror.reflectModule - Some(reflectModule(module).instance.asInstanceOf[FPCFAnalysisScheduler]) + Some(reflectModule(module).instance.asInstanceOf[FPCFAnalysisScheduler[_]]) } catch { case sre: ScalaReflectionException => error("FPCF registry", "cannot find analysis scheduler", sre) @@ -99,7 +94,7 @@ object FPCFAnalysesRegistry { registeredAnalyses foreach { a => register(a.id, a.description, a.factory) } - */ + */ val registeredAnalyses = config.getObject("org.opalj.fpcf.registry.analyses") val entriesIterator = registeredAnalyses.entrySet.iterator while (entriesIterator.hasNext) { @@ -138,28 +133,28 @@ object FPCFAnalysesRegistry { /** * Returns the current view of the registry for eager factories. */ - def eagerFactories: Iterable[FPCFEagerAnalysisScheduler] = this.synchronized { + def eagerFactories: Iterable[FPCFEagerAnalysisScheduler[_]] = this.synchronized { idToEagerScheduler.values } /** * Returns the current view of the registry for lazy factories. */ - def lazyFactories: Iterable[FPCFLazyAnalysisScheduler] = this.synchronized { + def lazyFactories: Iterable[FPCFLazyAnalysisScheduler[_]] = this.synchronized { idToLazyScheduler.values } /** * Returns the eager factory for analysis with a matching description. */ - def eagerFactory(id: String): FPCFEagerAnalysisScheduler = this.synchronized { + def eagerFactory(id: String): FPCFEagerAnalysisScheduler[_] = this.synchronized { idToEagerScheduler(id) } /** * Returns the lazy factory for analysis with a matching description. */ - def lazyFactory(id: String): FPCFLazyAnalysisScheduler = this.synchronized { + def lazyFactory(id: String): FPCFLazyAnalysisScheduler[_] = this.synchronized { idToLazyScheduler(id) } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysisScheduler.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysisScheduler.scala similarity index 58% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysisScheduler.scala rename to OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysisScheduler.scala index ea2cab8689..64707a6e99 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysisScheduler.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFAnalysisScheduler.scala @@ -1,15 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf +package org.opalj.fpcf.scheduling -import java.util.concurrent.atomic.AtomicInteger +import org.opalj.fpcf.{ComputationSpecification, PropertyBounds, PropertyStore} +import org.opalj.si.{FPCFAnalysis, MetaProject, ProjectInformationKeys} -import org.opalj.fpcf.ComputationSpecification -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyStore -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject +import java.util.concurrent.atomic.AtomicInteger +import scala.reflect.{ClassTag, classTag} /** * Specification of the properties of an analysis. @@ -20,16 +16,17 @@ import org.opalj.br.analyses.SomeProject * @author Michael Reif * @author Michael Eichberg */ -trait FPCFAnalysisScheduler extends ComputationSpecification[FPCFAnalysis] { +trait FPCFAnalysisScheduler[P <: MetaProject] extends ComputationSpecification[FPCFAnalysis] { + implicit val c: ClassTag[P] /** - * Returns all [[org.opalj.br.analyses.ProjectInformationKey]]s required by the analyses. + * Returns all [[ProjectInformationKey]]s required by the analyses. * * This information is in particular required by keys which - when the key is computed - make use of * other keys which are not statically known at compile time. If a single key that is (transitively) * used is not correctly listed, a deadlock will _always_ occur. */ - def requiredProjectInformation: ProjectInformationKeys + def requiredProjectInformation: ProjectInformationKeys[P] /** * The unique id of this factory. @@ -39,37 +36,37 @@ trait FPCFAnalysisScheduler extends ComputationSpecification[FPCFAnalysis] { final val uniqueId: Int = FPCFAnalysisScheduler.nextId final override def init(ps: PropertyStore): InitializationData = { - init(ps.context(classOf[SomeProject]), ps) + init(ps.context(classTag[P].runtimeClass).asInstanceOf[P], ps) } final override def uses(ps: PropertyStore): Set[PropertyBounds] = { - uses ++ uses(ps.context(classOf[SomeProject]), ps) + uses ++ uses(ps.context(classTag[P].runtimeClass).asInstanceOf[P], ps) } final override def beforeSchedule(ps: PropertyStore): Unit = { - beforeSchedule(ps.context(classOf[SomeProject]), ps) + beforeSchedule(ps.context(classTag[P].runtimeClass).asInstanceOf[P], ps) } final override def afterPhaseCompletion(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { - afterPhaseCompletion(ps.context(classOf[SomeProject]), ps, analysis) + afterPhaseCompletion(ps.context(classTag[P].runtimeClass).asInstanceOf[P], ps, analysis) } - def init(p: SomeProject, ps: PropertyStore): InitializationData + def init(p: P, ps: PropertyStore): InitializationData /** The uses that are configuration (project) dependent. */ - def uses(p: SomeProject, ps: PropertyStore): Set[PropertyBounds] = Set.empty + def uses(p: P, ps: PropertyStore): Set[PropertyBounds] = Set.empty /** The uses that are configuration independent. */ def uses: Set[PropertyBounds] - def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit + def beforeSchedule(p: P, ps: PropertyStore): Unit - def afterPhaseCompletion(p: SomeProject, ps: PropertyStore, analysis: FPCFAnalysis): Unit + def afterPhaseCompletion(p: P, ps: PropertyStore, analysis: FPCFAnalysis): Unit } /** - * Companion object of [[org.opalj.br.fpcf.FPCFAnalysisScheduler]] that defines internal helper + * Companion object of [[FPCFAnalysisScheduler]] that defines internal helper * functions and values. * * @author Michael Reif diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFEagerAnalysisScheduler.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFEagerAnalysisScheduler.scala similarity index 61% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFEagerAnalysisScheduler.scala rename to OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFEagerAnalysisScheduler.scala index 949cabf1e1..c844ec4a6c 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFEagerAnalysisScheduler.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFEagerAnalysisScheduler.scala @@ -1,34 +1,31 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf +package org.opalj.fpcf.scheduling -import org.opalj.fpcf.ComputationType -import org.opalj.fpcf.EagerComputation -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyStore -import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{ComputationType, EagerComputation, PropertyBounds, PropertyStore} +import org.opalj.si.{FPCFAnalysis, MetaProject, PropertyStoreKey} + +import scala.reflect.classTag /** * Factory for FPCF analyses which should be directly started/scheduled. * * @author Michael Eichberg */ -trait FPCFEagerAnalysisScheduler extends FPCFAnalysisScheduler { +trait FPCFEagerAnalysisScheduler[P <: MetaProject] extends FPCFAnalysisScheduler[P] { final override def computationType: ComputationType = EagerComputation final override def derivesLazily: Option[PropertyBounds] = None final override def schedule(ps: PropertyStore, i: InitializationData): FPCFAnalysis = { - start(ps.context(classOf[SomeProject]), ps, i) + start(ps.context(classTag[P].runtimeClass).asInstanceOf[P], ps, i) } /** * Starts the analysis for the given `project`. This method is typically implicitly * called by the [[FPCFAnalysesManager]]. */ - def start(p: SomeProject, i: InitializationData): FPCFAnalysis = { + def start(p: P, i: InitializationData): FPCFAnalysis = { start(p, p.get(PropertyStoreKey), i) } @@ -36,12 +33,12 @@ trait FPCFEagerAnalysisScheduler extends FPCFAnalysisScheduler { * Called when a schedule is executed and when this analysis shall register itself * with the property store using * [[org.opalj.fpcf.PropertyStore.scheduleEagerComputationForEntity]] or a variant thereof. - * This method is typically implicitly called by the [[org.opalj.br.fpcf.FPCFAnalysesManager]]. + * This method is typically implicitly called by the [[FPCFAnalysesManager]]. * * @note This analysis must not call `registerTriggeredComputation` or * `registerLazyPropertyComputation`. */ - def start(p: SomeProject, ps: PropertyStore, i: InitializationData): FPCFAnalysis + def start(p: P, ps: PropertyStore, i: InitializationData): FPCFAnalysis } /** @@ -49,15 +46,15 @@ trait FPCFEagerAnalysisScheduler extends FPCFAnalysisScheduler { * steps. */ // TODO Rename => Simple... -trait BasicFPCFEagerAnalysisScheduler extends FPCFEagerAnalysisScheduler { +trait BasicFPCFEagerAnalysisScheduler[P <: MetaProject] extends FPCFEagerAnalysisScheduler[P] { override type InitializationData = Null - override def init(p: SomeProject, ps: PropertyStore): Null = null - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + override def init(p: P, ps: PropertyStore): Null = null + override def beforeSchedule(p: P, ps: PropertyStore): Unit = {} override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} override def afterPhaseCompletion( - p: SomeProject, + p: P, ps: PropertyStore, analysis: FPCFAnalysis ): Unit = {} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFLazyAnalysisScheduler.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFLazyAnalysisScheduler.scala similarity index 57% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFLazyAnalysisScheduler.scala rename to OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFLazyAnalysisScheduler.scala index de46885817..5a7c91f728 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFLazyAnalysisScheduler.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFLazyAnalysisScheduler.scala @@ -1,12 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf +package org.opalj.fpcf.scheduling -import org.opalj.fpcf.ComputationType -import org.opalj.fpcf.LazyComputation -import org.opalj.fpcf.PropertyStore -import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{ComputationType, LazyComputation, PropertyStore} +import org.opalj.si.{FPCFAnalysis, MetaProject} /** * The underlying analysis will only be registered with the property store and @@ -14,7 +10,7 @@ import org.opalj.br.analyses.SomeProject * * @author Michael Eichberg */ -trait FPCFLazyAnalysisScheduler extends FPCFLazyLikeAnalysisScheduler { +trait FPCFLazyAnalysisScheduler[P <: MetaProject] extends FPCFLazyLikeAnalysisScheduler[P] { final override def computationType: ComputationType = LazyComputation @@ -25,15 +21,15 @@ trait FPCFLazyAnalysisScheduler extends FPCFLazyLikeAnalysisScheduler { * steps. */ // TODO Rename => Simple... -trait BasicFPCFLazyAnalysisScheduler extends FPCFLazyAnalysisScheduler { +trait BasicFPCFLazyAnalysisScheduler[P <: MetaProject] extends FPCFLazyAnalysisScheduler[P] { override type InitializationData = Null - override def init(p: SomeProject, ps: PropertyStore): Null = null - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + override def init(p: P, ps: PropertyStore): Null = null + override def beforeSchedule(p: P, ps: PropertyStore): Unit = {} override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} override def afterPhaseCompletion( - p: SomeProject, + p: P, ps: PropertyStore, analysis: FPCFAnalysis ): Unit = {} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFLazyLikeAnalysisScheduler.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFLazyLikeAnalysisScheduler.scala similarity index 65% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFLazyLikeAnalysisScheduler.scala rename to OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFLazyLikeAnalysisScheduler.scala index 5750715f00..fb2bbd99df 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFLazyLikeAnalysisScheduler.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFLazyLikeAnalysisScheduler.scala @@ -1,16 +1,15 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf +package org.opalj.fpcf.scheduling -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyStore -import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{PropertyBounds, PropertyStore} +import org.opalj.si.{FPCFAnalysis, MetaProject, PropertyStoreKey} + +import scala.reflect.classTag /** * @author Michael Eichberg */ -trait FPCFLazyLikeAnalysisScheduler extends FPCFAnalysisScheduler { +trait FPCFLazyLikeAnalysisScheduler[P <: MetaProject] extends FPCFAnalysisScheduler[P] { override def derivesLazily: Some[PropertyBounds] @@ -22,10 +21,10 @@ trait FPCFLazyLikeAnalysisScheduler extends FPCFAnalysisScheduler { ps: PropertyStore, i: InitializationData ): FPCFAnalysis = { - register(ps.context(classOf[SomeProject]), ps, i) + register(ps.context(classTag[P].runtimeClass).asInstanceOf[P], ps, i) } - final def register(project: SomeProject, i: InitializationData): FPCFAnalysis = { + final def register(project: P, i: InitializationData): FPCFAnalysis = { register(project, project.get(PropertyStoreKey), i) } @@ -35,16 +34,15 @@ trait FPCFLazyLikeAnalysisScheduler extends FPCFAnalysisScheduler { * [[org.opalj.fpcf.PropertyStore.registerLazyPropertyComputation]] or * [[org.opalj.fpcf.PropertyStore.registerTransformer]] method. * - * This method is typically called by the [[org.opalj.br.fpcf.FPCFAnalysesManager]]. + * This method is typically called by the [[org.opalj.fpcf.scheduling.FPCFAnalysesManager]]. * * @note This analysis must not call `registerTriggeredComputation` or a variant of * `scheduleEagerComputationForEntity`. */ def register( - project: SomeProject, + project: P, propertyStore: PropertyStore, i: InitializationData ): FPCFAnalysis } - diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFTransformerScheduler.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFTransformerScheduler.scala similarity index 54% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFTransformerScheduler.scala rename to OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFTransformerScheduler.scala index 4fd12dcd54..6fdc9ecb62 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFTransformerScheduler.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFTransformerScheduler.scala @@ -1,12 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf +package org.opalj.fpcf.scheduling -import org.opalj.fpcf.ComputationType -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.Transformer -import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{ComputationType, PropertyStore, Transformer} +import org.opalj.si.{FPCFAnalysis, MetaProject} /** * The underlying analysis will only be registered with the property store and @@ -15,21 +11,21 @@ import org.opalj.br.analyses.SomeProject * * @author Michael Eichberg */ -trait FPCFTransformerScheduler extends FPCFLazyLikeAnalysisScheduler { +trait FPCFTransformerScheduler[P <: MetaProject] extends FPCFLazyLikeAnalysisScheduler[P] { final override def computationType: ComputationType = Transformer } -trait BasicFPCFTransformerScheduler extends FPCFTransformerScheduler { +trait BasicFPCFTransformerScheduler[P <: MetaProject] extends FPCFTransformerScheduler[P] { override type InitializationData = Null - override def init(p: SomeProject, ps: PropertyStore): Null = null - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + override def init(p: P, ps: PropertyStore): Null = null + override def beforeSchedule(p: P, ps: PropertyStore): Unit = {} override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} override def afterPhaseCompletion( - p: SomeProject, + p: P, ps: PropertyStore, analysis: FPCFAnalysis ): Unit = {} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFTriggeredAnalysisScheduler.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFTriggeredAnalysisScheduler.scala similarity index 66% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFTriggeredAnalysisScheduler.scala rename to OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFTriggeredAnalysisScheduler.scala index 1a94942ab0..3036f88a96 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFTriggeredAnalysisScheduler.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/scheduling/FPCFTriggeredAnalysisScheduler.scala @@ -1,14 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf +package org.opalj.fpcf.scheduling -import org.opalj.fpcf.ComputationType -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyKind -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.TriggeredComputation -import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf._ +import org.opalj.si.{FPCFAnalysis, MetaProject, PropertyStoreKey} + +import scala.reflect.classTag /** * The underlying analysis will only be registered with the property store and @@ -20,14 +16,14 @@ import org.opalj.br.analyses.SomeProject * * @author Michael Eichberg */ -trait FPCFTriggeredAnalysisScheduler extends FPCFAnalysisScheduler { +trait FPCFTriggeredAnalysisScheduler[P <: MetaProject] extends FPCFAnalysisScheduler[P] { final override def computationType: ComputationType = TriggeredComputation final override def derivesLazily: Option[PropertyBounds] = None final override def schedule(ps: PropertyStore, i: InitializationData): FPCFAnalysis = { - register(ps.context(classOf[SomeProject]), ps, i) + register(ps.context(classTag[P].runtimeClass).asInstanceOf[P], ps, i) } /** @@ -35,35 +31,35 @@ trait FPCFTriggeredAnalysisScheduler extends FPCFAnalysisScheduler { */ def triggeredBy: PropertyKind - final def register(project: SomeProject, i: InitializationData): FPCFAnalysis = { + final def register(project: P, i: InitializationData): FPCFAnalysis = { register(project, project.get(PropertyStoreKey), i) } /** * Called when a schedule is executed and when this analysis shall register itself * with the property store using [[org.opalj.fpcf.PropertyStore#registerTriggeredComputation]]. - * This method is typically called by the [[org.opalj.br.fpcf.FPCFAnalysesManager]]. + * This method is typically called by the [[FPCFAnalysesManager]]. * * @note This analysis must not call `registerLazyPropertyComputation` or a variant of * `scheduleEagerComputationForEntity`. */ def register( - project: SomeProject, + project: P, propertyStore: PropertyStore, i: InitializationData ): FPCFAnalysis } -trait BasicFPCFTriggeredAnalysisScheduler extends FPCFTriggeredAnalysisScheduler { +trait BasicFPCFTriggeredAnalysisScheduler[P <: MetaProject] extends FPCFTriggeredAnalysisScheduler[P] { override type InitializationData = Null - override def init(p: SomeProject, ps: PropertyStore): Null = null - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + override def init(p: P, ps: PropertyStore): Null = null + override def beforeSchedule(p: P, ps: PropertyStore): Unit = {} override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = {} override def afterPhaseCompletion( - p: SomeProject, + p: P, ps: PropertyStore, analysis: FPCFAnalysis ): Unit = {} diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysis.scala b/OPAL/si/src/main/scala/org/opalj/si/FPCFAnalysis.scala similarity index 57% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysis.scala rename to OPAL/si/src/main/scala/org/opalj/si/FPCFAnalysis.scala index 8a42716810..83e567f55e 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/FPCFAnalysis.scala +++ b/OPAL/si/src/main/scala/org/opalj/si/FPCFAnalysis.scala @@ -1,22 +1,20 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf +package org.opalj.si import org.opalj.fpcf.PropertyStore -import org.opalj.br.analyses.ProjectBasedAnalysis -import org.opalj.br.analyses.SomeProject +import org.opalj.log.LogContext /** * Common super-trait of all analysis which use MISAF. * (Formerly known as Fixpoint-Computations Framework/PropertyStore.) */ -trait FPCFAnalysis extends ProjectBasedAnalysis { +trait FPCFAnalysis { + val project: MetaProject + + implicit def p: MetaProject = project + implicit final def logContext: LogContext = project.logContext final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) final def ps: PropertyStore = propertyStore - } - -abstract class DefaultFPCFAnalysis(val project: SomeProject) extends FPCFAnalysis diff --git a/OPAL/si/src/main/scala/org/opalj/si/MetaProject.scala b/OPAL/si/src/main/scala/org/opalj/si/MetaProject.scala new file mode 100644 index 0000000000..e0fc18cab5 --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/si/MetaProject.scala @@ -0,0 +1,187 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.si + +import com.typesafe.config.Config +import org.opalj.log.LogContext +import org.opalj.log.OPALLogger.info +import org.opalj.util.PerformanceEvaluation.time + +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.atomic.AtomicReferenceArray + +trait MetaProject { + + protected var projectInformation: AtomicReferenceArray[AnyRef] + implicit val logContext: LogContext + implicit val config: Config + // -------------------------------------------------------------------------------------------- + // + // CODE TO MAKE IT POSSIBLE TO ATTACH SOME INFORMATION TO A PROJECT (ON DEMAND) + // + // -------------------------------------------------------------------------------------------- + + /** + * Here, the usage of the project information key does not lead to its initialization! + */ + private[this] val projectInformationKeyInitializationData = { + new ConcurrentHashMap[ProjectInformationKey[_ <: MetaProject, AnyRef, AnyRef], AnyRef]() + } + + /** + * Returns the project specific initialization information for the given project information + * key. + */ + def getProjectInformationKeyInitializationData[P <: MetaProject, T <: AnyRef, I <: AnyRef]( + key: ProjectInformationKey[P, T, I] + ): Option[I] = { + Option(projectInformationKeyInitializationData.get(key).asInstanceOf[I]) + } + + /** + * Gets the project information key specific initialization object. If an object is already + * registered, that object will be used otherwise `info` will be evaluated and that value + * will be added and also returned. + * + * @note Initialization data is discarded once the key is used. + */ + def getOrCreateProjectInformationKeyInitializationData[P <: MetaProject, T <: AnyRef, I <: AnyRef]( + key: ProjectInformationKey[P, T, I], + info: => I + ): I = { + projectInformationKeyInitializationData.computeIfAbsent( + key.asInstanceOf[ProjectInformationKey[P, AnyRef, AnyRef]], + (_: ProjectInformationKey[_, AnyRef, AnyRef]) => info + ).asInstanceOf[I] + } + + /** + * Updates project information key specific initialization object. If an object is already + * registered, that object will be given to `info`. + * + * @note Initialization data is discarded once the key is used. + */ + def updateProjectInformationKeyInitializationData[P <: MetaProject, T <: AnyRef, I <: AnyRef]( + key: ProjectInformationKey[P, T, I] + )( + info: Option[I] => I + ): I = { + projectInformationKeyInitializationData.compute( + key.asInstanceOf[ProjectInformationKey[MetaProject, AnyRef, AnyRef]], + (_, current: AnyRef) => { + info(Option(current.asInstanceOf[I])) + }: I + ).asInstanceOf[I] + } + + /** + * Returns the additional project information that is ''currently'' available. + * + * If some analyses are still running it may be possible that additional + * information will be made available as part of the execution of those + * analyses. + * + * @note This method redetermines the available project information on each call. + */ + def availableProjectInformation: List[AnyRef] = { + var pis = List.empty[AnyRef] + val projectInformation = this.projectInformation + for (i <- 0 until projectInformation.length()) { + val pi = projectInformation.get(i) + if (pi != null) { + pis = pi :: pis + } + } + pis + } + + /** + * Returns the information attached to this project that is identified by the + * given `ProjectInformationKey`. + * + * If the information was not yet required, the information is computed and + * returned. Subsequent calls will directly return the information. + * + * @note (Development Time) + * Every analysis using [[ProjectInformationKey]]s must list '''All + * requirements; failing to specify a requirement can end up in a deadlock.''' + * @see [[ProjectInformationKey]] for further information. + */ + def get[P <: MetaProject, T <: AnyRef](pik: ProjectInformationKey[P, T, _]): T = { + val pikUId = pik.uniqueId + + /* Synchronization is done by the caller! */ + def derive(projectInformation: AtomicReferenceArray[AnyRef]): T = { + var className = pik.getClass.getSimpleName + if (className.endsWith("Key")) + className = className.substring(0, className.length - 3) + else if (className.endsWith("Key$")) + className = className.substring(0, className.length - 4) + + for (requiredProjectInformationKey <- pik.requirements(this)) { + get(requiredProjectInformationKey) + } + val pi = time { + val pi = pik.compute(this) + // we don't need the initialization data anymore + projectInformationKeyInitializationData.remove(pik) + pi + } { t => info("project", s"initialization of $className took ${t.toSeconds}") } + projectInformation.set(pikUId, pi) + pi + } + + val projectInformation = this.projectInformation + if (pikUId < projectInformation.length()) { + val pi = projectInformation.get(pikUId) + if (pi ne null) { + pi.asInstanceOf[T] + } else { + this.synchronized { + // It may be the case that the underlying array was replaced! + val projectInformation = this.projectInformation + // double-checked locking (works with Java >=6) + val pi = projectInformation.get(pikUId) + if (pi ne null) { + pi.asInstanceOf[T] + } else { + derive(projectInformation) + } + } + } + } else { + // We have to synchronize w.r.t. "this" object on write accesses + // to make sure that we do not loose a concurrent update or + // derive an information more than once. + this.synchronized { + val projectInformation = this.projectInformation + if (pikUId >= projectInformation.length()) { + val newLength = Math.max(projectInformation.length * 2, pikUId * 2) + val newProjectInformation = new AtomicReferenceArray[AnyRef](newLength) + org.opalj.control.iterateUntil(0, projectInformation.length()) { i => + newProjectInformation.set(i, projectInformation.get(i)) + } + this.projectInformation = newProjectInformation + return derive(newProjectInformation) + } + } + // else (pikUId < projectInformation.length()) => the underlying array is "large enough" + get(pik) + } + } + + /** + * Tests if the information identified by the given [[ProjectInformationKey]] + * is available. If the information is not (yet) available, the information + * will not be computed; `None` will be returned. + * + * @see [[ProjectInformationKey]] for further information. + */ + def has[P <: MetaProject, T <: AnyRef](pik: ProjectInformationKey[P, T, _]): Option[T] = { + val pikUId = pik.uniqueId + + if (pikUId < this.projectInformation.length()) + Option(this.projectInformation.get(pikUId).asInstanceOf[T]) + else + None + } +} diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectInformationKey.scala b/OPAL/si/src/main/scala/org/opalj/si/ProjectInformationKey.scala similarity index 93% rename from OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectInformationKey.scala rename to OPAL/si/src/main/scala/org/opalj/si/ProjectInformationKey.scala index 3ad431c428..46d9ac05a2 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/ProjectInformationKey.scala +++ b/OPAL/si/src/main/scala/org/opalj/si/ProjectInformationKey.scala @@ -1,10 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package analyses +package org.opalj.si import java.util.concurrent.atomic.AtomicInteger - /** * `ProjectInformationKey` objects are used to get/associate some * (immutable) information with a project that should be computed on demand. @@ -50,7 +47,7 @@ import java.util.concurrent.atomic.AtomicInteger * * @author Michael Eichberg */ -trait ProjectInformationKey[T <: AnyRef, I <: AnyRef] { +trait ProjectInformationKey[-P <: MetaProject, T <: AnyRef, I <: AnyRef] { /** * The unique id of this key. The key is used to enable efficient access and @@ -70,7 +67,7 @@ trait ProjectInformationKey[T <: AnyRef, I <: AnyRef] { * @note Classes/Objects that implement this trait should not make the method `public` * to avoid that this method is called accidentally by regular user code. */ - /*ABSTRACT*/ def requirements(project: SomeProject): ProjectInformationKeys + /*ABSTRACT*/ def requirements(project: MetaProject): ProjectInformationKeys[P] /** * Computes the information for the given project. @@ -79,7 +76,7 @@ trait ProjectInformationKey[T <: AnyRef, I <: AnyRef] { * make this method public. This method is only expected to be called * by an instance of a `Project`. */ - /*ABSTRACT*/ def compute(project: SomeProject): T + /*ABSTRACT*/ def compute(project: MetaProject): T } diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala b/OPAL/si/src/main/scala/org/opalj/si/PropertyStoreKey.scala similarity index 79% rename from OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala rename to OPAL/si/src/main/scala/org/opalj/si/PropertyStoreKey.scala index 62c85c6e86..9497ce13c2 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/PropertyStoreKey.scala +++ b/OPAL/si/src/main/scala/org/opalj/si/PropertyStoreKey.scala @@ -1,29 +1,21 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package br -package fpcf +package org.opalj.si import com.typesafe.config.Config -import net.ceedubs.ficus.Ficus._ - -import org.opalj.log.LogContext -import org.opalj.log.OPALLogger import org.opalj.concurrent.NumberOfThreadsForCPUBoundTasks -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.PropertyStoreContext -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{PropertyStore, PropertyStoreContext} +import org.opalj.log.{LogContext, OPALLogger} +import net.ceedubs.ficus.Ficus._ /** * The ''key'' object to get the project's [[org.opalj.fpcf.PropertyStore]]. * * @note It is possible to set the project's `debug` flag using the project's * `org.opalj.br.analyses.PropertyStore.debug` config key. - * * @author Michael Eichberg */ object PropertyStoreKey - extends ProjectInformationKey[PropertyStore, (List[PropertyStoreContext[AnyRef]]) => PropertyStore] { + extends ProjectInformationKey[MetaProject, PropertyStore, (List[PropertyStoreContext[AnyRef]]) => PropertyStore] { final val configKey = "org.opalj.fpcf.PropertyStore.Default" @@ -41,17 +33,19 @@ object PropertyStoreKey * * @return `Nil`. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: MetaProject): Seq[ProjectInformationKey[MetaProject, Nothing, Nothing]] = + Nil /** * Creates a new empty property store using the current [[parallelismLevel]]. */ - override def compute(project: SomeProject): PropertyStore = { + override def compute(project: MetaProject): PropertyStore = { implicit val logContext: LogContext = project.logContext val context: List[PropertyStoreContext[AnyRef]] = List( - PropertyStoreContext(classOf[SomeProject], project), - PropertyStoreContext(classOf[Config], project.config) + PropertyStoreContext(classOf[MetaProject], project), + PropertyStoreContext(classOf[Config], project.config), + PropertyStoreContext(project.getClass, project) ) project.getProjectInformationKeyInitializationData(this) match { case Some(psFactory) => diff --git a/OPAL/si/src/main/scala/org/opalj/si/package.scala b/OPAL/si/src/main/scala/org/opalj/si/package.scala new file mode 100644 index 0000000000..e6c7f39c2f --- /dev/null +++ b/OPAL/si/src/main/scala/org/opalj/si/package.scala @@ -0,0 +1,6 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj + +package object si { + type ProjectInformationKeys[-P <: MetaProject] = Seq[ProjectInformationKey[_ <: MetaProject, _ <: AnyRef, _ <: AnyRef]] +} diff --git a/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/FPCFAnalysesIntegrationTest.scala b/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/FPCFAnalysesIntegrationTest.scala index a1c59341eb..809044dba6 100644 --- a/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/FPCFAnalysesIntegrationTest.scala +++ b/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/FPCFAnalysesIntegrationTest.scala @@ -5,18 +5,14 @@ package fpcf package analyses import scala.reflect.runtime.universe.runtimeMirror - import java.nio.file.Path import java.nio.file.Paths import java.util.zip.GZIPInputStream - import scala.io.Source - import com.typesafe.config.ConfigValueFactory import org.junit.runner.RunWith import org.scalatest.funspec.AnyFunSpec import org.scalatestplus.junit.JUnitRunner - import org.opalj.util.Nanoseconds import org.opalj.util.PerformanceEvaluation.time import org.opalj.fpcf.ComputationSpecification @@ -29,13 +25,11 @@ import org.opalj.fpcf.SomePropertyKey import org.opalj.br.TestSupport.allBIProjects import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.FPCFAnalysesRegistry -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.properties.Context import org.opalj.ai.domain.l1 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.fpcf.scheduling.{FPCFAnalysesManagerKey, FPCFAnalysesRegistry} +import org.opalj.si.{FPCFAnalysis, PropertyStoreKey} import org.opalj.tac.cg.CHACallGraphKey import org.opalj.tac.fpcf.analyses.FPCFAnalysesIntegrationTest.factory import org.opalj.tac.fpcf.analyses.FPCFAnalysesIntegrationTest.p @@ -78,7 +72,7 @@ class FPCFAnalysesIntegrationTest extends AnyFunSpec { requirements + classOf[l1.DefaultDomainWithCFGAndDefUse[_]] } } else { - // Recreate project keeping all ProjectInformationKeys other than the + // Recreate project keeping all JavaProjectInformationKeys other than the // PropertyStore as we are interested only in FPCF analysis results. p = p.recreate { id => id != PropertyStoreKey.uniqueId && diff --git a/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/L1PuritySmokeTest.scala b/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/L1PuritySmokeTest.scala index a914f89ee6..66a72c5705 100644 --- a/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/L1PuritySmokeTest.scala +++ b/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/L1PuritySmokeTest.scala @@ -8,7 +8,6 @@ import org.junit.runner.RunWith import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers import org.scalatestplus.junit.JUnitRunner - import org.opalj.util.Nanoseconds import org.opalj.util.PerformanceEvaluation.time import org.opalj.fpcf.ComputationSpecification @@ -16,13 +15,12 @@ import org.opalj.br.TestSupport.allBIProjects import org.opalj.br.TestSupport.createJREProject import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.br.fpcf.analyses.EagerClassImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerTypeImmutabilityAnalysis import org.opalj.br.fpcf.analyses.EagerVirtualMethodPurityAnalysis -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.fpcf.properties.VirtualMethodPurity +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.{FPCFAnalysis, PropertyStoreKey} import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.purity.EagerL1PurityAnalysis diff --git a/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/SimpleEscapeAnalysisSmokeTest.scala b/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/SimpleEscapeAnalysisSmokeTest.scala index 47a451be24..afd3b6269d 100644 --- a/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/SimpleEscapeAnalysisSmokeTest.scala +++ b/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/SimpleEscapeAnalysisSmokeTest.scala @@ -14,9 +14,9 @@ import org.opalj.util.PerformanceEvaluation.time import org.opalj.br.TestSupport.allBIProjects import org.opalj.br.TestSupport.createJREProject import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.ai.fpcf.analyses.LazyL0BaseAIAnalysis +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey import org.opalj.tac.fpcf.analyses.escape.EagerSimpleEscapeAnalysis /** diff --git a/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/TACAIAnalysisIntegrationTest.scala b/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/TACAIAnalysisIntegrationTest.scala index fd381e6b3f..074a66033f 100644 --- a/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/TACAIAnalysisIntegrationTest.scala +++ b/OPAL/tac/src/it/scala/org/opalj/tac/fpcf/analyses/TACAIAnalysisIntegrationTest.scala @@ -15,10 +15,10 @@ import org.opalj.util.PerformanceEvaluation.time import org.opalj.concurrent.ConcurrentExceptions import org.opalj.br.analyses.SomeProject import org.opalj.br.TestSupport -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.ai.fpcf.analyses.LazyL0BaseAIAnalysis import org.opalj.ai.fpcf.properties.BaseAIResult +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey import org.opalj.tac.fpcf.properties.TACAI /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/ComputeTACAIKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/ComputeTACAIKey.scala index 0ad2abe758..c7b1b7f975 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/ComputeTACAIKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/ComputeTACAIKey.scala @@ -3,12 +3,12 @@ package org.opalj.tac import org.opalj.value.ValueInformation import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKey import org.opalj.br.analyses.SomeProject import org.opalj.ai.domain.l1.DefaultDomainWithCFGAndDefUse import org.opalj.ai.BaseAI import org.opalj.ai.domain.RecordDefUse import org.opalj.ai.Domain +import org.opalj.br.analyses.JavaProjectInformationKey /** * ''Key'' to compute the 3-address based code of a method computed using the configured @@ -18,7 +18,6 @@ import org.opalj.ai.Domain * * @example To get the index use the [[org.opalj.br.analyses.Project]]'s `get` method and * pass in `this` object. - * * @author Michael Eichberg */ object ComputeTACAIKey extends TACAIKey[Method => Domain with RecordDefUse] { @@ -26,7 +25,7 @@ object ComputeTACAIKey extends TACAIKey[Method => Domain with RecordDefUse] { /** * TACAI code has no special prerequisites. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[Nothing, Nothing]] = Nil /** * Returns an factory which computes the 3-address code of a method anew when called. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/EagerDetachedTACAIKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/EagerDetachedTACAIKey.scala index f3b8e15c6c..d8ec5a5c55 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/EagerDetachedTACAIKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/EagerDetachedTACAIKey.scala @@ -8,12 +8,12 @@ import scala.collection.mutable import org.opalj.value.ValueInformation import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKey import org.opalj.br.analyses.SomeProject import org.opalj.ai.domain.l1.DefaultDomainWithCFGAndDefUse import org.opalj.ai.BaseAI import org.opalj.ai.domain.RecordDefUse import org.opalj.ai.Domain +import org.opalj.br.analyses.JavaProjectInformationKey /** * ''Key'' to get the 3-address based code of a method computed using the configured @@ -22,7 +22,6 @@ import org.opalj.ai.Domain * * @example To get the index use the [[org.opalj.br.analyses.Project]]'s `get` method and * pass in `this` object. - * * @author Michael Eichberg */ object EagerDetachedTACAIKey extends TACAIKey[Method => Domain with RecordDefUse] { @@ -30,7 +29,7 @@ object EagerDetachedTACAIKey extends TACAIKey[Method => Domain with RecordDefUse /** * TACAI code has no special prerequisites. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[Nothing, Nothing]] = Nil /** * Returns an factory which computes and caches the 3-address code of a method when required. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/LazyDetachedTACAIKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/LazyDetachedTACAIKey.scala index af311563f9..6fbc803775 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/LazyDetachedTACAIKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/LazyDetachedTACAIKey.scala @@ -6,12 +6,12 @@ import scala.collection.concurrent.TrieMap import org.opalj.br.Method import org.opalj.br.analyses.SomeProject -import org.opalj.br.analyses.ProjectInformationKey import org.opalj.ai.domain.l1.DefaultDomainWithCFGAndDefUse import org.opalj.ai.BaseAI import org.opalj.value.ValueInformation import org.opalj.ai.domain.RecordDefUse import org.opalj.ai.Domain +import org.opalj.br.analyses.JavaProjectInformationKey /** * ''Key'' to get the 3-address based code of a method computed using the configured @@ -22,7 +22,6 @@ import org.opalj.ai.Domain * * @example To get the index use the [[org.opalj.br.analyses.Project]]'s `get` method and * pass in `this` object. - * * @author Michael Eichberg */ object LazyDetachedTACAIKey extends TACAIKey[Method => Domain with RecordDefUse] { @@ -30,7 +29,7 @@ object LazyDetachedTACAIKey extends TACAIKey[Method => Domain with RecordDefUse] /** * TACAI code has no special prerequisites. */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + override def requirements(project: SomeProject): Seq[JavaProjectInformationKey[Nothing, Nothing]] = Nil /** * Returns an factory which computes and caches the 3-address code of a method when required. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/LazyTACUsingAIKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/LazyTACUsingAIKey.scala index 1191d3cff4..8e08482029 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/LazyTACUsingAIKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/LazyTACUsingAIKey.scala @@ -6,11 +6,11 @@ import scala.collection.concurrent.TrieMap import org.opalj.br.Method import org.opalj.br.analyses.SomeProject -import org.opalj.br.analyses.ProjectInformationKey import org.opalj.ai.domain.RecordDefUse import org.opalj.ai.Domain import org.opalj.ai.AIResult import org.opalj.ai.common.SimpleAIKey +import org.opalj.br.analyses.JavaProjectInformationKey import org.opalj.value.ValueInformation /** @@ -29,7 +29,7 @@ object LazyTACUsingAIKey extends TACAIKey[Nothing] { */ override def requirements( project: SomeProject - ): Seq[ProjectInformationKey[Method => AIResult { val domain: Domain with RecordDefUse }, _ <: AnyRef]] = { + ): Seq[JavaProjectInformationKey[Method => AIResult { val domain: Domain with RecordDefUse }, _ <: AnyRef]] = { Seq(SimpleAIKey) } @@ -39,7 +39,7 @@ object LazyTACUsingAIKey extends TACAIKey[Nothing] { * All methods belonging to a project are converted using the same `domainFactory`. Hence, * the `domainFactory` needs to be set before compute is called/this key is passed to a * specific project. If multiple projects are instead concurrently, external synchronization - * is necessary (e.g., on the ProjectInformationKey) to ensure that each project is + * is necessary (e.g., on the JavaProjectInformationKey) to ensure that each project is * instantiated using the desired domain. */ override def compute( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/TACAIKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/TACAIKey.scala index 65ddcfc2d6..2b0945ccd3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/TACAIKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/TACAIKey.scala @@ -3,11 +3,11 @@ package org.opalj package tac import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKey +import org.opalj.br.analyses.JavaProjectInformationKey import org.opalj.value.ValueInformation /** * @author Michael Eichberg */ trait TACAIKey[I <: AnyRef] - extends ProjectInformationKey[Method => AITACode[TACMethodParameter, ValueInformation], I] + extends JavaProjectInformationKey[Method => AITACode[TACMethodParameter, ValueInformation], I] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/cg/AllocationSiteBasedPointsToCallGraphKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/cg/AllocationSiteBasedPointsToCallGraphKey.scala index 09cfe07e47..e206af9ab7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/cg/AllocationSiteBasedPointsToCallGraphKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/cg/AllocationSiteBasedPointsToCallGraphKey.scala @@ -3,11 +3,11 @@ package org.opalj package tac package cg -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.VirtualFormalParametersKey -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.SimpleContextsKey +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.analyses.cg.AllocationSitesPointsToTypeIterator import org.opalj.tac.fpcf.analyses.pointsto.AllocationSiteBasedArraycopyPointsToAnalysisScheduler @@ -20,7 +20,7 @@ import org.opalj.tac.fpcf.analyses.pointsto.AllocationSiteBasedUnsafePointsToAna import org.opalj.tac.fpcf.analyses.pointsto.ReflectionAllocationsAnalysisScheduler /** - * A [[org.opalj.br.analyses.ProjectInformationKey]] to compute a [[CallGraph]] based on + * A [[JavaProjectInformationKey]] to compute a [[CallGraph]] based on * the points-to analysis. * * @see [[CallGraphKey]] for further details. @@ -29,14 +29,14 @@ import org.opalj.tac.fpcf.analyses.pointsto.ReflectionAllocationsAnalysisSchedul */ object AllocationSiteBasedPointsToCallGraphKey extends CallGraphKey { - override def requirements(project: SomeProject): ProjectInformationKeys = { + override def requirements(project: SomeProject): JavaProjectInformationKeys = { Seq(DefinitionSitesKey, VirtualFormalParametersKey, SimpleContextsKey) ++: super.requirements(project) } override protected[cg] def callGraphSchedulers( project: SomeProject - ): Iterable[FPCFAnalysisScheduler] = { + ): Iterable[JavaFPCFAnalysisScheduler] = { val isLibrary = project.config.getString("org.opalj.br.analyses.cg.InitialEntryPointsKey.analysis") == "org.opalj.br.analyses.cg.LibraryEntryPointsFinder" @@ -50,6 +50,7 @@ object AllocationSiteBasedPointsToCallGraphKey extends CallGraphKey { AllocationSiteBasedNewInstanceAnalysisScheduler ) ::: (if (isLibrary) List(AllocationSiteBasedLibraryPointsToAnalysisScheduler) else Nil) } + override def getTypeIterator(project: SomeProject) = new AllocationSitesPointsToTypeIterator(project) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/cg/CFA_1_0_CallGraphKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/cg/CFA_1_0_CallGraphKey.scala index d3da07b184..e108b71cfa 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/cg/CFA_1_0_CallGraphKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/cg/CFA_1_0_CallGraphKey.scala @@ -3,15 +3,15 @@ package org.opalj package tac package cg -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler import org.opalj.tac.fpcf.analyses.cg.CallStringContextProvider import org.opalj.tac.fpcf.analyses.cg.TypeIterator import org.opalj.tac.fpcf.analyses.cg.TypesBasedPointsToTypeIterator /** - * A [[org.opalj.br.analyses.ProjectInformationKey]] to compute a [[CallGraph]] based on + * A [[JavaProjectInformationKey]] to compute a [[CallGraph]] based on * the points-to analysis. * * @see [[CallGraphKey]] for further details. @@ -20,13 +20,13 @@ import org.opalj.tac.fpcf.analyses.cg.TypesBasedPointsToTypeIterator */ object CFA_1_0_CallGraphKey extends CallGraphKey { - override def requirements(project: SomeProject): ProjectInformationKeys = { + override def requirements(project: SomeProject): JavaProjectInformationKeys = { TypeBasedPointsToCallGraphKey.requirements(project) } override protected def callGraphSchedulers( project: SomeProject - ): Iterable[FPCFAnalysisScheduler] = { + ): Iterable[JavaFPCFAnalysisScheduler] = { TypeBasedPointsToCallGraphKey.callGraphSchedulers(project) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/cg/CFA_1_1_CallGraphKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/cg/CFA_1_1_CallGraphKey.scala index ac5ffe73d9..57fca1343c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/cg/CFA_1_1_CallGraphKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/cg/CFA_1_1_CallGraphKey.scala @@ -3,13 +3,13 @@ package org.opalj package tac package cg -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler import org.opalj.tac.fpcf.analyses.cg.CFA_k_l_TypeIterator /** - * A [[org.opalj.br.analyses.ProjectInformationKey]] to compute a [[CallGraph]] based on + * A [[JavaProjectInformationKey]] to compute a [[CallGraph]] based on * the points-to analysis. * * @see [[CallGraphKey]] for further details. @@ -18,13 +18,13 @@ import org.opalj.tac.fpcf.analyses.cg.CFA_k_l_TypeIterator */ object CFA_1_1_CallGraphKey extends CallGraphKey { - override def requirements(project: SomeProject): ProjectInformationKeys = { + override def requirements(project: SomeProject): JavaProjectInformationKeys = { AllocationSiteBasedPointsToCallGraphKey.requirements(project) } override protected def callGraphSchedulers( project: SomeProject - ): Iterable[FPCFAnalysisScheduler] = { + ): Iterable[JavaFPCFAnalysisScheduler] = { AllocationSiteBasedPointsToCallGraphKey.callGraphSchedulers(project) } override def getTypeIterator(project: SomeProject) = diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/cg/CHACallGraphKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/cg/CHACallGraphKey.scala index 201f15693d..d3cb854e68 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/cg/CHACallGraphKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/cg/CHACallGraphKey.scala @@ -3,16 +3,16 @@ package org.opalj package tac package cg -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.VirtualFormalParametersKey -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.SimpleContextsKey +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.analyses.cg.CHATypeIterator /** - * A [[org.opalj.br.analyses.ProjectInformationKey]] to compute a [[CallGraph]] based on class + * A [[JavaProjectInformationKey]] to compute a [[CallGraph]] based on class * hierarchy analysis (CHA). * * @see [[CallGraphKey]] for further details. @@ -21,14 +21,14 @@ import org.opalj.tac.fpcf.analyses.cg.CHATypeIterator */ object CHACallGraphKey extends CallGraphKey { - override def requirements(project: SomeProject): ProjectInformationKeys = { + override def requirements(project: SomeProject): JavaProjectInformationKeys = { Seq(DefinitionSitesKey, VirtualFormalParametersKey, SimpleContextsKey) ++: super.requirements(project) } override protected def callGraphSchedulers( project: SomeProject - ): Iterable[FPCFAnalysisScheduler] = List.empty + ): Iterable[JavaFPCFAnalysisScheduler] = List.empty override def getTypeIterator(project: SomeProject) = new CHATypeIterator(project) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/cg/CallGraphKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/cg/CallGraphKey.scala index c91562e536..1750e75e79 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/cg/CallGraphKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/cg/CallGraphKey.scala @@ -4,36 +4,31 @@ package tac package cg import scala.reflect.runtime.universe.runtimeMirror - import scala.jdk.CollectionConverters._ - import org.opalj.log.LogContext import org.opalj.log.OPALLogger import org.opalj.log.OPALLogger.error import org.opalj.fpcf.PropertyStore -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.{DeclaredMethodsKey, JavaProjectInformationKey, JavaProjectInformationKeys, SomeProject} import org.opalj.br.analyses.cg.InitialEntryPointsKey -import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.br.analyses.cg.CallBySignatureKey import org.opalj.br.analyses.cg.IsOverridableMethodKey -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.FPCFAnalysisScheduler -import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey import org.opalj.tac.fpcf.analyses.LazyTACAIProvider import org.opalj.tac.fpcf.analyses.cg.CallGraphAnalysisScheduler import org.opalj.tac.fpcf.analyses.cg.TypeIterator /** - * An abstract [[org.opalj.br.analyses.ProjectInformationKey]] to compute a [[CallGraph]]. + * An abstract [[JavaProjectInformationKey]] to compute a [[CallGraph]]. * Uses the call graph analyses modules specified in the config file under the key * "org.opalj.tac.cg.CallGraphKey.modules". * * @author Florian Kuebler * @author Dominik Helm */ -trait CallGraphKey extends ProjectInformationKey[CallGraph, Nothing] { +trait CallGraphKey extends JavaProjectInformationKey[CallGraph, Nothing] { private[this] val CallBySignatureConfigKey = "org.opalj.br.analyses.cg.callBySignatureResolution" @@ -45,9 +40,9 @@ trait CallGraphKey extends ProjectInformationKey[CallGraph, Nothing] { */ protected def callGraphSchedulers( project: SomeProject - ): Iterable[FPCFAnalysisScheduler] + ): Iterable[JavaFPCFAnalysisScheduler] - override def requirements(project: SomeProject): ProjectInformationKeys = { + override def requirements(project: SomeProject): JavaProjectInformationKeys = { project.updateProjectInformationKeyInitializationData(TypeIteratorKey) { case Some(typeIterator: TypeIterator) if typeIterator ne this.typeIterator => implicit val logContext: LogContext = project.logContext @@ -76,7 +71,7 @@ trait CallGraphKey extends ProjectInformationKey[CallGraph, Nothing] { registeredAnalyses(project).flatMap(_.requiredProjectInformation) } - protected[this] def registeredAnalyses(project: SomeProject): scala.collection.Seq[FPCFAnalysisScheduler] = { + protected[this] def registeredAnalyses(project: SomeProject): scala.collection.Seq[JavaFPCFAnalysisScheduler] = { implicit val logContext: LogContext = project.logContext val config = project.config @@ -93,7 +88,7 @@ trait CallGraphKey extends ProjectInformationKey[CallGraph, Nothing] { val manager = project.get(FPCFAnalysesManagerKey) // TODO make TACAI analysis configurable - var analyses: List[FPCFAnalysisScheduler] = + var analyses: List[JavaFPCFAnalysisScheduler] = List( LazyTACAIProvider ) @@ -122,12 +117,12 @@ trait CallGraphKey extends ProjectInformationKey[CallGraph, Nothing] { private[this] def resolveAnalysisRunner( className: String - )(implicit logContext: LogContext): Option[FPCFAnalysisScheduler] = { + )(implicit logContext: LogContext): Option[JavaFPCFAnalysisScheduler] = { val mirror = runtimeMirror(getClass.getClassLoader) try { val module = mirror.staticModule(className) import mirror.reflectModule - Some(reflectModule(module).instance.asInstanceOf[FPCFAnalysisScheduler]) + Some(reflectModule(module).instance.asInstanceOf[JavaFPCFAnalysisScheduler]) } catch { case sre: ScalaReflectionException => error("call graph", s"cannot find analysis scheduler $className", sre) @@ -138,7 +133,7 @@ trait CallGraphKey extends ProjectInformationKey[CallGraph, Nothing] { } } - private[this] def requiresCallBySignatureKey(p: SomeProject): ProjectInformationKeys = { + private[this] def requiresCallBySignatureKey(p: SomeProject): JavaProjectInformationKeys = { val config = p.config if (config.hasPath(CallBySignatureConfigKey) && config.getBoolean(CallBySignatureConfigKey)) { @@ -150,9 +145,9 @@ trait CallGraphKey extends ProjectInformationKey[CallGraph, Nothing] { def getTypeIterator(project: SomeProject): TypeIterator } -object CallGraphKey extends ProjectInformationKey[CallGraph, CallGraph] { +object CallGraphKey extends JavaProjectInformationKey[CallGraph, CallGraph] { - override def requirements(project: SomeProject): ProjectInformationKeys = Seq(TypeIteratorKey) + override def requirements(project: SomeProject): JavaProjectInformationKeys = Seq(TypeIteratorKey) override def compute(project: SomeProject): CallGraph = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/cg/PropagationBasedCallGraphKeys.scala b/OPAL/tac/src/main/scala/org/opalj/tac/cg/PropagationBasedCallGraphKeys.scala index 6476efbbbe..766a80a4ec 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/cg/PropagationBasedCallGraphKeys.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/cg/PropagationBasedCallGraphKeys.scala @@ -3,11 +3,11 @@ package org.opalj package tac package cg -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.SimpleContextsKey +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler import org.opalj.tac.fpcf.analyses.cg.rta.ConfiguredNativeMethodsInstantiatedTypesAnalysisScheduler import org.opalj.tac.fpcf.analyses.cg.xta.ArrayInstantiationsAnalysisScheduler import org.opalj.tac.fpcf.analyses.cg.xta.CTASetEntitySelector @@ -21,7 +21,7 @@ import org.opalj.tac.fpcf.analyses.cg.xta.XTASetEntitySelector import org.opalj.tac.fpcf.analyses.cg.PropagationBasedTypeIterator /** - * A [[org.opalj.br.analyses.ProjectInformationKey]] to compute a [[CallGraph]] based on Tip and + * A [[JavaProjectInformationKey]] to compute a [[CallGraph]] based on Tip and * Palsberg's propagation-based algorithms. * * @see [[CallGraphKey]] for further details. @@ -40,7 +40,7 @@ import org.opalj.tac.fpcf.analyses.cg.PropagationBasedTypeIterator */ trait PropagationBasedCallGraphKey extends CallGraphKey { - override def requirements(project: SomeProject): ProjectInformationKeys = { + override def requirements(project: SomeProject): JavaProjectInformationKeys = { super.requirements(project) ++ Seq(InitialInstantiatedTypesKey, SimpleContextsKey) } @@ -48,7 +48,7 @@ trait PropagationBasedCallGraphKey extends CallGraphKey { override protected def callGraphSchedulers( project: SomeProject - ): Iterable[FPCFAnalysisScheduler] = { + ): Iterable[JavaFPCFAnalysisScheduler] = { val theTypeSetEntitySelector = typeSetEntitySelector() val isLibrary = diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/cg/RTACallGraphKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/cg/RTACallGraphKey.scala index 33b175ed74..05f599dd15 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/cg/RTACallGraphKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/cg/RTACallGraphKey.scala @@ -3,18 +3,18 @@ package org.opalj package tac package cg -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.SimpleContextsKey +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler import org.opalj.tac.fpcf.analyses.cg.rta.ConfiguredNativeMethodsInstantiatedTypesAnalysisScheduler import org.opalj.tac.fpcf.analyses.cg.rta.InstantiatedTypesAnalysisScheduler import org.opalj.tac.fpcf.analyses.cg.xta.LibraryInstantiatedTypesBasedEntryPointsAnalysis import org.opalj.tac.fpcf.analyses.cg.RTATypeIterator /** - * A [[org.opalj.br.analyses.ProjectInformationKey]] to compute a [[CallGraph]] based on rapid type + * A [[JavaProjectInformationKey]] to compute a [[CallGraph]] based on rapid type * analysis (RTA). * * @see [[CallGraphKey]] for further details. @@ -33,13 +33,13 @@ import org.opalj.tac.fpcf.analyses.cg.RTATypeIterator */ object RTACallGraphKey extends CallGraphKey { - override def requirements(project: SomeProject): ProjectInformationKeys = { + override def requirements(project: SomeProject): JavaProjectInformationKeys = { Seq(InitialInstantiatedTypesKey, SimpleContextsKey) ++: super.requirements(project) } override def callGraphSchedulers( project: SomeProject - ): Iterable[FPCFAnalysisScheduler] = { + ): Iterable[JavaFPCFAnalysisScheduler] = { // in case the library entrypoints finder is configured, we want to use the // EagerLibraryEntryPointsAnalysis val isLibrary = diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/cg/TypeBasedPointsToCallGraphKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/cg/TypeBasedPointsToCallGraphKey.scala index 5af7d1b846..4ca4447a29 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/cg/TypeBasedPointsToCallGraphKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/cg/TypeBasedPointsToCallGraphKey.scala @@ -3,11 +3,11 @@ package org.opalj package tac package cg -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.VirtualFormalParametersKey -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.SimpleContextsKey +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.analyses.cg.SimpleContextProvider import org.opalj.tac.fpcf.analyses.cg.TypeIterator @@ -21,7 +21,7 @@ import org.opalj.tac.fpcf.analyses.pointsto.TypeBasedTamiFlexPointsToAnalysisSch import org.opalj.tac.fpcf.analyses.pointsto.TypeBasedUnsafePointsToAnalysisScheduler /** - * A [[org.opalj.br.analyses.ProjectInformationKey]] to compute a [[CallGraph]] based on + * A [[JavaProjectInformationKey]] to compute a [[CallGraph]] based on * the points-to analysis. * * @see [[CallGraphKey]] for further details. @@ -30,14 +30,14 @@ import org.opalj.tac.fpcf.analyses.pointsto.TypeBasedUnsafePointsToAnalysisSched */ object TypeBasedPointsToCallGraphKey extends CallGraphKey { - override def requirements(project: SomeProject): ProjectInformationKeys = { + override def requirements(project: SomeProject): JavaProjectInformationKeys = { Seq(DefinitionSitesKey, VirtualFormalParametersKey, SimpleContextsKey) ++: super.requirements(project) } override protected[cg] def callGraphSchedulers( project: SomeProject - ): Iterable[FPCFAnalysisScheduler] = { + ): Iterable[JavaFPCFAnalysisScheduler] = { val isLibrary = project.config.getString("org.opalj.br.analyses.cg.InitialEntryPointsKey.analysis") == "org.opalj.br.analyses.cg.LibraryEntryPointsFinder" diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/cg/TypeIteratorKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/cg/TypeIteratorKey.scala index 3e0ff723ce..4d8a3cc508 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/cg/TypeIteratorKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/cg/TypeIteratorKey.scala @@ -5,21 +5,21 @@ package cg import org.opalj.log.LogContext import org.opalj.log.OPALLogger -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.JavaProjectInformationKey import org.opalj.tac.fpcf.analyses.cg.CHATypeIterator import org.opalj.tac.fpcf.analyses.cg.TypeIterator /** - * An [[org.opalj.br.analyses.ProjectInformationKey]] to get the [[TypeIterator]] used to compute + * An [[JavaProjectInformationKey]] to get the [[TypeIterator]] used to compute * the current project's call graph. * This key is intended to be set up by a corresponding [[org.opalj.tac.cg.CallGraphKey]]. */ object TypeIteratorKey - extends ProjectInformationKey[TypeIterator, () => TypeIterator] { + extends JavaProjectInformationKey[TypeIterator, () => TypeIterator] { - override def requirements(project: SomeProject): ProjectInformationKeys = Nil + override def requirements(project: SomeProject): JavaProjectInformationKeys = Nil override def compute(project: SomeProject): TypeIterator = { project.getProjectInformationKeyInitializationData(this) match { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/common/DefinitionSitesKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/common/DefinitionSitesKey.scala index d434b21e35..edab88a8d2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/common/DefinitionSitesKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/common/DefinitionSitesKey.scala @@ -3,20 +3,20 @@ package org.opalj package tac package common -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.JavaProjectInformationKey /** - * The [[org.opalj.br.analyses.ProjectInformationKey]] to retrieve the + * The [[JavaProjectInformationKey]] to retrieve the * [[DefinitionSites]] object for a project. * * @author Dominik Helm * @author Florian Kübler */ -object DefinitionSitesKey extends ProjectInformationKey[DefinitionSites, Nothing] { +object DefinitionSitesKey extends JavaProjectInformationKey[DefinitionSites, Nothing] { - override def requirements(project: SomeProject): ProjectInformationKeys = Seq.empty + override def requirements(project: SomeProject): JavaProjectInformationKeys = Seq.empty override def compute(project: SomeProject): DefinitionSites = new DefinitionSites(project) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/APIBasedAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/APIBasedAnalysis.scala index b03b583815..c6a78a6caa 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/APIBasedAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/APIBasedAnalysis.scala @@ -13,7 +13,7 @@ import org.opalj.fpcf.SomeEOptionP import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.analyses.cg.ContextualAnalysis import org.opalj.tac.fpcf.analyses.cg.TypeIterator @@ -33,7 +33,7 @@ import org.opalj.tac.fpcf.analyses.cg.TypeIterator * * @author Florian Kuebler */ -trait APIBasedAnalysis extends FPCFAnalysis with ContextualAnalysis { +trait APIBasedAnalysis extends ProjectBasedAnalysis with ContextualAnalysis { val apiMethod: DeclaredMethod implicit val typeIterator: TypeIterator diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/AbstractIFDSAnalysis.scala deleted file mode 100644 index fc4177e87a..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/AbstractIFDSAnalysis.scala +++ /dev/null @@ -1,942 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package tac -package fpcf -package analyses - -import scala.annotation.tailrec - -import scala.collection.{Set => SomeSet} -import scala.collection.mutable - -import org.opalj.fpcf.EOptionP -import org.opalj.fpcf.EPK -import org.opalj.fpcf.FinalE -import org.opalj.fpcf.FinalEP -import org.opalj.fpcf.FinalP -import org.opalj.fpcf.InterimEUBP -import org.opalj.fpcf.InterimResult -import org.opalj.fpcf.ProperPropertyComputationResult -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.Result -import org.opalj.fpcf.SomeEOptionP -import org.opalj.fpcf.SomeEPS -import org.opalj.value.ValueInformation -import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler -import org.opalj.br.Method -import org.opalj.br.ObjectType -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.SomeProject -import org.opalj.br.cfg.BasicBlock -import org.opalj.br.cfg.CFG -import org.opalj.br.cfg.CFGNode -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.DeclaredMethod -import org.opalj.br.DefinedMethod -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.fpcf.properties.Context -import org.opalj.tac.cg.TypeIteratorKey -import org.opalj.tac.fpcf.properties.cg.Callees -import org.opalj.tac.fpcf.analyses.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.analyses.cg.TypeIterator -import org.opalj.tac.fpcf.properties.IFDSProperty -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation -import org.opalj.tac.fpcf.properties.TACAI -import org.opalj.tac.fpcf.properties.TheTACAI - -/** - * The super type of all IFDS facts. - */ -trait AbstractIFDSFact - -/** - * The super type of all null facts. - */ -trait AbstractIFDSNullFact extends AbstractIFDSFact - -/** - * A framework for IFDS analyses. - * - * @tparam IFDSFact The type of flow facts the concrete analysis wants to track - * @author Dominik Helm - * @author Mario Trageser - */ -abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAnalysis { - - /** - * Provides the concrete property key that must be unique for every distinct concrete analysis - * and the lower bound for the IFDSProperty. - */ - val propertyKey: IFDSPropertyMetaInformation[IFDSFact] - - /** - * Creates an IFDSProperty containing the result of this analysis. - * - * @param result Maps each exit statement to the facts valid after the exit statement. - * @return An IFDSProperty containing the `result`. - */ - def createPropertyValue(result: Map[Statement, Set[IFDSFact]]): IFDSProperty[IFDSFact] - - /** - * Computes the DataFlowFacts valid after statement `statement` on the CFG edge to statement `succ` - * if the DataFlowFacts `in` held before `statement`. - */ - - /** - * Computes the data flow for a normal statement. - * - * @param statement The analyzed statement. - * @param successor The successor of the analyzed `statement`, to which the data flow is considered. - * @param in Some facts valid before the execution of the `statement`. - * @return The facts valid after the execution of `statement` - * under the assumption that `in` held before `statement` and `successor` will be executed next. - */ - def normalFlow(statement: Statement, successor: Statement, in: Set[IFDSFact]): Set[IFDSFact] - - /** - * Computes the data flow for a call to start edge. - * - * @param call The analyzed call statement. - * @param calleeContext The called method. - * @param in Some facts valid before the execution of the `call`. - * @return The facts valid after the execution of `statement` under the assumption that `in` held before `statement` and `statement` calls `callee`. - */ - def callFlow( - call: Statement, calleeContext: Context, in: Set[IFDSFact] - ): Set[IFDSFact] - - /** - * Computes the data flow for a exit to return edge. - * - * @param call The statement, which called the `callee`. - * @param calleeContext The method called by `call`. - * @param exit The statement, which terminated the `calle`. - * @param successor The statement of the caller, which will be executed after the `callee` returned. - * @param in Some facts valid before the execution of the `exit`. - * @return The facts valid after the execution of `exit` in the caller's context - * under the assumption that `in` held before the execution of `exit` and that `successor` will be executed next. - */ - def returnFlow( - call: Statement, - calleeContext: Context, - exit: Statement, - successor: Statement, - in: Set[IFDSFact] - ): Set[IFDSFact] - - /** - * Computes the data flow for a call to return edge. - * - * @param call The statement, which invoked the call. - * @param successor The statement, which will be executed after the call. - * @param in Some facts valid before the `call`. - * @return The facts valid after the call independently of what happens in the callee under the assumption that `in` held before `call`. - */ - def callToReturnFlow( - call: Statement, successor: Statement, in: Set[IFDSFact] - ): Set[IFDSFact] - - /** - * Computes the data flow for a summary edge of a native method call. - * - * @param call The statement, which invoked the call. - * @param calleeContext The method, called by `call`. - * @param successor The statement, which will be executed after the call. - * @param in Some facts valid before the `call`. - * @return The facts valid after the call, excluding the call-to-return flow. - */ - def nativeCall(call: Statement, calleeContext: Context, successor: Statement, in: Set[IFDSFact]): Set[IFDSFact] - - /** - * All declared methods in the project. - */ - final protected[this] implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - - /** - * The entry points of this analysis. - */ - val entryPoints: Map[Context, IFDSFact] - - /** - * The state of the analysis. For each method and source fact, there is a separate state. - * - * @param declaringClass The class defining the analyzed `method`. - * @param method The analyzed method. - * @param source A fact, that holds at the beginning of `method`. - * @param code The code of `method`. - * @param cfg The control glow graph of `method`. - * @param pendingIfdsCallSites Maps callees of the analyzed `method` together with their input facts - * to the basic block and statement index of the call site(s). - * @param pendingIfdsDependees Maps callees of the analyzed `method` together with their input facts to the intermediate result of their IFDS analysis. - * Only contains method-fact-pairs, for which this analysis is waiting for a result. - * @param pendingCgCallSites The basic blocks containing call sites, for which the analysis is still waiting for the call graph result. - * @param cgDependency If present, the analysis is waiting for the `method`'s call graph. - * @param incomingFacts Maps each basic block to the data flow facts valid at its first statement. - * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at the beginning of the node. - */ - class State( - val declaringClass: ObjectType, - val context: Context, - val source: (Context, IFDSFact), - val code: Array[Stmt[V]], - val cfg: CFG[Stmt[V], TACStmts[V]], - var pendingIfdsCallSites: Map[(Context, IFDSFact), Set[(BasicBlock, Int)]], - var pendingIfdsDependees: Map[(Context, IFDSFact), EOptionP[(Context, IFDSFact), IFDSProperty[IFDSFact]]] = Map.empty, - var pendingCgCallSites: Set[BasicBlock] = Set.empty, - var cgDependency: Option[SomeEOptionP] = None, - var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, - var outgoingFacts: Map[BasicBlock, Map[CFGNode, Set[IFDSFact]]] = Map.empty - ) - - implicit val typeIterator: TypeIterator = project.get(TypeIteratorKey) - - /** - * Performs an IFDS analysis for a method-fact-pair. - * - * @param entity The method-fact-pair that will be analyzed. - * @return An IFDS property mapping from exit statements to the data flow facts valid after these exit statements. - * Returns an interim result, if the TAC or call graph of this method or the IFDS analysis for a callee is still pending. - */ - def performAnalysis(entity: (Context, IFDSFact)): ProperPropertyComputationResult = { - val (context, sourceFact) = entity - - val declaredMethod = context.method - - // The analysis can only handle single defined methods - // If a method is not single defined, this analysis assumes that it does not create any facts. - if (!declaredMethod.hasSingleDefinedMethod) - return Result(entity, createPropertyValue(Map.empty)); - - val method = declaredMethod.definedMethod - val declaringClass: ObjectType = method.classFile.thisType - - // Fetch the method's three address code. If it is not present, return an empty interim result. - val (code, cfg) = propertyStore(method, TACAI.key) match { - case FinalP(TheTACAI(tac)) => (tac.stmts, tac.cfg) - - case epk: EPK[Method, TACAI] => - return InterimResult.forUB( - entity, - createPropertyValue(Map.empty), - Set(epk), - _ => performAnalysis(entity) - ); - - case tac => - throw new UnknownError(s"can't handle intermediate TACs ($tac)"); - } - - // Start processing at the start of the cfg with the given source fact - implicit val state: State = - new State(declaringClass, context, entity, code, cfg, Map(entity -> Set.empty)) - val start = cfg.startBlock - state.incomingFacts += start -> Set(sourceFact) - process(mutable.Queue((start, Set(sourceFact), None, None, None))) - createResult() - } - - /** - * Analyzes a queue of BasicBlocks. - * - * @param worklist A queue of the following elements: - * bb The basic block that will be analyzed. - * in New data flow facts found to hold at the beginning of the basic block. - * calleeWithUpdateIndex If the basic block is analyzed because there is new information for a callee, this is the call site's index. - * calleeWithUpdate If the basic block is analyzed because there is new information for a callee, this is the callee. - * calleeWithUpdateFact If the basic block is analyzed because there is new information for a callee with a specific input fact, - * this is the input fact. - */ - def process( - worklist: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Context], Option[IFDSFact])] - )( - implicit - state: State - ): Unit = { - while (worklist.nonEmpty) { - val (basicBlock, in, calleeWithUpdateIndex, calleeWithUpdate, calleeWithUpdateFact) = - worklist.dequeue() - val oldOut = state.outgoingFacts.getOrElse(basicBlock, Map.empty) - val nextOut = - analyzeBasicBlock(basicBlock, in, calleeWithUpdateIndex, calleeWithUpdate, calleeWithUpdateFact) - val allOut = mergeMaps(oldOut, nextOut) - state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) - - for (successor <- basicBlock.successors) { - if (successor.isExitNode) { - // Re-analyze recursive call sites with the same input fact. - val nextOutSuccessors = nextOut.get(successor) - if (nextOutSuccessors.isDefined && nextOutSuccessors.get.nonEmpty) { - val oldOutSuccessors = oldOut.get(successor) - if (oldOutSuccessors.isEmpty || - nextOutSuccessors.get.exists(nos => !oldOutSuccessors.get.contains(nos))) { - val source = state.source - reAnalyzeCalls(state.pendingIfdsCallSites(source), source._1, Some(source._2)) - } - } - // if ((nextOut.getOrElse(successor, Set.empty) -- oldOut.getOrElse(successor, Set.empty)).nonEmpty) { - // val source = state.source - // reAnalyzeCalls(state.pendingIfdsCallSites(source), source._1.definedMethod, Some(source._2)) - // } - } else { - val actualSuccessor = - (if (successor.isBasicBlock) { - successor - } else { - // skip CatchNodes jump to their handler BasicBlock - successor.successors.head - }).asBasicBlock - - val nextIn = nextOut.getOrElse(actualSuccessor, Set.empty) - val oldIn = state.incomingFacts.getOrElse(actualSuccessor, Set.empty) - val mergedIn = if (nextIn.size > oldIn.size) nextIn ++ oldIn else oldIn ++ nextIn - val newIn = nextIn -- oldIn - state.incomingFacts = state.incomingFacts.updated(actualSuccessor, mergedIn) - /* - * Only process the successor with new facts. - * It is analyzed at least one time because of the null fact. - */ - if (newIn.nonEmpty) { - worklist.enqueue((actualSuccessor, newIn, None, None, None)) - } - } - } - } - } - - /** - * Collects the facts valid at an exit node based on the current results. - * - * @param exit The exit node. - * @return A map, mapping from each predecessor of the `exit` node to the facts valid at the `exit` node - * under the assumption that the predecessor was executed before. - */ - def collectResult(exit: CFGNode)(implicit state: State): Map[Statement, Set[IFDSFact]] = { - var result = Map.empty[Statement, Set[IFDSFact]] - exit.predecessors foreach { predecessor => - if (predecessor.isBasicBlock) { - val basicBlock = predecessor.asBasicBlock - // FIXME ... replace flatMap...isDefined by something that doesn't create intermediate data-structures - if (state.outgoingFacts.get(basicBlock).flatMap(_.get(exit)).isDefined) { - val lastIndex = basicBlock.endPC - val stmt = Statement(state.context, basicBlock, state.code(lastIndex), lastIndex, state.code, state.cfg) - result += stmt -> state.outgoingFacts(basicBlock)(exit) - } - } - } - result - } - - /** - * Creates the current (intermediate) result for the analysis. - * - * @return A result containing a map, which maps each exit statement to the facts valid after the statement, based on the current results. - * If the analysis is still waiting for its method's TAC or call graph or the IFDS of another method, an interim result will be returned. - * - */ - def createResult()(implicit state: State): ProperPropertyComputationResult = { - val propertyValue = createPropertyValue(mergeMaps( - collectResult(state.cfg.normalReturnNode), - collectResult(state.cfg.abnormalReturnNode) - )) - - var dependees: Set[SomeEOptionP] = state.pendingIfdsDependees.valuesIterator.toSet - // In the follwing, we really want to avoid useless copying of dependees: - if (state.cgDependency.isDefined) { - if (dependees.isEmpty) { - dependees = Set(state.cgDependency.get) - } else { - // We only implement what is required by the propery store/interface - new Iterable[SomeEOptionP] { - override def iterator: Iterator[SomeEOptionP] = { - // This method is actually not called by the property store... - Iterator.single(state.cgDependency.get) ++ dependees.iterator - } - override def foreach[U](f: SomeEOptionP => U): Unit = { - f(state.cgDependency.get) - dependees.foreach(f) - } - override def size: Int = dependees.size + 1 - override def isEmpty = false - } - } - } - - if (dependees.isEmpty) { - Result(state.source, propertyValue) - } else { - InterimResult.forUB(state.source, propertyValue, dependees, propertyUpdate) - } - } - - /** - * Called, when the call graph for this method or an IFDSProperty for another method was computed. - * Re-analyzes the relevant parts of this method and returns the new analysis result. - * - * @param eps The new property value. - * @return The new (interim) result of this analysis. - */ - def propertyUpdate(eps: SomeEPS)( - implicit - state: State - ): ProperPropertyComputationResult = { - (eps: @unchecked) match { - case FinalE(e: (Context, IFDSFact) @unchecked) => reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - - case interimEUBP @ InterimEUBP(e: (Context, IFDSFact) @unchecked, ub: IFDSProperty[IFDSFact @unchecked]) => - if (ub.flows.values.forall(_.isInstanceOf[AbstractIFDSNullFact])) { - // Do not re-analyze the caller if we only get the null fact. - // Update the pendingIfdsDependee entry to the new interim result. - state.pendingIfdsDependees += e -> interimEUBP.asInstanceOf[EOptionP[(Context, IFDSFact), IFDSProperty[IFDSFact]]] - } else reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - - case FinalEP(_: DefinedMethod, _: Callees) => - reAnalyzebasicBlocks(state.pendingCgCallSites) - - case InterimEUBP(_: DefinedMethod, _: Callees) => - reAnalyzebasicBlocks(state.pendingCgCallSites) - } - - createResult() - } - - /** - * Computes for one basic block the facts valid on each CFG edge leaving the block if `sources` hold before the block. - * - * @param basicBlock The basic block, that will be analyzed. - * @param in The facts, that hold before the block. - * @param calleeWithUpdateIndex If the basic block is analyzed because there is new information for a callee, this is the call site's index. - * @param calleeWithUpdate If the basic block is analyzed because there is new information for a callee, this is the callee. - * @param calleeWithUpdateFact If the basic block is analyzed because there is new information for a callee with a specific input fact, - * this is the input fact. - * @return A map, mapping each successor node to its input facts. Instead of catch nodes, this map contains their handler nodes. - */ - def analyzeBasicBlock( - basicBlock: BasicBlock, - in: Set[IFDSFact], - calleeWithUpdateIndex: Option[Int], - calleeWithUpdate: Option[Context], - calleeWithUpdateFact: Option[IFDSFact] - )( - implicit - state: State - ): Map[CFGNode, Set[IFDSFact]] = { - - /* - * Collects information about a statement. - * - * @param index The statement's index. - * @return A tuple of the following elements: - * statement: The statement at `index`. - * calees: The methods possibly called at this statement, if it contains a call. - * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will be returned. - * calleeFact: If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdateFact` will be returned, None otherwise. - */ - def collectInformation( - index: Int - ): (Statement, Option[SomeSet[Context]], Option[IFDSFact]) = { - val stmt = state.code(index) - val statement = Statement(state.context, basicBlock, stmt, index, state.code, state.cfg) - val calleesO = - if (calleeWithUpdateIndex.contains(index)) calleeWithUpdate.map(Set(_)) else getCalleesIfCallStatement(basicBlock, index) - val calleeFact = if (calleeWithUpdateIndex.contains(index)) calleeWithUpdateFact else None - (statement, calleesO, calleeFact) - } - - var flows: Set[IFDSFact] = in - var index = basicBlock.startPC - - // Iterate over all statements but the last one, only keeping the resulting DataFlowFacts. - while (index < basicBlock.endPC) { - val (statement, calleesO, calleeFact) = collectInformation(index) - flows = if (calleesO.isEmpty) { - val successor = - Statement(state.context, basicBlock, state.code(index + 1), index + 1, state.code, state.cfg) - normalFlow(statement, successor, flows) - } else - // Inside a basic block, we only have one successor --> Take the head - handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head - index += 1 - } - - // Analyze the last statement for each possible successor statement. - val (statement, calleesO, callFact) = collectInformation(basicBlock.endPC) - var result: Map[CFGNode, Set[IFDSFact]] = - if (calleesO.isEmpty) { - var result: Map[CFGNode, Set[IFDSFact]] = Map.empty - for (node <- basicBlock.successors) { - result += node -> normalFlow(statement, firstStatement(node), flows) - } - result - } else { - handleCall(basicBlock, statement, calleesO.get, flows, callFact).map(entry => entry._1.node -> entry._2) - } - - // Propagate the null fact. - result = result.map(result => result._1 -> (propagateNullFact(in, result._2))) - result - } - - /** - * Retrieves the expression of an assignment or expression statement. - * - * @param statement The statement. Must be an Assignment or ExprStmt. - * @return The statement's expression. - */ - def getExpression(statement: Stmt[V]): Expr[V] = statement.astID match { - case Assignment.ASTID => statement.asAssignment.expr - case ExprStmt.ASTID => statement.asExprStmt.expr - case _ => throw new UnknownError("Unexpected statement") - } - - /** - * Gets the set of all methods possibly called at some statement. - * - * @param basicBlock The basic block containing the statement. - * @param index The statement's index. - * @return All methods possibly called at the statement index or None, if the statement does not contain a call. - */ - def getCalleesIfCallStatement(basicBlock: BasicBlock, index: Int)(implicit state: State): Option[SomeSet[Context]] = { - val statement = state.code(index) - val pc = statement.pc - statement.astID match { - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID => Some(getCallees(basicBlock, pc)) - case Assignment.ASTID | ExprStmt.ASTID => getExpression(statement).astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => Some(getCallees(basicBlock, pc)) - case _ => None - } - case _ => None - } - } - - /** - * Gets the set of all methods possibly called at some call statement. - * - * @param basicBlock The basic block containing the call. - * @param pc The call's program counter. - * @return All methods possibly called at the statement index. - */ - def getCallees( - basicBlock: BasicBlock, pc: Int - )(implicit state: State): SomeSet[Context] = { - val ep = propertyStore(state.context.method, Callees.key) - ep match { - case FinalEP(_, p) => - state.cgDependency = None - state.pendingCgCallSites -= basicBlock - p.callees(state.source._1, pc).toSet - case InterimEUBP(_, p) => - addCgDependency(basicBlock, ep) - p.callees(state.source._1, pc).toSet - case _ => - addCgDependency(basicBlock, ep) - Set.empty - } - } - - /** - * Maps some declared methods to their defined methods. - * - * @param declaredMethods Some declared methods. - * @return All defined methods of `declaredMethods`. - */ - def definedMethods(declaredMethods: Iterator[Context]): SomeSet[Method] = { - val result = scala.collection.mutable.Set.empty[Method] - declaredMethods.map(_.method).filter { declaredMethod => - declaredMethod.hasSingleDefinedMethod || declaredMethod.hasMultipleDefinedMethods - }.foreach { declaredMethod => - declaredMethod.foreachDefinedMethod(defineMethod => result.add(defineMethod)) - } - result - } - - /** - * Sets the cgDependency to `ep` and adds the `basicBlock` to the pending cg call sites. - * - * @param basicBlock The basic block, which will be added to the pending cg call sites. - * @param ep The result of the call graph analysis. - */ - def addCgDependency(basicBlock: BasicBlock, ep: EOptionP[DeclaredMethod, Callees])(implicit state: State): Unit = { - state.cgDependency = Some(ep) - state.pendingCgCallSites += basicBlock - } - - /** - * Re-analyzes some basic blocks. - * - * @param basicBlocks The basic blocks, that will be re-analyzed. - */ - def reAnalyzebasicBlocks(basicBlocks: Set[BasicBlock])(implicit state: State): Unit = { - val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Context], Option[IFDSFact])] = mutable.Queue.empty - for (bb <- basicBlocks) - queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) - process(queue) - } - - /** - * Re-analyzes some call sites with respect to one specific callee. - * - * @param callSites The call sites, which are analyzed. - * @param callee The callee, which will be considered at the `callSites`. - * @param fact If defined, the `callee` will only be analyzed for this fact. - */ - def reAnalyzeCalls( - callSites: Set[(BasicBlock, Int)], - calleeContext: Context, - fact: Option[IFDSFact] - )(implicit state: State): Unit = { - val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Context], Option[IFDSFact])] = - mutable.Queue.empty - for ((block, index) <- callSites) - queue.enqueue( - ( - block, - state.incomingFacts(block), - Some(index), - Some(calleeContext), - fact - ) - ) - process(queue) - } - - /** - * Processes a statement with a call. - * - * @param basicBlock The basic block that contains the statement - * @param call The call statement. - * @param callees All possible callees of the call. - * @param in The facts valid before the call statement. - * @param calleeWithUpdateFact If present, the `callees` will only be analyzed with this fact instead of the facts returned by callFlow. - * @return A map, mapping from each successor statement of the `call` to the facts valid at their start. - */ - def handleCall( - basicBlock: BasicBlock, - call: Statement, - callees: SomeSet[Context], - in: Set[IFDSFact], - calleeWithUpdateFact: Option[IFDSFact] - )( - implicit - state: State - ): Map[Statement, Set[IFDSFact]] = { - val successors = successorStatements(call, basicBlock) - // Facts valid at the start of each successor - var summaryEdges: Map[Statement, Set[IFDSFact]] = Map.empty - - // If calleeWithUpdateFact is present, this means that the basic block already has been analyzed with the `in` facts. - if (calleeWithUpdateFact.isEmpty) - for (successor <- successors) { - summaryEdges += successor -> propagateNullFact(in, callToReturnFlow(call, successor, in)) - } - - for (callee <- callees) { - if (callee.method.definedMethod.isNative) { - // We cannot analyze native methods. Let the concrete analysis decide what to do. - for { - successor <- successors - } { - summaryEdges += successor -> (summaryEdges(successor) ++ nativeCall(call, callee, successor, in)) - } - } else { - val callToStart = - if (calleeWithUpdateFact.isDefined) calleeWithUpdateFact.toSet - else propagateNullFact(in, callFlow(call, callee, in)) - var allNewExitFacts: Map[Statement, Set[IFDSFact]] = Map.empty - // Collect exit facts for each input fact separately - for (fact <- callToStart) { - /* - * If this is a recursive call with the same input facts, we assume that the call only produces the facts that are already known. - * The call site is added to `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts become known for the input fact. - */ - if ((callee eq state.context) && fact == state.source._2) { - // FIXME Get rid of "getOrElse(...,Set.empty)" due to its potentially very BAD performance - val newDependee = - state.pendingIfdsCallSites.getOrElse(state.source, Set.empty) + ((basicBlock, call.index)) - state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(state.source, newDependee) - allNewExitFacts = mergeMaps( - allNewExitFacts, - mergeMaps( - collectResult(state.cfg.normalReturnNode), - collectResult(state.cfg.abnormalReturnNode) - ) - ) - } else { - val e = (callee, fact) - val callFlows = propertyStore(e, propertyKey.key) - .asInstanceOf[EOptionP[(Context, IFDSFact), IFDSProperty[IFDSFact]]] - val oldValue = state.pendingIfdsDependees.get(e) - val oldExitFacts: Map[Statement, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[IFDSFact]]) => ep.ub.flows - case _ => Map.empty - } - val exitFacts: Map[Statement, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[IFDSFact]] => - // FIXME Get rid of "getOrElse(...,Set.empty)" due to its potentially very BAD performance - val newDependee = - state.pendingIfdsCallSites.getOrElse(e, Set.empty) - ((basicBlock, call.index)) - state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) - state.pendingIfdsDependees -= e - ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[IFDSFact]] => - /* - * Add the call site to `pendingIfdsCallSites` and `pendingIfdsDependees` and - * continue with the facts in the interim result for now. When the analysis for the - * callee finishes, the analysis for this call site will be triggered again. - */ - addIfdsDependee(e, callFlows, basicBlock, call.index) - ep.ub.flows - case _ => - addIfdsDependee(e, callFlows, basicBlock, call.index) - Map.empty - } - // Only process new facts that are not in `oldExitFacts` - allNewExitFacts = mergeMaps(allNewExitFacts, mapDifference(exitFacts, oldExitFacts)) - /* - * If new exit facts were discovered for the callee-fact-pair, all call sites depending on this pair have to be re-evaluated. - * oldValue is undefined if the callee-fact pair has not been queried before or returned a FinalEP. - */ - if (oldValue.isDefined && oldExitFacts != exitFacts) { - reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - } - } - } - - //Create exit to return facts. At first for normal returns, then for abnormal returns. - for { - successor <- successors - if successor.node.isBasicBlock || successor.node.isNormalReturnExitNode - exitStatement <- allNewExitFacts.keys - if exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID - } { - // FIXME Get rid of "getOrElse(...,Set.empty)" due to its potentially very BAD performance - summaryEdges += successor -> (summaryEdges.getOrElse(successor, Set.empty[IFDSFact]) ++ - returnFlow(call, callee, exitStatement, successor, allNewExitFacts.getOrElse(exitStatement, Set.empty))) - } - for { - successor <- successors - if successor.node.isCatchNode || successor.node.isAbnormalReturnExitNode - exitStatement <- allNewExitFacts.keys - if exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID - } { - // FIXME Get rid of "getOrElse(...,Set.empty)" due to its potentially very BAD performance - summaryEdges += successor -> (summaryEdges.getOrElse(successor, Set.empty[IFDSFact]) ++ - returnFlow(call, callee, exitStatement, successor, allNewExitFacts.getOrElse(exitStatement, Set.empty))) - } - } - } - summaryEdges - } - - /** - * Determines the successor statements for one source statement. - * - * @param statement The source statement. - * @param basicBlock The basic block containing the source statement. - * @return All successors of `statement`. - */ - def successorStatements( - statement: Statement, - basicBlock: BasicBlock - )( - implicit - state: State - ): Set[Statement] = { - val index = statement.index - if (index == basicBlock.endPC) { - for (successorBlock <- basicBlock.successors) yield firstStatement(successorBlock) - } else { - val nextIndex = index + 1 - Set(Statement(statement.context, basicBlock, statement.code(nextIndex), nextIndex, statement.code, statement.cfg)) - } - } - - /** - * Adds a method-fact-pair as to the IFDS call sites and dependees. - * - * @param entity The method-fact-pair. - * @param calleeProperty The property, that was returned for `entity`. - * @param callBB The basic block of the call site. - * @param callIndex The index of the call site. - */ - def addIfdsDependee( - entity: (Context, IFDSFact), - calleeProperty: EOptionP[(Context, IFDSFact), IFDSProperty[IFDSFact]], - callBB: BasicBlock, - callIndex: Int - )( - implicit - state: State - ): Unit = { - val callSites = state.pendingIfdsCallSites - state.pendingIfdsCallSites = callSites.updated(entity, callSites.getOrElse(entity, Set.empty) + ((callBB, callIndex))) - state.pendingIfdsDependees += entity -> calleeProperty - } - - /** - * If `from` contains a null fact, it will be added to `to`. - * - * @param from The set, which may contain the null fact initially. - * @param to The set, to which the null fact may be added. - * @return `to` with the null fact added, if it is contained in `from`. - */ - def propagateNullFact(from: Set[IFDSFact], to: Set[IFDSFact]): Set[IFDSFact] = { - val nullFact = from.find(_.isInstanceOf[AbstractIFDSNullFact]) - if (nullFact.isDefined) to + nullFact.get - else to - } - - /** - * Merges two maps that have sets as values. - * - * @param map1 The first map. - * @param map2 The second map. - * @return A map containing the keys of both maps. Each key is mapped to the union of both maps' values. - */ - def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { - var result = map1 - for ((key, values) <- map2) { - result.get(key) match { - case Some(resultValues) => - if (resultValues.size > values.size) - result = result.updated(key, resultValues ++ values) - else - result = result.updated(key, values ++ resultValues) - case None => - result = result.updated(key, values) - } - } - result - } - - /** - * Computes the difference of two maps that have sets as their values. - * - * @param minuend The map, from which elements will be removed. - * @param subtrahend The map, whose elements will be removed from `minuend`. - * @return A map, containing the keys and values of `minuend`. - * The values of the result only contain those elements not present in `subtrahend` for the same key. - */ - def mapDifference[S, T](minuend: Map[S, Set[T]], subtrahend: Map[S, Set[T]]): Map[S, Set[T]] = { - var result = minuend - for ((key, values) <- subtrahend) { - result = result.updated(key, result(key) -- values) - } - result - } - - /** - * Gets the call for a statement that contains a call. - * - * @param statement The statement. - * @return The call contained in `statement`. - */ - protected[this] def asCall(statement: Stmt[V]): Call[V] = statement.astID match { - case Assignment.ASTID => statement.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID => statement.asExprStmt.expr.asFunctionCall - case _ => statement.asMethodCall - } - - /** - * Gets the first statement of a cfg node. - * - * @param node The node, for which the first statement will be retrieved. - * @return If the `node` is a basic block, its first statement will be returned. - * If it is a catch node, the first statement of its handler will be returned. - * If it is an exit node an artificial statement without code will be returned. - */ - @tailrec - private def firstStatement(node: CFGNode)(implicit state: State): Statement = { - if (node.isBasicBlock) { - val index = node.asBasicBlock.startPC - Statement(state.context, node, state.code(index), index, state.code, state.cfg) - } else if (node.isCatchNode) { - firstStatement(node.successors.head) - } else if (node.isExitNode) { - Statement(state.context, node, null, 0, state.code, state.cfg) - } else throw new IllegalArgumentException(s"Unknown node type: $node") - } -} - -/** - * A statement that is passed to the concrete analysis. - * - * @param context The method containing the statement. - * @param node The basic block containing the statement. - * @param stmt The TAC statement. - * @param index The index of the Statement in the code. - * @param code The method's TAC code. - * @param cfg The method's CFG. - */ -case class Statement( - context: Context, - node: CFGNode, - stmt: Stmt[V], - index: Int, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] -) { - - override def hashCode(): Int = context.hashCode() * 31 + index - - override def equals(o: Any): Boolean = { - o match { - case s: Statement => s.index == index && s.context == context - case _ => false - } - } - - override def toString: String = s"${context.method.definedMethod.toJava}" - -} - -object AbstractIFDSAnalysis { - - /** - * The type of the TAC domain. - */ - type V = DUVar[ValueInformation] -} - -abstract class IFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalysisScheduler { - - final override type InitializationData = AbstractIFDSAnalysis[IFDSFact] - - def property: IFDSPropertyMetaInformation[IFDSFact] - - final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) - - override def requiredProjectInformation: ProjectInformationKeys = - Seq(DeclaredMethodsKey, TypeIteratorKey) - - override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI)) - - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} - - /** - * Registers the analysis as a lazy computation, that is, the method - * will call `ProperytStore.scheduleLazyComputation`. - */ - final override def register( - p: SomeProject, - ps: PropertyStore, - analysis: AbstractIFDSAnalysis[IFDSFact] - ): FPCFAnalysis = { - ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) - analysis - } - - override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { - val ifdsAnalysis = analysis.asInstanceOf[AbstractIFDSAnalysis[IFDSFact]] - for (e <- ifdsAnalysis.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } - } - - override def afterPhaseCompletion( - p: SomeProject, - ps: PropertyStore, - analysis: FPCFAnalysis - ): Unit = {} - -} - diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/FieldLocalityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/FieldLocalityAnalysis.scala index 40df8488e9..3f1d8cb0f6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/FieldLocalityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/FieldLocalityAnalysis.scala @@ -5,7 +5,6 @@ package fpcf package analyses import java.util.concurrent.ConcurrentHashMap - import org.opalj.collection.immutable.IntTrieSet import org.opalj.fpcf.EOptionP import org.opalj.fpcf.FinalP @@ -33,19 +32,11 @@ import org.opalj.br.fpcf.properties.NoFreshReturnValue import org.opalj.br.fpcf.properties.NoLocalField import org.opalj.br.fpcf.properties.PrimitiveReturnValue import org.opalj.br.fpcf.properties.ReturnValueFreshness -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, FieldAccessInformationKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.br.DeclaredMethod import org.opalj.br.Field import org.opalj.br.Method import org.opalj.br.ObjectType -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.FieldAccessInformationKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.ClosedPackagesKey import org.opalj.br.analyses.cg.TypeExtensibilityKey import org.opalj.br.cfg.BasicBlock @@ -55,6 +46,7 @@ import org.opalj.br.fpcf.properties.Context import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.ai.PCs import org.opalj.ai.ValueOrigin +import org.opalj.br.fpcf.{JavaBasicFPCFEagerAnalysisScheduler, JavaBasicFPCFLazyAnalysisScheduler, JavaFPCFAnalysisScheduler} import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSiteLike import org.opalj.tac.common.DefinitionSitesKey @@ -72,7 +64,7 @@ import org.opalj.tac.fpcf.properties.cg.Callers */ class FieldLocalityAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { type V = DUVar[ValueInformation] @@ -694,9 +686,9 @@ class FieldLocalityAnalysis private[analyses] ( } } -sealed trait FieldLocalityAnalysisScheduler extends FPCFAnalysisScheduler { +sealed trait FieldLocalityAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq( + override def requiredProjectInformation: JavaProjectInformationKeys = Seq( FieldAccessInformationKey, DeclaredMethodsKey, DefinitionSitesKey, @@ -720,9 +712,9 @@ sealed trait FieldLocalityAnalysisScheduler extends FPCFAnalysisScheduler { object EagerFieldLocalityAnalysis extends FieldLocalityAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val allFields = p.allFields val analysis = new FieldLocalityAnalysis(p) ps.scheduleEagerComputationsForEntities(allFields)(analysis.step1) @@ -736,9 +728,9 @@ object EagerFieldLocalityAnalysis object LazyFieldLocalityAnalysis extends FieldLocalityAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { - final override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + final override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new FieldLocalityAnalysis(p) ps.registerLazyPropertyComputation( FieldLocality.key, analysis.step1 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0TACAIAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0TACAIAnalysis.scala index 1ccc107c78..73bd67a2f5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0TACAIAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L0TACAIAnalysis.scala @@ -15,17 +15,17 @@ import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.ai.fpcf.analyses.L0BaseAIResultAnalysis import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.ai.fpcf.properties.AnAIResult import org.opalj.ai.fpcf.properties.BaseAIResult import org.opalj.ai.fpcf.properties.NoAIResult import org.opalj.ai.fpcf.properties.ProjectSpecificAIExecutor +import org.opalj.br.fpcf.{JavaFPCFEagerAnalysisScheduler, JavaFPCFLazyAnalysisScheduler} +import org.opalj.si.FPCFAnalysis import org.opalj.tac.fpcf.properties.NoTACAI import org.opalj.tac.fpcf.properties.TACAI @@ -35,7 +35,7 @@ import org.opalj.tac.fpcf.properties.TACAI * * @author Michael Eichberg */ -class L0TACAIAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { +class L0TACAIAnalysis private[analyses] (val project: SomeProject) extends ProjectBasedAnalysis { import org.opalj.tac.fpcf.analyses.TACAIAnalysis.computeTheTACAI @@ -95,7 +95,7 @@ class L0TACAIAnalysis private[analyses] (val project: SomeProject) extends FPCFA sealed trait L0TACAIAnalysisScheduler extends TACAIInitializer { - override def requiredProjectInformation: ProjectInformationKeys = Seq(AIDomainFactoryKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(AIDomainFactoryKey) final override def uses: Set[PropertyBounds] = Set(PropertyBounds.lub(BaseAIResult)) @@ -113,13 +113,13 @@ sealed trait L0TACAIAnalysisScheduler extends TACAIInitializer { } -object EagerL0TACAIAnalysis extends L0TACAIAnalysisScheduler with FPCFEagerAnalysisScheduler { +object EagerL0TACAIAnalysis extends L0TACAIAnalysisScheduler with JavaFPCFEagerAnalysisScheduler { override def derivesCollaboratively: Set[PropertyBounds] = Set.empty override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new L0TACAIAnalysis(p) val methods = p.allMethodsWithBody ps.scheduleEagerComputationsForEntities(methods)(analysis.computeTAC) @@ -127,11 +127,11 @@ object EagerL0TACAIAnalysis extends L0TACAIAnalysisScheduler with FPCFEagerAnaly } } -object LazyL0TACAIAnalysis extends L0TACAIAnalysisScheduler with FPCFLazyAnalysisScheduler { +object LazyL0TACAIAnalysis extends L0TACAIAnalysisScheduler with JavaFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new L0TACAIAnalysis(p) ps.registerLazyPropertyComputation(TACAI.key, analysis.computeTAC) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L1FieldMutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L1FieldMutabilityAnalysis.scala index 2617e4b4ca..7198d04b74 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L1FieldMutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L1FieldMutabilityAnalysis.scala @@ -27,9 +27,6 @@ import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.NoEscape import org.opalj.br.fpcf.properties.NonFinalFieldByAnalysis import org.opalj.br.fpcf.properties.NonFinalFieldByLackOfInformation -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.ClassFile import org.opalj.br.Field import org.opalj.br.Method @@ -40,11 +37,12 @@ import org.opalj.br.analyses.cg.ClosedPackagesKey import org.opalj.br.analyses.cg.TypeExtensibilityKey import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.JavaProjectInformationKeys +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.properties.Context import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.br.DeclaredMethod +import org.opalj.br.fpcf.{JavaBasicFPCFEagerAnalysisScheduler, JavaBasicFPCFLazyAnalysisScheduler, JavaFPCFAnalysisScheduler} import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSitesKey @@ -62,7 +60,7 @@ import org.opalj.tac.fpcf.properties.cg.Callers * @author Florian Kübler * @author Michael Eichberg */ -class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { +class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) extends ProjectBasedAnalysis { class State( val field: Field, @@ -356,9 +354,9 @@ class L1FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } } -sealed trait L1FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +sealed trait L1FieldMutabilityAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq( + override def requiredProjectInformation: JavaProjectInformationKeys = Seq( TypeExtensibilityKey, ClosedPackagesKey, FieldAccessInformationKey, @@ -377,13 +375,13 @@ sealed trait L1FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { */ object EagerL1FieldMutabilityAnalysis extends L1FieldMutabilityAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new L1FieldMutabilityAnalysis(p) val fields = p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldMutability) @@ -396,11 +394,11 @@ object EagerL1FieldMutabilityAnalysis */ object LazyL1FieldMutabilityAnalysis extends L1FieldMutabilityAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new L1FieldMutabilityAnalysis(p) ps.registerLazyPropertyComputation( FieldMutability.key, analysis.determineFieldMutability diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldMutabilityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldMutabilityAnalysis.scala index 8074734a2f..45f7a9fa85 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldMutabilityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/L2FieldMutabilityAnalysis.scala @@ -38,7 +38,6 @@ import org.opalj.br.fpcf.properties.NonFinalFieldByAnalysis import org.opalj.br.fpcf.properties.NonFinalFieldByLackOfInformation import org.opalj.br.fpcf.properties.NotPrematurelyReadField import org.opalj.br.fpcf.properties.PrematurelyReadField -import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler import org.opalj.br.ClassFile import org.opalj.br.ComputationalTypeFloat import org.opalj.br.ComputationalTypeInt @@ -54,24 +53,23 @@ import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.ClosedPackagesKey import org.opalj.br.analyses.cg.TypeExtensibilityKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.cfg.BasicBlock import org.opalj.br.cfg.CFG import org.opalj.br.cfg.CFGNode import org.opalj.br.fpcf.properties.FieldMutability import org.opalj.br.fpcf.properties.FinalField import org.opalj.br.fpcf.properties.NonFinalField -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.br.fpcf.properties.FieldPrematurelyRead import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.properties.Context import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.ai.isImmediateVMException import org.opalj.ai.pcOfImmediateVMException import org.opalj.ai.pcOfMethodExternalException +import org.opalj.br.fpcf.{JavaBasicFPCFEagerAnalysisScheduler, JavaBasicFPCFLazyAnalysisScheduler, JavaFPCFAnalysisScheduler} import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSitesKey @@ -89,7 +87,7 @@ import org.opalj.tac.fpcf.properties.cg.Callers * @author Florian Kübler * @author Michael Eichberg */ -class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) extends FPCFAnalysis { +class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) extends ProjectBasedAnalysis { case class State( field: Field, @@ -1104,9 +1102,9 @@ class L2FieldMutabilityAnalysis private[analyses] (val project: SomeProject) ext } } -trait L2FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L2FieldMutabilityAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq( + override def requiredProjectInformation: JavaProjectInformationKeys = Seq( TypeExtensibilityKey, ClosedPackagesKey, FieldAccessInformationKey, @@ -1132,9 +1130,9 @@ trait L2FieldMutabilityAnalysisScheduler extends FPCFAnalysisScheduler { */ object EagerL2FieldMutabilityAnalysis extends L2FieldMutabilityAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { - final override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + final override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new L2FieldMutabilityAnalysis(p) val fields = p.allFields ps.scheduleEagerComputationsForEntities(fields)(analysis.determineFieldMutability) @@ -1151,13 +1149,13 @@ object EagerL2FieldMutabilityAnalysis */ object LazyL2FieldMutabilityAnalysis extends L2FieldMutabilityAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { final override def register( p: SomeProject, ps: PropertyStore, unused: Null - ): FPCFAnalysis = { + ): ProjectBasedAnalysis = { val analysis = new L2FieldMutabilityAnalysis(p) ps.registerLazyPropertyComputation( FieldMutability.key, analysis.determineFieldMutability diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/SystemPropertiesAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/SystemPropertiesAnalysisScheduler.scala index d873bbac98..b644e70584 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/SystemPropertiesAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/SystemPropertiesAnalysisScheduler.scala @@ -22,9 +22,9 @@ import org.opalj.br.fpcf.properties.SystemProperties import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.analyses.cg.ReachableMethodAnalysis @@ -130,9 +130,9 @@ class SystemPropertiesAnalysisScheduler private[analyses] ( } -object SystemPropertiesAnalysisScheduler extends BasicFPCFTriggeredAnalysisScheduler { +object SystemPropertiesAnalysisScheduler extends JavaBasicFPCFTriggeredAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = Set( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIInitializer.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIInitializer.scala index eef9176085..f72b9902d3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIInitializer.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIInitializer.scala @@ -6,16 +6,16 @@ package analyses import org.opalj.fpcf.PropertyStore import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.ai.domain.RecordDefUse import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.fpcf.JavaFPCFAnalysisScheduler /** * Transforms an aiResult to the 3-address code. * * @author Michael Eichberg */ -trait TACAIInitializer extends FPCFAnalysisScheduler { +trait TACAIInitializer extends JavaFPCFAnalysisScheduler { override type InitializationData = Null diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIProvider.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIProvider.scala index a874d73f38..52660cf65e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIProvider.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAIProvider.scala @@ -6,20 +6,20 @@ package analyses import org.opalj.fpcf.Entity import org.opalj.fpcf.FinalEP -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Result import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.ai.fpcf.analyses.DomainBasedFPCFAnalysisScheduler import org.opalj.ai.fpcf.analyses.L0BaseAIResultAnalysis import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.ai.fpcf.properties.ProjectSpecificAIExecutor +import org.opalj.br.fpcf.{JavaFPCFEagerAnalysisScheduler, JavaFPCFLazyAnalysisScheduler} +import org.opalj.si.FPCFAnalysis import org.opalj.tac.fpcf.analyses.TACAIAnalysis.computeTheTACAI import org.opalj.tac.fpcf.properties.TACAI @@ -30,7 +30,7 @@ import org.opalj.tac.fpcf.properties.TACAI * * @author Michael Eichberg */ -class TACAIProvider private[analyses] (val project: SomeProject) extends FPCFAnalysis { +class TACAIProvider private[analyses] (val project: SomeProject) extends ProjectBasedAnalysis { final implicit val aiFactory: ProjectSpecificAIExecutor = project.get(AIDomainFactoryKey) @@ -44,7 +44,7 @@ class TACAIProvider private[analyses] (val project: SomeProject) extends FPCFAna sealed trait TACAIProviderScheduler extends TACAIInitializer with DomainBasedFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(AIDomainFactoryKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(AIDomainFactoryKey) final def derivedProperty: PropertyBounds = PropertyBounds.finalP(TACAI) @@ -60,13 +60,13 @@ sealed trait TACAIProviderScheduler extends TACAIInitializer with DomainBasedFPC } -object EagerTACAIProvider extends TACAIProviderScheduler with FPCFEagerAnalysisScheduler { +object EagerTACAIProvider extends TACAIProviderScheduler with JavaFPCFEagerAnalysisScheduler { override def derivesCollaboratively: Set[PropertyBounds] = Set.empty override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new TACAIProvider(p) // THE TACAIPROVIDER MUST NOT BE CREATED IN INIT! val methods = p.allMethodsWithBody ps.scheduleEagerComputationsForEntities(methods)(analysis.computeTAC) @@ -74,11 +74,11 @@ object EagerTACAIProvider extends TACAIProviderScheduler with FPCFEagerAnalysisS } } -object LazyTACAIProvider extends TACAIProviderScheduler with FPCFLazyAnalysisScheduler { +object LazyTACAIProvider extends TACAIProviderScheduler with JavaFPCFLazyAnalysisScheduler { override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new TACAIProvider(p) // THE TACAIPROVIDER MUST NOT BE CREATED IN INIT! ps.registerLazyPropertyComputation(TACAI.key, analysis.computeTAC) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAITransformer.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAITransformer.scala index 8b7f2097cb..118bce1341 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAITransformer.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/TACAITransformer.scala @@ -9,11 +9,8 @@ import org.opalj.fpcf.FinalEP import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyStore import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFTransformerScheduler -import org.opalj.br.fpcf.DefaultFPCFAnalysis -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.{DefaultFPCFAnalysis, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} +import org.opalj.br.fpcf.JavaBasicFPCFTransformerScheduler import org.opalj.ai.fpcf.properties.BaseAIResult import org.opalj.tac.fpcf.properties.NoTACAI import org.opalj.tac.fpcf.properties.TACAI @@ -23,9 +20,9 @@ import org.opalj.tac.fpcf.properties.TACAI * * @author Michael Eichberg */ -object TACAITransformer extends BasicFPCFTransformerScheduler with TACAIInitializer { +object TACAITransformer extends JavaBasicFPCFTransformerScheduler with TACAIInitializer { - override def requiredProjectInformation: ProjectInformationKeys = Seq.empty + override def requiredProjectInformation: JavaProjectInformationKeys = Seq.empty def derivedProperty: PropertyBounds = PropertyBounds.finalP(TACAI) @@ -33,7 +30,7 @@ object TACAITransformer extends BasicFPCFTransformerScheduler with TACAIInitiali override def derivesLazily: Some[PropertyBounds] = Some(derivedProperty) - override def register(p: SomeProject, ps: PropertyStore, i: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, i: Null): ProjectBasedAnalysis = { class TheTACAITransformer extends DefaultFPCFAnalysis(p) with ((Entity, BaseAIResult) => FinalEP[Method, TACAI]) { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphAnalysis.scala index 34732807c5..43d2515698 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphAnalysis.scala @@ -34,15 +34,17 @@ import org.opalj.fpcf.PropertyStore import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.InitialEntryPointsKey import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.cg.CallBySignatureKey +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.OnlyCallersWithUnknownContext -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.properties.TACAI +import scala.reflect.ClassTag + /** * Generates call graphs based on the used [[TypeIterator]]. * It uses the AI information of the three-address code to get the most precise information for @@ -457,9 +459,10 @@ class CallGraphAnalysis private[cg] ( } } -object CallGraphAnalysisScheduler extends BasicFPCFTriggeredAnalysisScheduler { +object CallGraphAnalysisScheduler extends JavaBasicFPCFTriggeredAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + implicit override val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, InitialEntryPointsKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphDeserializer.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphDeserializer.scala index 533bcd54e7..0d366914bb 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphDeserializer.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/CallGraphDeserializer.scala @@ -7,13 +7,10 @@ package cg import java.io.File import java.io.FileInputStream - import scala.collection.mutable.ArrayBuffer - import play.api.libs.json.Json import play.api.libs.json.Reads import play.api.libs.json.Writes - import org.opalj.fpcf.ProperPropertyComputationResult import org.opalj.fpcf.PropertyBounds import org.opalj.fpcf.PropertyComputationResult @@ -25,10 +22,10 @@ import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.FieldType import org.opalj.br.MethodDescriptor import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.PCAndInstruction -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.properties.SimpleContextsKey import org.opalj.br.fpcf.properties.SimpleContexts import org.opalj.tac.fpcf.properties.cg.Callees @@ -120,7 +117,7 @@ object MethodDesc { private class CallGraphDeserializer private[analyses] ( final val serializedCG: File, final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { private implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) private val simpleContexts: SimpleContexts = project.get(SimpleContextsKey) @@ -199,12 +196,12 @@ private class CallGraphDeserializer private[analyses] ( } } -class CallGraphDeserializerScheduler(serializedCG: File) extends BasicFPCFEagerAnalysisScheduler { +class CallGraphDeserializerScheduler(serializedCG: File) extends JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, SimpleContextsKey) - override def start(p: SomeProject, ps: PropertyStore, i: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, i: Null): ProjectBasedAnalysis = { val analysis = new CallGraphDeserializer(serializedCG, p) ps.scheduleEagerComputationForEntity(p)(analysis.analyze) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ConfiguredNativeMethodsCallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ConfiguredNativeMethodsCallGraphAnalysis.scala index e85cb48603..9b069534f7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ConfiguredNativeMethodsCallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ConfiguredNativeMethodsCallGraphAnalysis.scala @@ -20,15 +20,15 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEPS import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.NoCallers -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.cg.TypeIteratorKey /** @@ -49,7 +49,7 @@ import org.opalj.tac.cg.TypeIteratorKey */ class ConfiguredNativeMethodsCallGraphAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { val configKey = "org.opalj.fpcf.analyses.ConfiguredNativeMethodsAnalysis" @@ -126,8 +126,8 @@ class ConfiguredNativeMethodsCallGraphAnalysis private[analyses] ( } } -object ConfiguredNativeMethodsCallGraphAnalysisScheduler extends BasicFPCFTriggeredAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = +object ConfiguredNativeMethodsCallGraphAnalysisScheduler extends JavaBasicFPCFTriggeredAnalysisScheduler { + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs(Callers) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/DoPrivilegedCGAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/DoPrivilegedCGAnalysis.scala index 883e3d69d6..f13b71335a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/DoPrivilegedCGAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/DoPrivilegedCGAnalysis.scala @@ -20,16 +20,16 @@ import org.opalj.value.ValueInformation import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.br.ArrayType import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.br.ReferenceType -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.VirtualFormalParametersKey -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.properties.TheTACAI @@ -163,7 +163,7 @@ class DoPrivilegedMethodAnalysis private[cg] ( class DoPrivilegedCGAnalysis private[cg] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { def analyze(p: SomeProject): PropertyComputationResult = { var analyses: List[DoPrivilegedMethodAnalysis] = Nil @@ -309,9 +309,9 @@ class DoPrivilegedCGAnalysis private[cg] ( } } -object DoPrivilegedAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { +object DoPrivilegedAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, VirtualFormalParametersKey, DefinitionSitesKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = Set.empty diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/FinalizerAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/FinalizerAnalysis.scala index 4ff2f44a86..2f3e4ed938 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/FinalizerAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/FinalizerAnalysis.scala @@ -20,11 +20,11 @@ import org.opalj.fpcf.Results import org.opalj.br.MethodDescriptor import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.DeclaredMethod +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.NoCallers import org.opalj.tac.fpcf.properties.cg.OnlyVMLevelCallers @@ -37,7 +37,7 @@ import org.opalj.tac.fpcf.properties.cg.OnlyVMLevelCallers * * @author Florian Kuebler */ -class FinalizerAnalysis private[analyses] ( final val project: SomeProject) extends FPCFAnalysis { +class FinalizerAnalysis private[analyses] ( final val project: SomeProject) extends ProjectBasedAnalysis { implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) @@ -87,9 +87,9 @@ class FinalizerAnalysis private[analyses] ( final val project: SomeProject) exte } } -object FinalizerAnalysisScheduler extends BasicFPCFTriggeredAnalysisScheduler { +object FinalizerAnalysisScheduler extends JavaBasicFPCFTriggeredAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs(Callers) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/LoadedClassesAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/LoadedClassesAnalysis.scala index 117e151bd5..688194fd02 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/LoadedClassesAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/LoadedClassesAnalysis.scala @@ -25,10 +25,10 @@ import org.opalj.fpcf.UBP import org.opalj.br.DeclaredMethod import org.opalj.br.Method import org.opalj.br.ObjectType -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.LoadedClasses import org.opalj.tac.fpcf.properties.cg.NoCallers @@ -42,7 +42,7 @@ import org.opalj.tac.fpcf.properties.TACAI */ class LoadedClassesAnalysis( val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { /** * If the method in `callersOfMethod` has no callers * ([[NoCallers]]), it is not reachable, and its declaring class @@ -235,9 +235,9 @@ class LoadedClassesAnalysis( } } -object LoadedClassesAnalysisScheduler extends BasicFPCFTriggeredAnalysisScheduler { +object LoadedClassesAnalysisScheduler extends JavaBasicFPCFTriggeredAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq.empty + override def requiredProjectInformation: JavaProjectInformationKeys = Seq.empty override def uses: Set[PropertyBounds] = PropertyBounds.ubs( LoadedClasses, @@ -251,7 +251,7 @@ object LoadedClassesAnalysisScheduler extends BasicFPCFTriggeredAnalysisSchedule override def derivesCollaboratively: Set[PropertyBounds] = PropertyBounds.ubs(LoadedClasses) - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new LoadedClassesAnalysis(p) ps.registerTriggeredComputation(triggeredBy, analysis.handleCaller) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ReachableMethodAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ReachableMethodAnalysis.scala index 774316dc7e..1d3e897225 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ReachableMethodAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ReachableMethodAnalysis.scala @@ -15,7 +15,7 @@ import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.DeclaredMethod @@ -30,7 +30,7 @@ import org.opalj.tac.fpcf.properties.TACAI * * @author Florian Kuebler */ -trait ReachableMethodAnalysis extends FPCFAnalysis with TypeConsumerAnalysis { +trait ReachableMethodAnalysis extends ProjectBasedAnalysis with TypeConsumerAnalysis { protected implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/SerializationRelatedCallsAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/SerializationRelatedCallsAnalysis.scala index 8a5357e93e..614c93ed41 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/SerializationRelatedCallsAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/SerializationRelatedCallsAnalysis.scala @@ -6,7 +6,6 @@ package analyses package cg import scala.annotation.tailrec - import org.opalj.collection.immutable.IntTrieSet import org.opalj.fpcf.Entity import org.opalj.fpcf.EPS @@ -21,8 +20,7 @@ import org.opalj.fpcf.SomeEPS import org.opalj.value.ASObjectValue import org.opalj.value.ValueInformation import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.DeclaredMethod import org.opalj.br.ElementReferenceType import org.opalj.br.MethodDescriptor @@ -32,11 +30,12 @@ import org.opalj.br.ObjectType import org.opalj.br.ObjectType.{ObjectOutputStream => ObjectOutputStreamType} import org.opalj.br.ObjectType.{ObjectInputStream => ObjectInputStreamType} import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.br.Method import org.opalj.br.ReferenceType +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.fpcf.properties.TheTACAI @@ -84,7 +83,7 @@ class OOSWriteObjectAnalysis private[analyses] ( val parameters = Seq(receiverOption.flatMap(os => persistentUVar(os.asVar))) implicit val state: CGState[ContextType] = new CGState[ContextType]( - callerContext, FinalEP(callerContext.method.definedMethod, TheTACAI(tac)), + callerContext, FinalEP(callerContext.method.definedMethod, TheTACAI(tac)) ) handleOOSWriteObject(callerContext, param, pc, receiver, parameters, indirectCalls) @@ -471,7 +470,7 @@ class OISReadObjectAnalysis private[analyses] ( */ class SerializationRelatedCallsAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { def process(p: SomeProject): PropertyComputationResult = { val readObjectAnalysis = new OISReadObjectAnalysis(project) @@ -482,9 +481,9 @@ class SerializationRelatedCallsAnalysis private[analyses] ( } } -object SerializationRelatedCallsAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { +object SerializationRelatedCallsAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = @@ -498,7 +497,7 @@ object SerializationRelatedCallsAnalysisScheduler extends BasicFPCFEagerAnalysis override def derivesEagerly: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, i: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, i: Null): ProjectBasedAnalysis = { val analysis = new SerializationRelatedCallsAnalysis(p) ps.scheduleEagerComputationForEntity(p)(analysis.process) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/StaticInitializerAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/StaticInitializerAnalysis.scala index fe73fcf563..255be7783c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/StaticInitializerAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/StaticInitializerAnalysis.scala @@ -6,7 +6,6 @@ package analyses package cg import scala.language.existentials - import org.opalj.fpcf.EOptionP import org.opalj.fpcf.EPK import org.opalj.fpcf.EPS @@ -27,10 +26,10 @@ import org.opalj.br.DefinedMethod import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.LoadedClasses import org.opalj.tac.fpcf.properties.cg.OnlyVMLevelCallers @@ -44,7 +43,7 @@ import org.opalj.tac.fpcf.properties.cg.OnlyVMLevelCallers * @author Florian Kuebler */ // TODO Instead of added the clinits for all super types, add all super types to be loaded -class StaticInitializerAnalysis(val project: SomeProject) extends FPCFAnalysis { +class StaticInitializerAnalysis(val project: SomeProject) extends ProjectBasedAnalysis { private val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) @@ -150,9 +149,9 @@ class StaticInitializerAnalysis(val project: SomeProject) extends FPCFAnalysis { } -object StaticInitializerAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { +object StaticInitializerAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs(LoadedClasses) @@ -162,7 +161,7 @@ object StaticInitializerAnalysisScheduler extends BasicFPCFEagerAnalysisSchedule override def derivesEagerly: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new StaticInitializerAnalysis(p) ps.scheduleEagerComputationForEntity(p)( analysis.analyze diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ThreadRelatedCallsAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ThreadRelatedCallsAnalysis.scala index 2c31b769d4..03d67bdac5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ThreadRelatedCallsAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/ThreadRelatedCallsAnalysis.scala @@ -18,17 +18,17 @@ import org.opalj.fpcf.SomeEPS import org.opalj.value.IsReferenceValue import org.opalj.br.analyses.SomeProject import org.opalj.br.DeclaredMethod -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType import org.opalj.br.VoidType import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.br.ReferenceType +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.properties.Context import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.properties.TACAI @@ -538,7 +538,7 @@ class UncaughtExceptionHandlerAnalysis private[analyses] ( */ class ThreadRelatedCallsAnalysis private[cg] ( val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { def process(p: SomeProject): PropertyComputationResult = { val declaredMethods = p.get(DeclaredMethodsKey) @@ -588,9 +588,9 @@ class ThreadRelatedCallsAnalysis private[cg] ( } } -object ThreadRelatedCallsAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { +object ThreadRelatedCallsAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/TypeIterator.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/TypeIterator.scala index 081d144583..e3e1f48cac 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/TypeIterator.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/TypeIterator.scala @@ -41,7 +41,6 @@ import org.opalj.br.analyses.VirtualFormalParameters import org.opalj.br.analyses.VirtualFormalParametersKey import org.opalj.br.fpcf.properties.pointsto.AllocationSite import org.opalj.br.fpcf.properties.pointsto.NoAllocationSites -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.tac.fpcf.properties.cg.InstantiatedTypes import org.opalj.tac.fpcf.properties.cg.NoInstantiatedTypes import org.opalj.br.fpcf.properties.pointsto.NoTypes @@ -61,6 +60,7 @@ import org.opalj.br.analyses.FieldAccessInformationKey import org.opalj.br.Method import org.opalj.br.PCs import org.opalj.br.fpcf.properties.pointsto.allocationSiteLongToTypeId +import org.opalj.si.PropertyStoreKey import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSites import org.opalj.tac.common.DefinitionSitesKey diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MethodMatcher.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MethodMatcher.scala index 8c77563964..b72776ed9b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MethodMatcher.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MethodMatcher.scala @@ -53,7 +53,7 @@ class ClassBasedMethodMatcher( val onlyMethodsExactlyInClass: Boolean ) extends MethodMatcher { - // TODO use a ProjectInformationKey or WeakHashMap to cache methods per project + // TODO use a JavaProjectInformationKey or WeakHashMap to cache methods per project // (for the contains check) private[this] def methods(implicit p: SomeProject): Set[Method] = possibleClasses.flatMap { c => // todo what about "inherited" static methods? diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/ReflectionRelatedCallsAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/ReflectionRelatedCallsAnalysis.scala index 781698b9b1..72bb48ac95 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/ReflectionRelatedCallsAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/ReflectionRelatedCallsAnalysis.scala @@ -29,7 +29,7 @@ import org.opalj.value.ASObjectValue import org.opalj.value.ValueInformation import org.opalj.br.analyses.SomeProject import org.opalj.br.DeclaredMethod -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.ArrayType import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType @@ -40,13 +40,13 @@ import org.opalj.br.InvokeVirtualMethodHandle import org.opalj.br.NewInvokeSpecialMethodHandle import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.BooleanType -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.LoadedClasses import org.opalj.br.analyses.ProjectIndexKey import org.opalj.br.Method +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.analyses.cg.reflection.MatcherUtil.retrieveSuitableMatcher import org.opalj.tac.fpcf.analyses.cg.reflection.MethodHandlesUtil.retrieveDescriptorBasedMethodMatcher @@ -1355,7 +1355,7 @@ object ReflectionRelatedCallsAnalysis { */ class ReflectionRelatedCallsAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { def process(p: SomeProject): PropertyComputationResult = { val declaredMethods = project.get(DeclaredMethodsKey) @@ -1470,9 +1470,9 @@ class ReflectionRelatedCallsAnalysis private[analyses] ( } } -object ReflectionRelatedCallsAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { +object ReflectionRelatedCallsAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, ProjectIndexKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs( @@ -1492,7 +1492,7 @@ object ReflectionRelatedCallsAnalysisScheduler extends BasicFPCFEagerAnalysisSch LoadedClasses ) - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new ReflectionRelatedCallsAnalysis(p) ps.scheduleEagerComputationForEntity(p)(analysis.process) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TamiFlexCallGraphAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TamiFlexCallGraphAnalysis.scala index 07cbf9c74a..a82221a402 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TamiFlexCallGraphAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TamiFlexCallGraphAnalysis.scala @@ -14,15 +14,15 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Results import org.opalj.value.ValueInformation import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.ArrayType import org.opalj.br.DeclaredMethod import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.analyses.JavaProjectInformationKeys +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.cg.TypeIteratorKey @@ -39,7 +39,7 @@ import scala.collection.immutable.ArraySeq */ class TamiFlexCallGraphAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) val ConstructorT: ObjectType = ObjectType("java/lang/reflect/Constructor") @@ -158,9 +158,9 @@ class TamiFlexMethodInvokeAnalysis private[analyses] ( } } -object TamiFlexCallGraphAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { +object TamiFlexCallGraphAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, TamiFlexKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs( @@ -174,7 +174,7 @@ object TamiFlexCallGraphAnalysisScheduler extends BasicFPCFEagerAnalysisSchedule Callees ) - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new TamiFlexCallGraphAnalysis(p) ps.scheduleEagerComputationForEntity(p)(analysis.process) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/ConfiguredNativeMethodsInstantiatedTypesAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/ConfiguredNativeMethodsInstantiatedTypesAnalysis.scala index 72ba4c0b8b..0c479a8e38 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/ConfiguredNativeMethodsInstantiatedTypesAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/ConfiguredNativeMethodsInstantiatedTypesAnalysis.scala @@ -17,16 +17,13 @@ import org.opalj.fpcf.PropertyComputationResult import org.opalj.fpcf.PropertyKind import org.opalj.fpcf.PropertyStore import org.opalj.br.DeclaredMethod -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, JavaProjectInformationKeys, ProjectBasedAnalysis, SomeProject} import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.InstantiatedTypes import org.opalj.tac.fpcf.properties.cg.NoCallers import org.opalj.br.ReferenceType +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler + import scala.collection.immutable.ArraySeq /** @@ -37,7 +34,7 @@ import scala.collection.immutable.ArraySeq */ class ConfiguredNativeMethodsInstantiatedTypesAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { private[this] implicit val declaredMethods: DeclaredMethods = p.get(DeclaredMethodsKey) @@ -102,9 +99,9 @@ class ConfiguredNativeMethodsInstantiatedTypesAnalysis private[analyses] ( } } -object ConfiguredNativeMethodsInstantiatedTypesAnalysisScheduler extends BasicFPCFTriggeredAnalysisScheduler { +object ConfiguredNativeMethodsInstantiatedTypesAnalysisScheduler extends JavaBasicFPCFTriggeredAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs(Callers, InstantiatedTypes) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/InstantiatedTypesAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/InstantiatedTypesAnalysis.scala index 44db7f46db..cacb6d40fd 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/InstantiatedTypesAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/rta/InstantiatedTypesAnalysis.scala @@ -25,16 +25,16 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.SomeEPS import org.opalj.br.DeclaredMethod import org.opalj.br.ObjectType -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.InstantiatedTypes import org.opalj.tac.fpcf.properties.cg.NoCallers import org.opalj.br.instructions.NEW import org.opalj.br.ReferenceType +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.cg.TypeIteratorKey /** @@ -47,7 +47,7 @@ import org.opalj.tac.cg.TypeIteratorKey */ class InstantiatedTypesAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { private[this] implicit val typeIterator: TypeIterator = project.get(TypeIteratorKey) @@ -205,9 +205,9 @@ object InstantiatedTypesAnalysis { } } -object InstantiatedTypesAnalysisScheduler extends BasicFPCFTriggeredAnalysisScheduler { +object InstantiatedTypesAnalysisScheduler extends JavaBasicFPCFTriggeredAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeIteratorKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(TypeIteratorKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs( InstantiatedTypes, @@ -220,7 +220,7 @@ object InstantiatedTypesAnalysisScheduler extends BasicFPCFTriggeredAnalysisSche override def derivesEagerly: Set[PropertyBounds] = Set.empty - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new InstantiatedTypesAnalysis(p) ps.registerTriggeredComputation(triggeredBy, analysis.analyze) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/ArrayInstantiationsAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/ArrayInstantiationsAnalysis.scala index f560bdf64f..af6291559e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/ArrayInstantiationsAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/ArrayInstantiationsAnalysis.scala @@ -8,10 +8,10 @@ package xta import org.opalj.br.ArrayType import org.opalj.br.Method -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.InstantiatedTypes import org.opalj.br.instructions.CreateNewArrayInstruction @@ -24,8 +24,8 @@ import org.opalj.fpcf.PropertyKind import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Results import org.opalj.tac.fpcf.properties.TACAI -import scala.collection.mutable +import scala.collection.mutable import org.opalj.tac.cg.TypeIteratorKey /** @@ -117,11 +117,11 @@ final class ArrayInstantiationsAnalysis( class ArrayInstantiationsAnalysisScheduler( selectSetEntity: TypeSetEntitySelector -) extends BasicFPCFTriggeredAnalysisScheduler { +) extends JavaBasicFPCFTriggeredAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeIteratorKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(TypeIteratorKey) - override def register(project: SomeProject, propertyStore: PropertyStore, i: Null): FPCFAnalysis = { + override def register(project: SomeProject, propertyStore: PropertyStore, i: Null): ProjectBasedAnalysis = { val analysis = new ArrayInstantiationsAnalysis(project, selectSetEntity) propertyStore.registerTriggeredComputation(Callers.key, analysis.analyze) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/InstantiatedTypesAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/InstantiatedTypesAnalysis.scala index 2d68845ace..4393e874ca 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/InstantiatedTypesAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/InstantiatedTypesAnalysis.scala @@ -8,13 +8,13 @@ package xta import org.opalj.br._ import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.ClosedPackagesKey import org.opalj.br.analyses.cg.InitialEntryPointsKey import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.InstantiatedTypes import org.opalj.tac.fpcf.properties.cg.NoCallers @@ -60,7 +60,7 @@ import scala.collection.mutable.ArrayBuffer class InstantiatedTypesAnalysis private[analyses] ( final val project: SomeProject, val setEntitySelector: TypeSetEntitySelector -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { private[this] implicit val typeIterator: TypeIterator = project.get(TypeIteratorKey) @@ -253,9 +253,9 @@ class InstantiatedTypesAnalysis private[analyses] ( class InstantiatedTypesAnalysisScheduler( val selectSetEntity: TypeSetEntitySelector -) extends BasicFPCFTriggeredAnalysisScheduler { +) extends JavaBasicFPCFTriggeredAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq( + override def requiredProjectInformation: JavaProjectInformationKeys = Seq( TypeIteratorKey, ClosedPackagesKey, DeclaredMethodsKey, InitialEntryPointsKey, InitialInstantiatedTypesKey ) @@ -270,7 +270,7 @@ class InstantiatedTypesAnalysisScheduler( override def derivesEagerly: Set[PropertyBounds] = Set.empty - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new InstantiatedTypesAnalysis(p, selectSetEntity) ps.registerTriggeredComputation(triggeredBy, analysis.analyze) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/LibraryInstantiatedTypesBasedEntryPointsAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/LibraryInstantiatedTypesBasedEntryPointsAnalysis.scala index 5e533fc32a..83c7d33911 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/LibraryInstantiatedTypesBasedEntryPointsAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/LibraryInstantiatedTypesBasedEntryPointsAnalysis.scala @@ -7,7 +7,6 @@ package cg package xta import java.util.concurrent.ConcurrentHashMap - import org.opalj.fpcf.EOptionP import org.opalj.fpcf.EPK import org.opalj.fpcf.InterimEP @@ -25,14 +24,14 @@ import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.UBP import org.opalj.fpcf.UBPS import org.opalj.br.ReferenceType -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler import org.opalj.br.DeclaredMethod import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.InstantiatedTypes import org.opalj.tac.fpcf.properties.cg.OnlyCallersWithUnknownContext @@ -51,7 +50,7 @@ import org.opalj.tac.fpcf.properties.cg.OnlyCallersWithUnknownContext */ class LibraryInstantiatedTypesBasedEntryPointsAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) // TODO: Use a Scala set @@ -136,19 +135,19 @@ class LibraryInstantiatedTypesBasedEntryPointsAnalysis private[analyses] ( } } -object LibraryInstantiatedTypesBasedEntryPointsAnalysis extends BasicFPCFTriggeredAnalysisScheduler { +object LibraryInstantiatedTypesBasedEntryPointsAnalysis extends JavaBasicFPCFTriggeredAnalysisScheduler { override def register( project: SomeProject, propertyStore: PropertyStore, i: Null - ): FPCFAnalysis = { + ): ProjectBasedAnalysis = { val analysis = new LibraryInstantiatedTypesBasedEntryPointsAnalysis(project) propertyStore.registerTriggeredComputation(InstantiatedTypes.key, analysis.analyze) analysis } - override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey) override def uses: Set[PropertyBounds] = Set( PropertyBounds.ub(InstantiatedTypes) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/TypePropagationAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/TypePropagationAnalysis.scala index 5ee2719e7a..8f7550f8e2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/TypePropagationAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/xta/TypePropagationAnalysis.scala @@ -7,17 +7,15 @@ package cg package xta import scala.jdk.CollectionConverters._ - import org.opalj.br.Code import org.opalj.br.DeclaredMethod import org.opalj.br.Field import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.ReferenceType -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.InstantiatedTypes @@ -36,6 +34,7 @@ import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEPS import org.opalj.fpcf.SomePartialResult import org.opalj.br.DefinedMethod +import org.opalj.br.fpcf.JavaBasicFPCFTriggeredAnalysisScheduler import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.properties.TACAI @@ -486,9 +485,9 @@ final class TypePropagationAnalysis private[analyses] ( final class TypePropagationAnalysisScheduler( val selectSetEntity: TypeSetEntitySelector -) extends BasicFPCFTriggeredAnalysisScheduler { +) extends JavaBasicFPCFTriggeredAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeIteratorKey) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(TypeIteratorKey) override type InitializationData = Null @@ -496,7 +495,7 @@ final class TypePropagationAnalysisScheduler( override def init(p: SomeProject, ps: PropertyStore): Null = null - override def register(project: SomeProject, propertyStore: PropertyStore, i: Null): FPCFAnalysis = { + override def register(project: SomeProject, propertyStore: PropertyStore, i: Null): ProjectBasedAnalysis = { val analysis = new TypePropagationAnalysis(project, selectSetEntity) propertyStore.registerTriggeredComputation(Callers.key, analysis.analyze) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/AbstractEscapeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/AbstractEscapeAnalysis.scala index 1923bcae57..c74651df6f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/AbstractEscapeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/AbstractEscapeAnalysis.scala @@ -21,7 +21,7 @@ import org.opalj.br.analyses.VirtualFormalParameters import org.opalj.br.analyses.VirtualFormalParametersKey import org.opalj.br.fpcf.properties.EscapeViaReturn import org.opalj.br.fpcf.properties.EscapeViaStaticField -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.properties.Context import org.opalj.br.fpcf.properties.GlobalEscape import org.opalj.br.fpcf.properties.NoEscape @@ -47,7 +47,7 @@ import org.opalj.tac.fpcf.properties.TACAI * @author Florian Kuebler */ -trait AbstractEscapeAnalysis extends FPCFAnalysis { +trait AbstractEscapeAnalysis extends ProjectBasedAnalysis { type AnalysisContext <: AbstractEscapeAnalysisContext type AnalysisState <: AbstractEscapeAnalysisState diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/InterProceduralEscapeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/InterProceduralEscapeAnalysis.scala index 13e9e31ca8..4e04cbc10f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/InterProceduralEscapeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/InterProceduralEscapeAnalysis.scala @@ -26,20 +26,18 @@ import org.opalj.br.analyses.VirtualFormalParameter import org.opalj.br.analyses.VirtualFormalParameters import org.opalj.br.analyses.VirtualFormalParametersKey import org.opalj.br.analyses.cg.IsOverridableMethodKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.fpcf.properties.EscapeProperty -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.properties.GlobalEscape import org.opalj.br.fpcf.properties.NoEscape -import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler import org.opalj.br.fpcf.properties.Context import org.opalj.br.fpcf.properties.SimpleContext import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.cg.NoCallers import org.opalj.ai.ValueOrigin +import org.opalj.br.fpcf.{JavaBasicFPCFEagerAnalysisScheduler, JavaBasicFPCFLazyAnalysisScheduler, JavaFPCFAnalysisScheduler} import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.properties.TACAI @@ -157,9 +155,9 @@ class InterProceduralEscapeAnalysis private[analyses] ( } } -sealed trait InterProceduralEscapeAnalysisScheduler extends FPCFAnalysisScheduler { +sealed trait InterProceduralEscapeAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, VirtualFormalParametersKey, IsOverridableMethodKey, TypeIteratorKey) final def derivedProperty: PropertyBounds = PropertyBounds.lub(EscapeProperty) @@ -173,13 +171,13 @@ sealed trait InterProceduralEscapeAnalysisScheduler extends FPCFAnalysisSchedule object EagerInterProceduralEscapeAnalysis extends InterProceduralEscapeAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { type V = DUVar[ValueInformation] - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = super.requiredProjectInformation ++ Seq(DefinitionSitesKey, SimpleContextsKey) - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new InterProceduralEscapeAnalysis(p) val declaredMethods = p.get(DeclaredMethodsKey) @@ -217,13 +215,13 @@ object EagerInterProceduralEscapeAnalysis object LazyInterProceduralEscapeAnalysis extends InterProceduralEscapeAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { /** * Registers the analysis as a lazy computation, that is, the method * will call `ProperytStore.scheduleLazyComputation`. */ - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new InterProceduralEscapeAnalysis(p) ps.registerLazyPropertyComputation(EscapeProperty.key, analysis.determineEscape) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/ReturnValueFreshnessAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/ReturnValueFreshnessAnalysis.scala index 81e14825ad..3e9089910f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/ReturnValueFreshnessAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/ReturnValueFreshnessAnalysis.scala @@ -40,15 +40,12 @@ import org.opalj.br.Field import org.opalj.br.Method import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.br.fpcf.properties.FieldLocality import org.opalj.br.fpcf.properties.ReturnValueFreshness -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.properties.Context import org.opalj.br.fpcf.properties.ExtensibleGetter import org.opalj.br.fpcf.properties.ExtensibleLocalFieldWithGetter @@ -56,6 +53,7 @@ import org.opalj.br.fpcf.properties.NoLocalField import org.opalj.br.fpcf.properties.SimpleContext import org.opalj.br.fpcf.properties.SimpleContexts import org.opalj.br.fpcf.properties.SimpleContextsKey +import org.opalj.br.fpcf.{JavaBasicFPCFEagerAnalysisScheduler, JavaBasicFPCFLazyAnalysisScheduler, JavaFPCFAnalysisScheduler} import org.opalj.tac.cg.CallGraphKey import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.properties.cg.Callees @@ -173,7 +171,7 @@ class ReturnValueFreshnessState(val context: Context) { */ class ReturnValueFreshnessAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { private[this] implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) private[this] val simpleContexts: SimpleContexts = project.get(SimpleContextsKey) @@ -525,11 +523,11 @@ class ReturnValueFreshnessAnalysis private[analyses] ( } } -sealed trait ReturnValueFreshnessAnalysisScheduler extends FPCFAnalysisScheduler { +sealed trait ReturnValueFreshnessAnalysisScheduler extends JavaFPCFAnalysisScheduler { final def derivedProperty: PropertyBounds = PropertyBounds.lub(ReturnValueFreshness) - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DefinitionSitesKey, SimpleContextsKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = { @@ -545,9 +543,9 @@ sealed trait ReturnValueFreshnessAnalysisScheduler extends FPCFAnalysisScheduler object EagerReturnValueFreshnessAnalysis extends ReturnValueFreshnessAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val cg = p.get(CallGraphKey) val methods = cg.reachableMethods() @@ -565,9 +563,9 @@ object EagerReturnValueFreshnessAnalysis object LazyReturnValueFreshnessAnalysis extends ReturnValueFreshnessAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new ReturnValueFreshnessAnalysis(p) ps.registerLazyPropertyComputation(ReturnValueFreshness.key, analysis.determineFreshness) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/SimpleEscapeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/SimpleEscapeAnalysis.scala index 7b661b0ccd..641182a113 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/SimpleEscapeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/escape/SimpleEscapeAnalysis.scala @@ -14,7 +14,7 @@ import org.opalj.br.DefinedMethod import org.opalj.br.Method import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.VirtualFormalParameter import org.opalj.br.analyses.VirtualFormalParameters @@ -22,13 +22,11 @@ import org.opalj.br.analyses.VirtualFormalParametersKey import org.opalj.br.fpcf.properties.AtMost import org.opalj.br.fpcf.properties.EscapeProperty import org.opalj.br.fpcf.properties.NoEscape -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.BasicFPCFLazyAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFAnalysisScheduler +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.properties.Context import org.opalj.br.fpcf.properties.SimpleContextsKey import org.opalj.ai.ValueOrigin +import org.opalj.br.fpcf.{JavaBasicFPCFEagerAnalysisScheduler, JavaBasicFPCFLazyAnalysisScheduler, JavaFPCFAnalysisScheduler} import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.properties.TACAI @@ -95,9 +93,9 @@ class SimpleEscapeAnalysis( final val project: SomeProject) override def createState: AbstractEscapeAnalysisState = new AbstractEscapeAnalysisState {} } -trait SimpleEscapeAnalysisScheduler extends FPCFAnalysisScheduler { +trait SimpleEscapeAnalysisScheduler extends JavaFPCFAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, VirtualFormalParametersKey, TypeIteratorKey) final override def uses: Set[PropertyBounds] = Set( @@ -114,12 +112,12 @@ trait SimpleEscapeAnalysisScheduler extends FPCFAnalysisScheduler { */ object EagerSimpleEscapeAnalysis extends SimpleEscapeAnalysisScheduler - with BasicFPCFEagerAnalysisScheduler { + with JavaBasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = super.requiredProjectInformation ++ Seq(DefinitionSitesKey, SimpleContextsKey) - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val declaredMethods = p.get(DeclaredMethodsKey) val simpleContexts = p.get(SimpleContextsKey) @@ -142,9 +140,9 @@ object EagerSimpleEscapeAnalysis object LazySimpleEscapeAnalysis extends SimpleEscapeAnalysisScheduler - with BasicFPCFLazyAnalysisScheduler { + with JavaBasicFPCFLazyAnalysisScheduler { - override def register(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def register(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new SimpleEscapeAnalysis(p) ps.registerLazyPropertyComputation(EscapeProperty.key, analysis.determineEscape) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala new file mode 100644 index 0000000000..1761a3029a --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala @@ -0,0 +1,10 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.Method +import org.opalj.fpcf.ifds.Callable + +case class JavaMethod(method: Method) extends Callable { + override def name: String = method.name + override def signature: String = method.toJava +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/EvaluationRunner.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/EvaluationRunner.scala new file mode 100644 index 0000000000..17a62665a7 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/EvaluationRunner.scala @@ -0,0 +1,189 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import java.io.File +import java.io.PrintWriter +import scala.language.existentials +import com.typesafe.config.ConfigValueFactory +import org.opalj.bytecode +import org.opalj.util.Milliseconds +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.seq.PKESequentialPropertyStore +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.SomeProject +import org.opalj.ai.domain.l0.PrimitiveTACAIDomain +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.fpcf.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, Statistics} +import org.opalj.fpcf.scheduling.FPCFAnalysesManagerKey +import org.opalj.si.PropertyStoreKey +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.properties.cg.Callers + +abstract class EvaluationRunner { + + protected def analysisClass: IFDSAnalysisScheduler[_, _, _, _] + + protected def printAnalysisResults(analysis: IFDSAnalysis[_, _, _], ps: PropertyStore): Unit = () + + protected def run( + debug: Boolean, + useL2: Boolean, + delay: Boolean, + evalSchedulingStrategies: Boolean, + evaluationFile: Option[File] + ): Unit = { + + if (debug) { + PropertyStore.updateDebug(true) + } + + def evalProject(p: SomeProject): (Milliseconds, Statistics, Option[Object]) = { + if (useL2) { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { + case None => Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) => + requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] + } + } else { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { + case None => Set(classOf[PrimitiveTACAIDomain]) + case Some(requirements) => requirements + classOf[PrimitiveTACAIDomain] + } + } + + val ps = p.get(PropertyStoreKey) + var analysisTime: Milliseconds = Milliseconds.None + p.get(RTACallGraphKey) + println("Start: "+new java.util.Date) + org.opalj.util.gc() + val analysis = + time { + p.get(FPCFAnalysesManagerKey).runAll(analysisClass)._2 + }(t => analysisTime = t.toMilliseconds).collect { + case (_, a: IFDSAnalysis[_, _, _]) => a + }.head + + printAnalysisResults(analysis, ps) + println(s"The analysis took $analysisTime.") + println( + ps.statistics.iterator + .map(_.toString()) + .toList + .sorted + .mkString("PropertyStore Statistics:\n\t", "\n\t", "\n") + ) + ( + analysisTime, + analysis.statistics, + additionalEvaluationResult(analysis) + ) + } + + val p = Project(bytecode.RTJar) + + if (delay) { + println("Sleeping for three seconds.") + Thread.sleep(3000) + } + + if (evalSchedulingStrategies) { + val results = for { + i <- 1 to EvaluationRunner.NUM_EXECUTIONS_EVAL_SCHEDULING_STRATEGIES + strategy <- PKESequentialPropertyStore.Strategies + } yield { + println(s"Round: $i - $strategy") + val strategyValue = ConfigValueFactory.fromAnyRef(strategy) + val newConfig = + p.config.withValue(PKESequentialPropertyStore.TasksManagerKey, strategyValue) + val evaluationResult = evalProject(Project.recreate(p, newConfig)) + org.opalj.util.gc() + (i, strategy, evaluationResult._1, evaluationResult._2) + } + println(results.mkString("AllResults:\n\t", "\n\t", "\n")) + if (evaluationFile.nonEmpty) { + val pw = new PrintWriter(evaluationFile.get) + PKESequentialPropertyStore.Strategies.foreach { strategy => + val strategyResults = results.filter(_._2 == strategy) + val averageTime = strategyResults.map(_._3.timeSpan).sum / strategyResults.size + val (normalFlow, callToStart, exitToReturn, callToReturn) = + computeAverageStatistics(strategyResults.map(_._4)) + pw.println(s"Strategy $strategy:") + pw.println(s"Average time: ${averageTime}ms") + pw.println(s"Average calls of normalFlow: $normalFlow") + pw.println(s"Average calls of callToStart: $callToStart") + pw.println(s"Average calls of exitToReturn: $exitToReturn") + pw.println(s"Average calls of callToReturn: $callToReturn") + pw.println() + } + pw.close() + } + } else { + var times = Seq.empty[Milliseconds] + var statistics = Seq.empty[Statistics] + var additionalEvaluationResults = Seq.empty[Object] + for { + _ <- 1 to EvaluationRunner.NUM_EXECUTIONS + } { + val evaluationResult = evalProject(Project.recreate(p)) + val additionalEvaluationResult = evaluationResult._3 + times :+= evaluationResult._1 + statistics :+= evaluationResult._2 + if (additionalEvaluationResult.isDefined) + additionalEvaluationResults :+= additionalEvaluationResult.get + } + if (evaluationFile.nonEmpty) { + val (normalFlow, callFlow, returnFlow, callToReturnFlow) = computeAverageStatistics( + statistics + ) + val time = times.map(_.timeSpan).sum / times.size + val pw = new PrintWriter(evaluationFile.get) + pw.println(s"Average time: ${time}ms") + pw.println(s"Average calls of normalFlow: $normalFlow") + pw.println(s"Average calls of callFlow: $callFlow") + pw.println(s"Average calls of returnFlow: $returnFlow") + pw.println(s"Average calls of callToReturnFlow: $callToReturnFlow") + if (additionalEvaluationResults.nonEmpty) + writeAdditionalEvaluationResultsToFile(pw, additionalEvaluationResults) + pw.close() + } + } + } + + protected def additionalEvaluationResult(analysis: IFDSAnalysis[_, _, _]): Option[Object] = None + + protected def writeAdditionalEvaluationResultsToFile( + writer: PrintWriter, + additionalEvaluationResults: Seq[Object] + ): Unit = {} + + protected def canBeCalledFromOutside( + method: DeclaredMethod, + propertyStore: PropertyStore + ): Boolean = + propertyStore(method, Callers.key) match { + // This is the case, if the method may be called from outside the library. + case FinalEP(_, p: Callers) => p.hasCallersWithUnknownContext + case _ => + throw new IllegalStateException( + "call graph mut be computed before the analysis starts" + ) + } + + private def computeAverageStatistics(statistics: Seq[Statistics]): (Int, Int, Int, Int) = { + val length = statistics.length + val normalFlow = statistics.map(_.normalFlow).sum / length + val callFlow = statistics.map(_.callFlow).sum / length + val returnFlow = statistics.map(_.returnFlow).sum / length + val callToReturnFlow = statistics.map(_.callToReturnFlow).sum / length + (normalFlow, callFlow, returnFlow, callToReturnFlow) + } +} + +object EvaluationRunner { + var NUM_EXECUTIONS = 10 + var NUM_EXECUTIONS_EVAL_SCHEDULING_STRATEGIES = 2 +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala new file mode 100644 index 0000000000..52332d56b9 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala @@ -0,0 +1,116 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.{DeclaredMethod, Method} +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} +import org.opalj.fpcf.ifds.ICFG +import org.opalj.fpcf.{FinalEP, PropertyStore} +import org.opalj.si.PropertyStoreKey +import org.opalj.tac.cg.TypeIteratorKey +import org.opalj.tac.{Assignment, DUVar, Expr, ExprStmt, LazyDetachedTACAIKey, NonVirtualFunctionCall, NonVirtualMethodCall, StaticFunctionCall, StaticMethodCall, Stmt, TACMethodParameter, TACode, VirtualFunctionCall, VirtualMethodCall} +import org.opalj.tac.fpcf.analyses.cg.TypeIterator +import org.opalj.tac.fpcf.properties.cg.Callees +import org.opalj.value.ValueInformation + +class ForwardICFG(implicit project: SomeProject) + extends ICFG[Method, JavaStatement] { + val tacai: Method => TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) + val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + implicit val typeIterator: TypeIterator = project.get(TypeIteratorKey) + + /** + * Determines the statements at which the analysis starts. + * + * @param callable The analyzed callable. + * @return The statements at which the analysis starts. + */ + override def startStatements(callable: Method): Set[JavaStatement] = { + val TACode(_, code, _, cfg, _) = tacai(callable) + Set(JavaStatement(callable, 0, code, cfg)) + } + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + override def nextStatements(statement: JavaStatement): Set[JavaStatement] = { + statement.cfg + .successors(statement.index) + .map { index => JavaStatement(statement, index) } + } + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + override def getCalleesIfCallStatement(statement: JavaStatement): Option[collection.Set[Method]] = + statement.stmt.astID match { + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID => + Some(getCallees(statement)) + case Assignment.ASTID | ExprStmt.ASTID => + getExpression(statement.stmt).astID match { + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => + Some(getCallees(statement)) + case _ => None + } + case _ => None + } + + /** + * Retrieves the expression of an assignment or expression statement. + * + * @param statement The statement. Must be an Assignment or ExprStmt. + * @return The statement's expression. + */ + private def getExpression(statement: Stmt[_]): Expr[_] = statement.astID match { + case Assignment.ASTID => statement.asAssignment.expr + case ExprStmt.ASTID => statement.asExprStmt.expr + case _ => throw new UnknownError("Unexpected statement") + } + + private def getCallees(statement: JavaStatement): collection.Set[Method] = { + val pc = statement.stmt.pc + val caller = declaredMethods(statement.callable) + val ep = propertyStore(caller, Callees.key) + ep match { + case FinalEP(_, p) => definedMethods(p.directCallees(typeIterator.newContext(caller), pc).map(_.method)) + case _ => + throw new IllegalStateException( + "call graph must be computed before the analysis starts" + ) + } + } + + override def isExitStatement(statement: JavaStatement): Boolean = { + statement.index == statement.node.asBasicBlock.endPC && + statement.node.successors.exists(_.isExitNode) + } + + /** + * Maps some declared methods to their defined methods. + * + * @param declaredMethods Some declared methods. + * @return All defined methods of `declaredMethods`. + */ + private def definedMethods(declaredMethods: Iterator[DeclaredMethod]): collection.Set[Method] = { + val result = scala.collection.mutable.Set.empty[Method] + declaredMethods + .filter( + declaredMethod => + declaredMethod.hasSingleDefinedMethod || + declaredMethod.hasMultipleDefinedMethods + ) + .foreach( + declaredMethod => + declaredMethod + .foreachDefinedMethod(defineMethod => result.add(defineMethod)) + ) + result + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala new file mode 100644 index 0000000000..6c8e4405c9 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -0,0 +1,108 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{PropertyBounds, PropertyKey, PropertyStore} +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.tac.fpcf.properties.cg.Callers + +import java.io.{File, PrintWriter} +import org.opalj.br.Method +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.JavaProjectInformationKeys +import org.opalj.fpcf.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSProperty, IFDSPropertyMetaInformation, Statistics} +import org.opalj.si.PropertyStoreKey +import org.opalj.tac.cg.TypeIteratorKey + +import scala.reflect.ClassTag + +/** + * A variable type analysis implemented as an IFDS analysis. + * In contrast to an ordinary variable type analysis, which also determines types of fields, + * this analysis only determines the types of local variables. + * The subsuming taint can be mixed in to enable subsuming. + * + * @param project The analyzed project. + * @author Mario Trageser + */ +class IFDSBasedVariableTypeAnalysis(project: SomeProject, subsumeFacts: Boolean = false) + extends IFDSAnalysis()(project, new VariableTypeProblem(project, subsumeFacts), VTAResult) + +class IFDSBasedVariableTypeAnalysisScheduler(subsumeFacts: Boolean = false) extends IFDSAnalysisScheduler[SomeProject, VTAFact, Method, JavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new IFDSBasedVariableTypeAnalysis(p, subsumeFacts) + + override def property: IFDSPropertyMetaInformation[JavaStatement, VTAFact] = VTAResult + + override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) + + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, TypeIteratorKey, PropertyStoreKey) + + override implicit val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} + +/** + * The IFDSProperty for this analysis. + */ +case class VTAResult(flows: Map[JavaStatement, Set[VTAFact]], debugData: Map[JavaStatement, Set[VTAFact]] = Map.empty) extends IFDSProperty[JavaStatement, VTAFact] { + + override type Self = VTAResult + override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result) + override def create(result: Map[JavaStatement, Set[VTAFact]], debugData: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result, debugData) + + override def key: PropertyKey[VTAResult] = VTAResult.key +} + +object VTAResult extends IFDSPropertyMetaInformation[JavaStatement, VTAFact] { + + override type Self = VTAResult + override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result) + override def create(result: Map[JavaStatement, Set[VTAFact]], debugData: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result, debugData) + + val key: PropertyKey[VTAResult] = PropertyKey.create("VTAnew", new VTAResult(Map.empty)) +} + +class IFDSBasedVariableTypeAnalysisRunner(subsumeFacts: Boolean = false) extends EvaluationRunner { + + override def analysisClass: IFDSBasedVariableTypeAnalysisScheduler = new IFDSBasedVariableTypeAnalysisScheduler(subsumeFacts) + + override protected def additionalEvaluationResult( + analysis: IFDSAnalysis[_, _, _] + ): Option[Object] = + if (analysis.ifdsProblem.subsumeFacts) Some(analysis.statistics) else None + + override protected def writeAdditionalEvaluationResultsToFile( + writer: PrintWriter, + additionalEvaluationResults: Seq[Object] + ): Unit = { + val numberOfSubsumptions = additionalEvaluationResults.map(_.asInstanceOf[Statistics]) + val length = additionalEvaluationResults.length + val tries = numberOfSubsumptions.map(_.subsumeTries).sum / length + val successes = numberOfSubsumptions.map(_.subsumptions).sum / length + writer.println(s"Average tries to subsume: $tries") + writer.println(s"Average successful subsumes: $successes") + } +} + +object IFDSBasedVariableTypeAnalysisRunner { + def main(args: Array[String]): Unit = { + if (args.contains("--help")) { + println("Potential parameters:") + println(" -seq (to use the SequentialPropertyStore)") + println(" -l2 (to use the l2 domain instead of the default l1 domain)") + println(" -delay (for a three seconds delay before the taint flow analysis is started)") + println(" -debug (for debugging mode in the property store)") + println(" -evalSchedulingStrategies (evaluates all available scheduling strategies)") + println(" -subsumeFacts (enables subsuming)") + println(" -f (Stores the average runtime to this file)") + } else { + val fileIndex = args.indexOf("-f") + new IFDSBasedVariableTypeAnalysisRunner(args.contains("-subsumeFacts")).run( + args.contains("-debug"), + args.contains("-l2"), + args.contains("-delay"), + args.contains("-evalSchedulingStrategies"), + if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None + ) + } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala new file mode 100644 index 0000000000..da295100a1 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -0,0 +1,115 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.br.cfg.{CFG, CFGNode} +import org.opalj.fpcf.ifds.Dependees.Getter +import org.opalj.fpcf.ifds.{AbstractIFDSFact, IFDSProblem, Statement} +import org.opalj.tac.{Assignment, Call, DUVar, ExprStmt, Return, ReturnValue, Stmt, TACStmts} +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.value.ValueInformation + +/** + * A statement that is passed to the concrete analysis. + * + * @param method The method containing the statement. + * @param index The index of the Statement in the code. + * @param code The method's TAC code. + * @param cfg The method's CFG. + */ +case class JavaStatement( + method: Method, + index: Int, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] +) extends Statement[Method, CFGNode] { + + override def hashCode(): Int = method.hashCode() * 31 + index + + override def equals(o: Any): Boolean = o match { + case s: JavaStatement => s.index == index && s.method == method + case _ => false + } + + override def toString: String = s"${method.signatureToJava(false)}[${index}]\n\t${stmt}\n\t${method.toJava}" + override def callable: Method = method + override def node: CFGNode = cfg.bb(index) + def stmt: Stmt[V] = code(index) +} + +object JavaStatement { + def apply(referenceStatement: JavaStatement, newIndex: Int): JavaStatement = + JavaStatement(referenceStatement.method, newIndex, referenceStatement.code, referenceStatement.cfg) +} + +abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) + extends IFDSProblem[Fact, Method, JavaStatement](new ForwardICFG()(project)) { + + override def needsPredecessor(statement: JavaStatement): Boolean = false + + /** + * Checks if the return flow is actually possible from the given exit statement to the given successor. + * This is used to filter flows of exceptions into normal code without being caught + * + * @param exit the exit statement of the returning method + * @param successor the successor statement of the call within the callee function + * @return whether successor might actually be the next statement after the exit statement + */ + protected def isPossibleReturnFlow(exit: JavaStatement, successor: JavaStatement): Boolean = { + (successor.node.isBasicBlock || successor.node.isNormalReturnExitNode) && + (exit.stmt.astID == Return.ASTID || exit.stmt.astID == ReturnValue.ASTID) || + (successor.node.isCatchNode || successor.node.isAbnormalReturnExitNode) && + (exit.stmt.astID != Return.ASTID && exit.stmt.astID != ReturnValue.ASTID) + } + + override def outsideAnalysisContext(callee: Method): Option[(JavaStatement, JavaStatement, Fact, Getter) => Set[Fact]] = callee.body.isDefined match { + case true => None + case false => Some((_: JavaStatement, _: JavaStatement, in: Fact, _: Getter) => Set(in)) + } +} + +object JavaIFDSProblem { + /** + * The type of the TAC domain. + */ + type V = DUVar[ValueInformation] + + /** + * Converts the index of a method's formal parameter to its tac index in the method's scope and + * vice versa. + * + * @param index The index of a formal parameter in the parameter list or of a variable. + * @param isStaticMethod States, whether the method is static. + * @return A tac index if a parameter index was passed or a parameter index if a tac index was + * passed. + */ + def switchParamAndVariableIndex(index: Int, isStaticMethod: Boolean): Int = + (if (isStaticMethod) -2 else -1) - index + + /** + * Gets the call object for a statement that contains a call. + * + * @param call The call statement. + * @return The call object for `call`. + */ + def asCall(call: Stmt[V]): Call[V] = call.astID match { + case Assignment.ASTID => call.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID => call.asExprStmt.expr.asFunctionCall + case _ => call.asMethodCall + } + + /** + * Checks whether the callee's formal parameter is of a reference type. + */ + def isRefTypeParam(callee: Method, index: Int): Boolean = + if (index == -1) true + else { + val parameterOffset = if (callee.isStatic) 0 else 1 + callee.descriptor.parameterType( + switchParamAndVariableIndex(index, callee.isStatic) + - parameterOffset + ).isReferenceType + } + +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala new file mode 100644 index 0000000000..f1beff596c --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala @@ -0,0 +1,363 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br._ +import org.opalj.br.analyses.SomeProject +import org.opalj.collection.immutable.EmptyIntTrieSet +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem => NewJavaIFDSProblem} +import org.opalj.tac._ +import org.opalj.value.ValueInformation + +import scala.annotation.tailrec +import org.opalj.fpcf.ifds.{AbstractIFDSFact, AbstractIFDSNullFact} +import org.opalj.fpcf.ifds.Dependees.Getter +import org.opalj.fpcf.FinalEP +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.si.PropertyStoreKey +import org.opalj.tac.fpcf.properties.cg.Callers + +trait VTAFact extends AbstractIFDSFact { + /** + * Checks, if this fact subsumes an `other` fact. + * + * @param other The other fact. + * @param project The analyzed project. + * @return True, if this fact subsumes the `other`fact + */ + def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = false +} +case object VTANullFact extends VTAFact with AbstractIFDSNullFact + +/** + * A possible run time type of a variable. + * + * @param definedBy The variable's definition site. + * @param t The variable's type. + * @param upperBound True, if the variable's type could also be every subtype of `t`. + */ +case class VariableType(definedBy: Int, t: ReferenceType, upperBound: Boolean) extends VTAFact { + + /** + * If this VariableType is an upper bound, it subsumes every subtype. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { + if (upperBound) other match { + case VariableType(definedByOther, tOther, _) if definedBy == definedByOther && t.isObjectType && tOther.isObjectType => + project.classHierarchy.isSubtypeOf(tOther.asObjectType, t.asObjectType) + case _ => false + } + else false + } +} + +/** + * A possible run time type of the receiver of a call. + * + * @param line The line of the call. + * @param t The callee's type. + * @param upperBound True, if the callee's type could also be every subtype of `t`. + */ +case class CalleeType(line: Int, t: ReferenceType, upperBound: Boolean) extends VTAFact { + + /** + * If this CalleeType is an upper bound, it subsumes every subtype. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { + if (upperBound) other match { + case CalleeType(lineOther, tOther, _) if line == lineOther && t.isObjectType && tOther.isObjectType => + tOther.asObjectType.isSubtypeOf(t.asObjectType)(project.classHierarchy) + case _ => false + } + else false + } +} + +class VariableTypeProblem(project: SomeProject, override val subsumeFacts: Boolean = false) extends JavaIFDSProblem[VTAFact](project) { + val propertyStore = project.get(PropertyStoreKey) + val declaredMethods = project.get(DeclaredMethodsKey) + + override def nullFact: VTAFact = VTANullFact + + /** + * The analysis starts with all public methods in java.lang or org.opalj. + */ + override def entryPoints: Seq[(Method, VTAFact)] = { + project.allProjectClassFiles + .filter(classInsideAnalysisContext) + .flatMap(classFile => classFile.methods) + .filter(isEntryPoint) + .flatMap(entryPointsForMethod) + } + + /** + * If a new object is instantiated and assigned to a variable or array, a new ValueType will be + * created for the assignment's target. + * If there is an assignment of a variable or array element, a new VariableType will be + * created for the assignment's target with the source's type. + * If there is a field read, a new VariableType will be created with the field's declared type. + */ + override def normalFlow( + statement: JavaStatement, + in: VTAFact, + predecessor: Option[JavaStatement] + ): Set[VTAFact] = { + val inSet = Set(in) + val stmt = statement.stmt + stmt.astID match { + case Assignment.ASTID => + // Add facts for the assigned variable. + inSet ++ newFacts(statement.method, statement.stmt.asAssignment.expr, statement.index, in) + case ArrayStore.ASTID => + /* +* Add facts for the array store, like it was a variable assignment. +* By doing so, we only want to get the variable's type. +* Then, we change the definedBy-index to the one of the array and wrap the variable's +* type with an array type. +* Note, that an array type may have at most 255 dimensions. +*/ + val flow = scala.collection.mutable.Set.empty[VTAFact] + flow ++= inSet + newFacts(statement.method, stmt.asArrayStore.value, statement.index, in).foreach { + case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) => + stmt.asArrayStore.arrayRef.asVar.definedBy + .foreach(flow += VariableType(_, ArrayType(t), upperBound)) + case _ => // Nothing to do + } + flow.toSet + // If the statement is neither an assignment, nor an array store, we just propagate our facts. + case _ => inSet + } + } + + /** + * For each variable, which can be passed as an argument to the call, a new VariableType is + * created for the callee context. + */ + override def callFlow( + call: JavaStatement, + callee: Method, + in: VTAFact + ): Set[VTAFact] = { + val inSet = Set(in) + val callObject = JavaIFDSProblem.asCall(call.stmt) + val allParams = callObject.allParams + // Iterate over all input facts and over all parameters of the call. + val flow = scala.collection.mutable.Set.empty[VTAFact] + inSet.foreach { + case VariableType(definedBy, t, upperBound) => + allParams.iterator.zipWithIndex.foreach { + /* +* We are only interested in a pair of a variable type and a parameter, if the +* variable and the parameter can refer to the same object. +*/ + case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) => + // If this is the case, create a new fact for the method's formal parameter. + flow += VariableType( + NewJavaIFDSProblem + .switchParamAndVariableIndex(parameterIndex, callee.isStatic), + t, + upperBound + ) + case _ => // Nothing to do + } + case _ => // Nothing to do + } + flow.toSet + } + + /** + * If the call is an instance call, new CalleeTypes will be created for the call, one for each + * VariableType, which could be the call's target. + */ + override def callToReturnFlow( + call: JavaStatement, + in: VTAFact, + successor: JavaStatement + ): Set[VTAFact] = { + val inSet = Set(in) + // Check, to which variables the callee may refer + val calleeDefinitionSites = JavaIFDSProblem.asCall(call.stmt).receiverOption + .map(callee => callee.asVar.definedBy) + .getOrElse(EmptyIntTrieSet) + val calleeTypeFacts = inSet.collect { + // If we know the variable's type, we also know on which type the call is performed. + case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) => + CalleeType(call.index, t, upperBound) + } + if (inSet.size >= calleeTypeFacts.size) inSet ++ calleeTypeFacts + else calleeTypeFacts ++ inSet + } + + /** + * If the call returns a value which is assigned to a variable, a new VariableType will be + * created in the caller context with the returned variable's type. + */ + override def returnFlow( + exit: JavaStatement, + in: VTAFact, + call: JavaStatement, + callFact: VTAFact, + successor: JavaStatement + ): Set[VTAFact] = + // We only create a new fact, if the call returns a value, which is assigned to a variable. + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val inSet = Set(in) + val returnValue = exit.stmt.asReturnValue.expr.asVar + inSet.collect { + // If we know the type of the return value, we create a fact for the assigned variable. + case VariableType(definedBy, t, upperBound) if returnValue.definedBy.contains(definedBy) => + VariableType(call.index, t, upperBound) + } + } else Set.empty + + /** + * Only methods in java.lang and org.opalj are inside the analysis context. + * + * @param callee The callee. + * @return True, if the callee is inside the analysis context. + */ + override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = + if (classInsideAnalysisContext(callee.classFile) && + super.outsideAnalysisContext(callee).isEmpty) + None + else { + Some(((call: JavaStatement, successor: JavaStatement, in: VTAFact, getter: Getter) => { + val returnType = callee.descriptor.returnType + if (call.stmt.astID == Assignment.ASTID && returnType.isReferenceType) { + Set(VariableType(call.index, returnType.asReferenceType, upperBound = true)) + } else Set.empty[VTAFact] + }): OutsideAnalysisContextHandler) + } + + /** + * When `normalFlow` reaches an assignment or array store, this method computes the new facts + * created by the statement. + * + * @param expression The source expression of the assignment or array store. + * @param statementIndex The statement's index. + * @param in The facts, which hold before the statement. + * @return The new facts created by the statement. + */ + private def newFacts( + method: Method, + expression: Expr[DUVar[ValueInformation]], + statementIndex: Int, + in: VTAFact + ): Iterator[VariableType] = { + val inSet = Set(in) + expression.astID match { + case New.ASTID => + inSet.iterator.collect { + // When a constructor is called, we always know the exact type. + case VTANullFact => + VariableType(statementIndex, expression.asNew.tpe, upperBound = false) + } + case Var.ASTID => + inSet.iterator.collect { + // When we know the source type, we also know the type of the assigned variable. + case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) => + VariableType(statementIndex, t, upperBound) + } + case ArrayLoad.ASTID => + inSet.iterator.collect { + // When we know the array's type, we also know the type of the loaded element. + case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && + expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) => + VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound) + } + case GetField.ASTID | GetStatic.ASTID => + val t = expression.asFieldRead.declaredFieldType + /* + * We do not track field types. So we must assume, that it contains any subtype of its + * compile time type. + */ + if (t.isReferenceType) + Iterator(VariableType(statementIndex, t.asReferenceType, upperBound = true)) + else Iterator.empty + case _ => Iterator.empty + } + } + + /** + * Checks, if some type is an array type containing an object type. + * + * @param t The type to be checked. + * @param includeObjectType If true, this method also returns true if `t` is an object type + * itself. + * + * @return True, if `t` is an array type of an object type. + */ + @tailrec private def isArrayOfObjectType( + t: FieldType, + includeObjectType: Boolean = false + ): Boolean = { + if (t.isArrayType) isArrayOfObjectType(t.asArrayType.elementType, includeObjectType = true) + else if (t.isObjectType && includeObjectType) true + else false + } + + /** + * Checks, if a class is inside the analysis context. + * By default, that are the packages java.lang and org.opalj. + * + * @param classFile The class, which is checked. + * @return True, if the class is inside the analysis context. + */ + private def classInsideAnalysisContext(classFile: ClassFile): Boolean = { + val fqn = classFile.fqn + fqn.startsWith("java/lang") || fqn.startsWith("org/opalj/fpcf/fixtures/vta") + } + + /** + * Checks, if a method is an entry point of this analysis. + * + * @param method The method to be checked. + * @return True, if this method is an entry point of the analysis. + */ + private def isEntryPoint(method: Method): Boolean = { + method.body.isDefined && canBeCalledFromOutside(method) + } + + /** + * Checks, if some `method` can be called from outside the library. + * The call graph must be computed, before this method may be invoked. + * + * @param method The method, which may be callable from outside. + * @return True, if `method` can be called from outside the library. + */ + private def canBeCalledFromOutside(method: Method): Boolean = { + val FinalEP(_, callers) = propertyStore(declaredMethods(method), Callers.key) + callers.hasCallersWithUnknownContext + } + + /** + * For an entry point method, this method computes all pairs (`method`, inputFact) where + * inputFact is a VariableType for one of the method's parameter with its compile time type as + * an upper bound. + * + * @param method The entry point method. + * + * @return All pairs (`method`, inputFact) where inputFact is a VariableType for one of the + * method's parameter with its compile time type as an upper bound. + */ + private def entryPointsForMethod(method: Method): Seq[(Method, VTAFact)] = { + // Iterate over all parameters, which have a reference type. + (method.descriptor.parameterTypes.zipWithIndex.collect { + case (t, index) if t.isReferenceType => + /* +* Create a fact for the parameter, which says, that the parameter may have any +* subtype of its compile time type. +*/ + VariableType( + NewJavaIFDSProblem.switchParamAndVariableIndex(index, method.isStatic), + t.asReferenceType, + upperBound = true + ) + /* +* In IFDS problems, we must always also analyze the null fact, because it creates the facts, +* which hold independently of other source facts. +* Map the input facts, in which we are interested, to a pair of the method and the fact. +*/ + } :+ VTANullFact).map(fact => (method, fact)) + } +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala new file mode 100644 index 0000000000..8600220c14 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -0,0 +1,314 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.br.Method +import org.opalj.br.analyses.{DeclaredMethodsKey, SomeProject} +import org.opalj.fpcf.ifds.Dependees.Getter +import org.opalj.tac._ +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} + +abstract class ForwardTaintProblem(project: SomeProject) + extends JavaIFDSProblem[TaintFact](project) + with TaintProblem[Method, JavaStatement, TaintFact] { + val declaredMethods = project.get(DeclaredMethodsKey) + override def nullFact: TaintFact = TaintNullFact + + override def needsPredecessor(statement: JavaStatement): Boolean = false + + /** + * If a variable gets assigned a tainted value, the variable will be tainted. + */ + override def normalFlow(statement: JavaStatement, in: TaintFact, predecessor: Option[JavaStatement]): Set[TaintFact] = { + statement.stmt.astID match { + case Assignment.ASTID => + Set(in) ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) + case ArrayStore.ASTID => + val store = statement.stmt.asArrayStore + val definedBy = store.arrayRef.asVar.definedBy + val arrayIndex = TaintProblem.getIntConstant(store.index, statement.code) + if (isTainted(store.value, in)) { + if (arrayIndex.isDefined) { + // Taint a known array index + Set(in) ++ definedBy.map { ArrayElement(_, arrayIndex.get) } + } else + // Taint the whole array if the index is unknown + Set(in) ++ definedBy.map { Variable(_) } + } else if (arrayIndex.isDefined && definedBy.size == 1 && in == ArrayElement(definedBy.head, arrayIndex.get)) { + // untaint + Set() + } else Set(in) + case PutField.ASTID => + val put = statement.stmt.asPutField + val definedBy = put.objRef.asVar.definedBy + if (isTainted(put.value, in)) + Set(in) ++ definedBy.map { InstanceField(_, put.declaringClass, put.name) } + else + Set(in) + case PutStatic.ASTID => + val put = statement.stmt.asPutStatic + if (isTainted(put.value, in)) + Set(in, StaticField(put.declaringClass, put.name)) + else + Set(in) + case _ => Set(in) + } + } + + /** + * Propagates tainted parameters to the callee. If a call to the sink method with a tainted + * parameter is detected, no call-to-start + * edges will be created. + */ + override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = { + val callObject = JavaIFDSProblem.asCall(call.stmt) + val allParams = callObject.allParams + + val allParamsWithIndices = allParams.zipWithIndex + in match { + // Taint formal parameter if actual parameter is tainted + case Variable(index) => + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) => + Some(Variable(JavaIFDSProblem.switchParamAndVariableIndex( + paramIndex, + callee.isStatic + ))) + case _ => None // Nothing to do + }.toSet + + // Taint element of formal parameter if element of actual parameter is tainted + case ArrayElement(index, taintedIndex) => + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) => + Some(ArrayElement( + JavaIFDSProblem.switchParamAndVariableIndex(paramIndex, callee.isStatic), + taintedIndex + )) + case _ => None // Nothing to do + }.toSet + + case InstanceField(index, declClass, taintedField) => + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.flatMap { + case (param, pIndex) if param.asVar.definedBy.contains(index) && + (JavaIFDSProblem.switchParamAndVariableIndex(pIndex, callee.isStatic) != -1 || + project.classHierarchy.isSubtypeOf(declClass, declaredMethods(callee).declaringClassType)) => + Some(InstanceField( + JavaIFDSProblem.switchParamAndVariableIndex(pIndex, callee.isStatic), + declClass, taintedField + )) + case _ => None // Nothing to do + }.toSet + + case sf: StaticField => Set(sf) + + case _ => Set() // Nothing to do + + } + } + + /** + * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. + * If the callee's return value was tainted and it is assigned to a variable in the callee, the + * variable will be tainted. + * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds + * at this method. + * Creates new taints and FlowFacts, if necessary. + * If the sanitize method was called, nothing will be tainted. + */ + override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { + if (!isPossibleReturnFlow(exit, successor)) return Set.empty + + val callee = exit.callable + if (sanitizesReturnValue(callee)) return Set.empty + val callStatement = JavaIFDSProblem.asCall(call.stmt) + val allParams = callStatement.allParams + var flows: Set[TaintFact] = Set.empty + in match { + // Taint actual parameter if formal parameter is tainted + case Variable(index) if index < 0 && index > -100 && JavaIFDSProblem.isRefTypeParam(callee, index) => + val param = allParams( + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(Variable) + + // Taint element of actual parameter if element of formal parameter is tainted + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 => + val param = allParams( + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) + + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 => + // Taint field of actual parameter if field of formal parameter is tainted + val param = + allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic)) + param.asVar.definedBy.foreach { defSite => + flows += InstanceField(defSite, declClass, taintedField) + } + + case sf: StaticField => flows += sf + + // Track the call chain to the sink back + case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) => + flows += FlowFact(JavaMethod(call.method) +: flow) + case _ => + } + + // Propagate taints of the return value + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + in match { + case Variable(index) if returnValueDefinedBy.contains(index) => + flows += Variable(call.index) + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) => + flows += ArrayElement(call.index, taintedIndex) + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) => + flows += InstanceField(call.index, declClass, taintedField) + case TaintNullFact => + val taints = createTaints(callee, call) + if (taints.nonEmpty) flows ++= taints + case _ => // Nothing to do + } + } + val flowFact = createFlowFact(callee, call, in) + if (flowFact.isDefined) flows += flowFact.get + + flows + } + + /** + * Removes taints according to `sanitizesParameter`. + */ + override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = + if (sanitizesParameter(call, in)) Set() else Set(in) + + /** + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a variable. + * Creates a taint, if necessary. + * + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. + */ + protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] + + /** + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFact(callee: Method, call: JavaStatement, + in: TaintFact): Option[FlowFact] + + /** + * If a parameter is tainted, the result will also be tainted. + * We assume that the callee does not call the source method. + */ + override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { + super.outsideAnalysisContext(callee) match { + case Some(_) => Some(((call: JavaStatement, successor: JavaStatement, in: TaintFact, _: Getter) => { + val allParams = JavaIFDSProblem.asCall(call.stmt).receiverOption ++ JavaIFDSProblem.asCall(call.stmt).params + if (call.stmt.astID == Assignment.ASTID && (in match { + case Variable(index) => + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) => true + case _ => false + } + case ArrayElement(index, _) => + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) => true + case _ => false + } + case _ => false + })) Set(Variable(call.index)) + else Set.empty + }): OutsideAnalysisContextHandler) + case None => None + } + + } + + /** + * Creates new facts for an assignment. A new fact for the assigned variable will be created, + * if the expression contains a tainted variable + * + * @param expression The source expression of the assignment + * @param statement The assignment statement + * @param in The incoming facts + * @return The new facts, created by the assignment + */ + private def createNewTaints(expression: Expr[V], statement: JavaStatement, in: TaintFact): Set[TaintFact] = { + expression.astID match { + case Var.ASTID => + val definedBy = expression.asVar.definedBy + in match { + case Variable(index) if definedBy.contains(index) => + Set(Variable(statement.index)) + case _ => Set() + } + case ArrayLoad.ASTID => + val loadExpression = expression.asArrayLoad + val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy + if (in match { + // One specific array element may be tainted + case ArrayElement(index, taintedElement) => + val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) + arrayDefinedBy.contains(index) && + (loadedIndex.isEmpty || taintedElement == loadedIndex.get) + // Or the whole array + case Variable(index) => arrayDefinedBy.contains(index) + case _ => false + }) Set(Variable(statement.index)) + else + Set.empty + case GetField.ASTID => + val get = expression.asGetField + val objectDefinedBy = get.objRef.asVar.definedBy + if (in match { + // The specific field may be tainted + case InstanceField(index, _, taintedField) => + taintedField == get.name && objectDefinedBy.contains(index) + // Or the whole object + case Variable(index) => objectDefinedBy.contains(index) + case _ => false + }) + Set(Variable(statement.index)) + else + Set.empty + case GetStatic.ASTID => + val get = expression.asGetStatic + if (in == StaticField(get.declaringClass, get.name)) + Set(Variable(statement.index)) + else Set.empty + case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | + NewArray.ASTID | ArrayLength.ASTID => + (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) => + acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) + case _ => Set.empty + } + } + + /** + * Checks, if the result of some variable expression could be tainted. + * + * @param expression The variable expression. + * @param in The current data flow facts. + * @return True, if the expression could be tainted + */ + private def isTainted(expression: Expr[V], in: TaintFact): Boolean = { + val definedBy = expression.asVar.definedBy + expression.isVar && (in match { + case Variable(index) => definedBy.contains(index) + case ArrayElement(index, _) => definedBy.contains(index) + case InstanceField(index, _, _) => definedBy.contains(index) + case _ => false + }) + } +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala new file mode 100644 index 0000000000..a5d476a7f1 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -0,0 +1,111 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.br.ObjectType +import org.opalj.fpcf.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} +import org.opalj.tac.{Assignment, Expr, Stmt} +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V + +trait TaintFact extends AbstractIFDSFact + +case object TaintNullFact extends TaintFact with AbstractIFDSNullFact + +/** + * A tainted variable. + * + * @param index The variable's definition site. + */ +case class Variable(index: Int) extends TaintFact + +/** + * A tainted array element. + * + * @param index The array's definition site. + * @param element The index of the tainted element in the array. + */ +case class ArrayElement(index: Int, element: Int) extends TaintFact + +/** + * A tainted static field. + * + * @param classType The field's class. + * @param fieldName The field's name. + */ +case class StaticField(classType: ObjectType, fieldName: String) extends TaintFact + +/** + * A tainted instance field. + * + * @param index The definition site of the field's value. + * @param classType The field's type. + * @param fieldName The field's value. + */ +case class InstanceField(index: Int, classType: ObjectType, fieldName: String) extends TaintFact + +/** + * A path of method calls, originating from the analyzed method, over which a tainted variable + * reaches the sink. + * + * @param flow A sequence of method calls, originating from but not including this method. + */ +case class FlowFact(flow: Seq[Callable]) extends TaintFact { + override val hashCode: Int = { + var r = 1 + flow.foreach(f => r = (r + f.hashCode()) * 31) + r + } +} + +trait TaintProblem[C, Statement, IFDSFact] { + + /** + * Checks, if some `callee` is a sanitizer, which sanitizes its return value. + * In this case, no return flow facts will be created. + * + * @param callee The method, which was called. + * @return True, if the method is a sanitizer. + */ + protected def sanitizesReturnValue(callee: C): Boolean + + /** + * Called in callToReturnFlow. This method can return whether the input fact + * will be removed after `callee` was called. I.e. the method could sanitize parameters. + * + * @param call The call statement. + * @param in The fact which holds before the call. + * @return Whether in will be removed after the call. + */ + protected def sanitizesParameter(call: Statement, in: IFDSFact): Boolean +} + +object TaintProblem { + + /** + * Checks, if some expression always evaluates to the same int constant. + * + * @param expression The expression. + * @param code The TAC code, which contains the expression. + * @return Some int, if this analysis is sure that `expression` always evaluates to the same int + * constant, None otherwise. + */ + def getIntConstant(expression: Expr[V], code: Array[Stmt[V]]): Option[Int] = { + if (expression.isIntConst) Some(expression.asIntConst.value) + else if (expression.isVar) { + val definedByIterator = expression.asVar.definedBy.iterator + var allDefinedByWereConstant = true + var result = scala.collection.mutable.Seq.empty[Int] + while (definedByIterator.hasNext && allDefinedByWereConstant) { + val definedBy = definedByIterator.next() + if (definedBy >= 0) { + val stmt = code(definedBy) + if (stmt.astID == Assignment.ASTID && stmt.asAssignment.expr.isIntConst) + result :+= stmt.asAssignment.expr.asIntConst.value + else allDefinedByWereConstant = false + } else allDefinedByWereConstant = false + } + if (allDefinedByWereConstant && result.tail.forall(_ == result.head)) + Some(result.head) + else None + } else None + } +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToAnalysis.scala index d4729caa7d..e72c304114 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToAnalysis.scala @@ -6,7 +6,6 @@ package analyses package pointsto import scala.collection.mutable.ArrayBuffer - import org.opalj.log.OPALLogger.logOnce import org.opalj.log.Warn import org.opalj.collection.immutable.IntTrieSet @@ -38,14 +37,14 @@ import org.opalj.br.FieldType import org.opalj.br.ObjectType import org.opalj.br.ArrayType import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.VirtualFormalParametersKey -import org.opalj.br.fpcf.FPCFAnalysis import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.br.fpcf.FPCFTriggeredAnalysisScheduler import org.opalj.br.DeclaredMethod +import org.opalj.br.fpcf.JavaFPCFTriggeredAnalysisScheduler import org.opalj.br.fpcf.properties.Context +import org.opalj.si.FPCFAnalysis import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSitesKey @@ -591,13 +590,13 @@ trait AbstractPointsToAnalysis extends PointsToAnalysisBase with ReachableMethod } } -trait AbstractPointsToAnalysisScheduler extends FPCFTriggeredAnalysisScheduler { +trait AbstractPointsToAnalysisScheduler extends JavaFPCFTriggeredAnalysisScheduler { def propertyKind: PropertyMetaInformation def createAnalysis: SomeProject => AbstractPointsToAnalysis override type InitializationData = Null - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, VirtualFormalParametersKey, DefinitionSitesKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToBasedAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToBasedAnalysis.scala index 763a310965..97ba77ea1b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToBasedAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/AbstractPointsToBasedAnalysis.scala @@ -10,7 +10,7 @@ import org.opalj.fpcf.EOptionP import org.opalj.fpcf.PropertyKey import org.opalj.br.analyses.VirtualFormalParameters import org.opalj.br.analyses.VirtualFormalParametersKey -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.properties.pointsto.PointsToSetLike import org.opalj.br.ReferenceType import org.opalj.tac.common.DefinitionSites @@ -24,7 +24,7 @@ import org.opalj.tac.fpcf.analyses.cg.TypeIterator * @author Dominik Helm * @author Florian Kuebler */ -trait AbstractPointsToBasedAnalysis extends FPCFAnalysis with ContextualAnalysis { +trait AbstractPointsToBasedAnalysis extends ProjectBasedAnalysis with ContextualAnalysis { protected[this] type ElementType protected[this] type PointsToSet >: Null <: PointsToSetLike[ElementType, _, PointsToSet] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ArraycopyPointsToAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ArraycopyPointsToAnalysis.scala index 6d7ca8e138..1eb815bf02 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ArraycopyPointsToAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ArraycopyPointsToAnalysis.scala @@ -21,13 +21,13 @@ import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType import org.opalj.br.VoidType import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.VirtualFormalParametersKey import org.opalj.br.fpcf.properties.pointsto.AllocationSitePointsToSet -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.br.fpcf.properties.pointsto.TypeBasedPointsToSet -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.analyses.cg.V @@ -90,21 +90,21 @@ abstract class ArraycopyPointsToAnalysis private[pointsto] ( } } -trait ArraycopyPointsToAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { +trait ArraycopyPointsToAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { val propertyKind: PropertyMetaInformation val createAnalysis: SomeProject => ArraycopyPointsToAnalysis override type InitializationData = Null - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, VirtualFormalParametersKey, DefinitionSitesKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs(Callers, propertyKind) override def derivesCollaboratively: Set[PropertyBounds] = PropertyBounds.ubs(propertyKind) - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = createAnalysis(p) ps.scheduleEagerComputationForEntity(p)(analysis.process) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ConfiguredMethodsPointsToAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ConfiguredMethodsPointsToAnalysis.scala index b5ee9b623c..06233777aa 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ConfiguredMethodsPointsToAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ConfiguredMethodsPointsToAnalysis.scala @@ -21,8 +21,6 @@ import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.Results import org.opalj.fpcf.SomeEPS import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFTriggeredAnalysisScheduler import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey @@ -36,7 +34,9 @@ import org.opalj.br.analyses.VirtualFormalParametersKey import org.opalj.br.fpcf.properties.pointsto.PointsToSetLike import org.opalj.br.ArrayType import org.opalj.br.ReferenceType -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys +import org.opalj.br.fpcf.JavaFPCFTriggeredAnalysisScheduler +import org.opalj.si.FPCFAnalysis import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.analyses.cg.SimpleContextProvider @@ -294,13 +294,13 @@ abstract class ConfiguredMethodsPointsToAnalysis private[analyses] ( } } -trait ConfiguredMethodsPointsToAnalysisScheduler extends FPCFTriggeredAnalysisScheduler { +trait ConfiguredMethodsPointsToAnalysisScheduler extends JavaFPCFTriggeredAnalysisScheduler { def propertyKind: PropertyMetaInformation def createAnalysis: SomeProject => ConfiguredMethodsPointsToAnalysis override type InitializationData = Null - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, VirtualFormalParametersKey, DefinitionSitesKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/LibraryPointsToAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/LibraryPointsToAnalysis.scala index 69c5d9ed4f..88d63cc205 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/LibraryPointsToAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/LibraryPointsToAnalysis.scala @@ -19,19 +19,20 @@ import org.opalj.br.fpcf.properties.pointsto.AllocationSitePointsToSet import org.opalj.br.fpcf.properties.pointsto.TypeBasedPointsToSet import org.opalj.br.Field import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.ArrayType import org.opalj.br.ReferenceType import org.opalj.br.analyses.cg.ClosedPackagesKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey import org.opalj.br.Type import org.opalj.br.analyses.SomeProject import org.opalj.br.analyses.cg.InitialEntryPointsKey import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.VirtualFormalParametersKey +import org.opalj.br.fpcf.JavaFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.properties.NoContext -import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler +import org.opalj.si.FPCFAnalysis import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.fpcf.properties.cg.Callers @@ -233,12 +234,12 @@ abstract class LibraryPointsToAnalysis( final val project: SomeProject) } -trait LibraryPointsToAnalysisScheduler extends FPCFEagerAnalysisScheduler { +trait LibraryPointsToAnalysisScheduler extends JavaFPCFEagerAnalysisScheduler { val propertyKind: PropertyMetaInformation val createAnalysis: SomeProject => LibraryPointsToAnalysis - override def requiredProjectInformation: ProjectInformationKeys = Seq( + override def requiredProjectInformation: JavaProjectInformationKeys = Seq( TypeIteratorKey, ClosedPackagesKey, DeclaredMethodsKey, InitialEntryPointsKey, VirtualFormalParametersKey, InitialInstantiatedTypesKey @@ -252,7 +253,7 @@ trait LibraryPointsToAnalysisScheduler extends FPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, analysis: LibraryPointsToAnalysis): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, analysis: LibraryPointsToAnalysis): ProjectBasedAnalysis = { analysis } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/NewInstanceAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/NewInstanceAnalysis.scala index fd530c5297..5a19e85308 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/NewInstanceAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/NewInstanceAnalysis.scala @@ -25,13 +25,13 @@ import org.opalj.br.ReferenceType import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.fpcf.properties.pointsto.AllocationSitePointsToSet -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.fpcf.properties.pointsto.TypeBasedPointsToSet import org.opalj.br.ArrayType +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSite import org.opalj.tac.common.DefinitionSitesKey @@ -195,11 +195,11 @@ abstract class NewInstanceMethodAnalysis( } } -trait NewInstanceAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { +trait NewInstanceAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { def propertyKind: PropertyMetaInformation def createAnalysis: SomeProject => NewInstanceAnalysis - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, DefinitionSitesKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs(Callees, propertyKind) @@ -208,7 +208,7 @@ trait NewInstanceAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = createAnalysis(p) ps.scheduleEagerComputationForEntity(p)(analysis.process) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ReflectionAllocationsAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ReflectionAllocationsAnalysis.scala index f9e65fc768..0ddbf70328 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ReflectionAllocationsAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/ReflectionAllocationsAnalysis.scala @@ -14,12 +14,12 @@ import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.BooleanType import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.VirtualFormalParametersKey import org.opalj.br.fpcf.properties.pointsto.PointsToSetLike -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.properties.pointsto.AllocationSitePointsToSet import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSitesKey @@ -34,7 +34,7 @@ import scala.collection.immutable.ArraySeq */ class ReflectionAllocationsAnalysis private[analyses] ( final val project: SomeProject -) extends FPCFAnalysis { +) extends ProjectBasedAnalysis { val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) @@ -185,8 +185,8 @@ class ReflectionMethodAllocationsAnalysis( } } -object ReflectionAllocationsAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = +object ReflectionAllocationsAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, VirtualFormalParametersKey, DefinitionSitesKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs(Callers, AllocationSitePointsToSet) @@ -196,7 +196,7 @@ object ReflectionAllocationsAnalysisScheduler extends BasicFPCFEagerAnalysisSche override def derivesEagerly: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = new ReflectionAllocationsAnalysis(p) ps.scheduleEagerComputationForEntity(p)(analysis.process) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/TamiFlexKey.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/TamiFlexKey.scala index 2f34f61732..0bb10dfd38 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/TamiFlexKey.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/TamiFlexKey.scala @@ -15,10 +15,10 @@ import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.SomeProject import org.opalj.br.MethodDescriptor import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.ProjectInformationKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.FieldType import org.opalj.br.ReferenceType +import org.opalj.br.analyses.JavaProjectInformationKey /** * Container class, to represent a tamiflex log: @@ -86,10 +86,10 @@ class TamiFlexLogData( * * @author Florian Kuebler */ -object TamiFlexKey extends ProjectInformationKey[TamiFlexLogData, Nothing] { +object TamiFlexKey extends JavaProjectInformationKey[TamiFlexLogData, Nothing] { val configKey = "org.opalj.tac.fpcf.analyses.pointsto.TamiFlex.logFile" - override def requirements(project: SomeProject): ProjectInformationKeys = Seq(DeclaredMethodsKey) + override def requirements(project: SomeProject): JavaProjectInformationKeys = Seq(DeclaredMethodsKey) override def compute(project: SomeProject): TamiFlexLogData = { implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/TamiFlexPointsToAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/TamiFlexPointsToAnalysis.scala index 5d9edc18fb..42e48b5f4e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/TamiFlexPointsToAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/TamiFlexPointsToAnalysis.scala @@ -19,17 +19,17 @@ import org.opalj.br.ArrayType import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.IntegerType import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.br.fpcf.properties.pointsto.AllocationSitePointsToSet -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.br.BooleanType import org.opalj.br.ReferenceType import org.opalj.br.VoidType import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.VirtualFormalParametersKey +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.br.fpcf.properties.pointsto.PointsToSetLike import org.opalj.br.fpcf.properties.pointsto.TypeBasedPointsToSet import org.opalj.tac.cg.TypeIteratorKey @@ -186,12 +186,12 @@ abstract class TamiFlexPointsToAnalysis private[analyses] ( } } -trait TamiFlexPointsToAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { +trait TamiFlexPointsToAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { val propertyKind: PropertyMetaInformation val createAnalysis: SomeProject => TamiFlexPointsToAnalysis - override def requiredProjectInformation: ProjectInformationKeys = Seq( + override def requiredProjectInformation: JavaProjectInformationKeys = Seq( DeclaredMethodsKey, VirtualFormalParametersKey, DefinitionSitesKey, @@ -205,7 +205,7 @@ trait TamiFlexPointsToAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler override def derivesEagerly: Set[PropertyBounds] = Set.empty - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = createAnalysis(p) ps.scheduleEagerComputationForEntity(p)(analysis.process) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/UnsafePointsToAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/UnsafePointsToAnalysis.scala index e9d6dbad9f..f2231284b7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/UnsafePointsToAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/pointsto/UnsafePointsToAnalysis.scala @@ -19,11 +19,10 @@ import org.opalj.br.LongType import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.VirtualFormalParametersKey -import org.opalj.br.fpcf.BasicFPCFEagerAnalysisScheduler import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.properties.pointsto.AllocationSitePointsToSet import org.opalj.br.fpcf.properties.pointsto.TypeBasedPointsToSet import org.opalj.br.BooleanType @@ -31,6 +30,7 @@ import org.opalj.br.IntegerType import org.opalj.br.ReferenceType import org.opalj.br.VoidType import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.fpcf.JavaBasicFPCFEagerAnalysisScheduler import org.opalj.tac.cg.TypeIteratorKey import org.opalj.tac.common.DefinitionSitesKey import org.opalj.tac.fpcf.analyses.cg.V @@ -246,21 +246,21 @@ abstract class UnsafePutPointsToAnalysis( } } -trait UnsafePointsToAnalysisScheduler extends BasicFPCFEagerAnalysisScheduler { +trait UnsafePointsToAnalysisScheduler extends JavaBasicFPCFEagerAnalysisScheduler { val propertyKind: PropertyMetaInformation val createAnalysis: SomeProject => UnsafePointsToAnalysis override type InitializationData = Null - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, VirtualFormalParametersKey, DefinitionSitesKey, TypeIteratorKey) override def uses: Set[PropertyBounds] = PropertyBounds.ubs(Callers, propertyKind) override def derivesCollaboratively: Set[PropertyBounds] = PropertyBounds.ubs(propertyKind) - override def start(p: SomeProject, ps: PropertyStore, unused: Null): FPCFAnalysis = { + override def start(p: SomeProject, ps: PropertyStore, unused: Null): ProjectBasedAnalysis = { val analysis = createAnalysis(p) ps.scheduleEagerComputationForEntity(p)(analysis.process) analysis diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis.scala index 92c8f525fb..dc4bbb12a9 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/AbstractPurityAnalysis.scala @@ -43,7 +43,7 @@ import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.analyses.ConfiguredPurity import org.opalj.br.fpcf.analyses.ConfiguredPurityKey import org.opalj.br.fpcf.properties.Context @@ -63,7 +63,7 @@ import org.opalj.tac.fpcf.properties.TACAI * * Provides types and methods needed for purity analyses. */ -trait AbstractPurityAnalysis extends FPCFAnalysis { +trait AbstractPurityAnalysis extends ProjectBasedAnalysis { /** The type of the TAC domain. */ type V = DUVar[ValueInformation] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L1PurityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L1PurityAnalysis.scala index da5388bb56..2e81ab77cb 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L1PurityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L1PurityAnalysis.scala @@ -6,7 +6,6 @@ package analyses package purity import net.ceedubs.ficus.Ficus._ - import org.opalj.collection.immutable.EmptyIntTrieSet import org.opalj.collection.immutable.IntTrieSet import org.opalj.fpcf.Entity @@ -29,7 +28,7 @@ import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.SomeProject -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.cfg.CFG import org.opalj.br.fpcf.properties.ClassifiedImpure import org.opalj.br.fpcf.properties.ClassImmutability @@ -42,11 +41,8 @@ import org.opalj.br.fpcf.properties.Pure import org.opalj.br.fpcf.properties.SideEffectFree import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.br.fpcf.properties.VirtualMethodPurity -import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFAnalysisScheduler -import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler import org.opalj.br.fpcf.analyses.ConfiguredPurityKey import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers @@ -55,6 +51,8 @@ import org.opalj.br.fpcf.properties.Context import org.opalj.br.fpcf.properties.SimpleContext import org.opalj.br.fpcf.properties.SimpleContextsKey import org.opalj.ai.isImmediateVMException +import org.opalj.br.fpcf.{JavaFPCFAnalysisScheduler, JavaFPCFEagerAnalysisScheduler, JavaFPCFLazyAnalysisScheduler} +import org.opalj.si.FPCFAnalysis import org.opalj.tac.cg.CallGraphKey import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.fpcf.properties.cg.NoCallers @@ -426,11 +424,11 @@ object L1PurityAnalysis { } } -trait L1PurityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L1PurityAnalysisScheduler extends JavaFPCFAnalysisScheduler { final def derivedProperty: PropertyBounds = PropertyBounds.lub(Purity) - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, SimpleContextsKey, ConfiguredPurityKey) override def uses: Set[PropertyBounds] = { @@ -459,14 +457,14 @@ trait L1PurityAnalysisScheduler extends FPCFAnalysisScheduler { } -object EagerL1PurityAnalysis extends L1PurityAnalysisScheduler with FPCFEagerAnalysisScheduler { +object EagerL1PurityAnalysis extends L1PurityAnalysisScheduler with JavaFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = super.requiredProjectInformation :+ CallGraphKey override def start( p: SomeProject, ps: PropertyStore, analysis: InitializationData - ): FPCFAnalysis = { + ): ProjectBasedAnalysis = { val cg = p.get(CallGraphKey) val methods = cg.reachableMethods().collect { case c @ Context(dm) if dm.hasSingleDefinedMethod && dm.definedMethod.body.isDefined && !analysis.configuredPurity.wasSet(dm) && ps(dm, Callers.key).ub != NoCallers => @@ -486,11 +484,11 @@ object EagerL1PurityAnalysis extends L1PurityAnalysisScheduler with FPCFEagerAna override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } -object LazyL1PurityAnalysis extends L1PurityAnalysisScheduler with FPCFLazyAnalysisScheduler { +object LazyL1PurityAnalysis extends L1PurityAnalysisScheduler with JavaFPCFLazyAnalysisScheduler { override def register( p: SomeProject, ps: PropertyStore, analysis: InitializationData - ): FPCFAnalysis = { + ): ProjectBasedAnalysis = { ps.registerLazyPropertyComputation(Purity.key, analysis.doDeterminePurity) analysis } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis.scala index b7a14b2e2b..0917e2863e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/purity/L2PurityAnalysis.scala @@ -6,11 +6,8 @@ package analyses package purity import scala.annotation.switch - import scala.collection.immutable.IntMap - import net.ceedubs.ficus.Ficus._ - import org.opalj.collection.immutable.EmptyIntTrieSet import org.opalj.collection.immutable.IntTrieSet import org.opalj.fpcf.EOptionP @@ -33,7 +30,7 @@ import org.opalj.br.Field import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.JavaProjectInformationKeys import org.opalj.br.analyses.SomeProject import org.opalj.br.cfg.CFG import org.opalj.br.fpcf.properties.ClassifiedImpure @@ -61,11 +58,8 @@ import org.opalj.br.fpcf.properties.TypeImmutability import org.opalj.br.fpcf.properties.UsesConstantDataOnly import org.opalj.br.fpcf.properties.UsesNoStaticData import org.opalj.br.fpcf.properties.UsesVaryingData -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.fpcf.FPCFEagerAnalysisScheduler -import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.br.analyses.ProjectBasedAnalysis import org.opalj.br.fpcf.properties.Purity -import org.opalj.br.fpcf.FPCFAnalysisScheduler import org.opalj.br.fpcf.analyses.ConfiguredPurityKey import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.properties.cg.Callers @@ -74,6 +68,8 @@ import org.opalj.br.fpcf.properties.Context import org.opalj.br.fpcf.properties.SimpleContext import org.opalj.br.fpcf.properties.SimpleContextsKey import org.opalj.ai.isImmediateVMException +import org.opalj.br.fpcf.{JavaFPCFAnalysisScheduler, JavaFPCFEagerAnalysisScheduler, JavaFPCFLazyAnalysisScheduler} +import org.opalj.si.FPCFAnalysis import org.opalj.tac.cg.CallGraphKey import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.fpcf.properties.cg.NoCallers @@ -950,11 +946,11 @@ object L2PurityAnalysis { } } -trait L2PurityAnalysisScheduler extends FPCFAnalysisScheduler { +trait L2PurityAnalysisScheduler extends JavaFPCFAnalysisScheduler { final def derivedProperty: PropertyBounds = PropertyBounds.lub(Purity) - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(DeclaredMethodsKey, SimpleContextsKey, ConfiguredPurityKey) override def uses: Set[PropertyBounds] = { @@ -988,14 +984,14 @@ trait L2PurityAnalysisScheduler extends FPCFAnalysisScheduler { } -object EagerL2PurityAnalysis extends L2PurityAnalysisScheduler with FPCFEagerAnalysisScheduler { +object EagerL2PurityAnalysis extends L2PurityAnalysisScheduler with JavaFPCFEagerAnalysisScheduler { - override def requiredProjectInformation: ProjectInformationKeys = + override def requiredProjectInformation: JavaProjectInformationKeys = super.requiredProjectInformation :+ CallGraphKey override def start( p: SomeProject, ps: PropertyStore, analysis: InitializationData - ): FPCFAnalysis = { + ): ProjectBasedAnalysis = { val cg = p.get(CallGraphKey) val methods = cg.reachableMethods().collect { case c @ Context(dm) if dm.hasSingleDefinedMethod && dm.definedMethod.body.isDefined && !analysis.configuredPurity.wasSet(dm) && ps(dm, Callers.key).ub != NoCallers => @@ -1016,11 +1012,11 @@ object EagerL2PurityAnalysis extends L2PurityAnalysisScheduler with FPCFEagerAna override def derivesCollaboratively: Set[PropertyBounds] = Set.empty } -object LazyL2PurityAnalysis extends L2PurityAnalysisScheduler with FPCFLazyAnalysisScheduler { +object LazyL2PurityAnalysis extends L2PurityAnalysisScheduler with JavaFPCFLazyAnalysisScheduler { override def register( p: SomeProject, ps: PropertyStore, analysis: InitializationData - ): FPCFAnalysis = { + ): ProjectBasedAnalysis = { ps.registerLazyPropertyComputation(Purity.key, analysis.doDeterminePurity) analysis } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala deleted file mode 100644 index 07bd837351..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala +++ /dev/null @@ -1,37 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package tac -package fpcf -package properties - -import org.opalj.fpcf.Property -import org.opalj.fpcf.PropertyMetaInformation -import org.opalj.value.KnownTypedValue -import org.opalj.tac.fpcf.analyses.Statement - -trait IFDSPropertyMetaInformation[DataFlowFact] extends PropertyMetaInformation - -abstract class IFDSProperty[DataFlowFact] - extends Property - with IFDSPropertyMetaInformation[DataFlowFact] { - - /** The type of the TAC domain. */ - type V = DUVar[KnownTypedValue] - - /** - * Maps exit statements to the data flow facts, which hold after them. - */ - def flows: Map[Statement, Set[DataFlowFact]] - - override def equals(other: Any): Boolean = other match { - case that: IFDSProperty[DataFlowFact @unchecked] => - // We cached the "hashCode" to make the following comparison more efficient; - // note that all properties are eventually added to some set and therefore - // the hashCode is required anyway! - (this eq that) || (this.hashCode == that.hashCode && this.flows == that.flows) - case _ => - false - } - - override lazy val hashCode: Int = flows.hashCode() -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala new file mode 100644 index 0000000000..6a1ce195b3 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.properties + +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.ifds.{IFDSProperty, IFDSPropertyMetaInformation} +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact + +case class Taint(flows: Map[JavaStatement, Set[TaintFact]], debugData: Map[JavaStatement, Set[TaintFact]] = Map.empty) extends IFDSProperty[JavaStatement, TaintFact] { + + override type Self = Taint + override def create(result: Map[JavaStatement, Set[TaintFact]]): IFDSProperty[JavaStatement, TaintFact] = new Taint(result) + override def create(result: Map[JavaStatement, Set[TaintFact]], debugData: Map[JavaStatement, Set[TaintFact]]): IFDSProperty[JavaStatement, TaintFact] = new Taint(result, debugData) + + override def key: PropertyKey[Taint] = Taint.key +} + +object Taint extends IFDSPropertyMetaInformation[JavaStatement, TaintFact] { + + override type Self = Taint + override def create(result: Map[JavaStatement, Set[TaintFact]]): IFDSProperty[JavaStatement, TaintFact] = new Taint(result) + override def create(result: Map[JavaStatement, Set[TaintFact]], debugData: Map[JavaStatement, Set[TaintFact]]): IFDSProperty[JavaStatement, TaintFact] = new Taint(result, debugData) + + val key: PropertyKey[Taint] = PropertyKey.create("Taint", new Taint(Map.empty)) +} diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala new file mode 100644 index 0000000000..6f6e8151ed --- /dev/null +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala @@ -0,0 +1,88 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint.old +/* TODO Fix as soon as backwards analysis is implemented +import org.opalj.br.analyses.SomeProject +import org.opalj.br.{DeclaredMethod, Method} +import org.opalj.fpcf.PropertyStore +import org.opalj.ifds.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ifds._ +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, Variable} +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem + +case class UnbalancedTaintFact(index: Int, innerFact: TaintFact, callChain: Array[Method]) + extends UnbalancedReturnFact[TaintFact] with TaintFact + +/** + * An analysis that checks, if the return value of a `source` method can flow to the parameter of a + * `sink` method. + * + * @author Mario Trageser + */ +class BackwardTaintAnalysisFixture(implicit val project: SomeProject) + extends BackwardIFDSAnalysis(new BackwardTaintProblemFixture(project), OldTaint) + +class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p) { + + override val entryPoints: Seq[(DeclaredMethod, TaintFact)] = p.allProjectClassFiles.filter(classFile => + classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") + .flatMap(_.methods).filter(_.name == "sink") + .map(method => declaredMethods(method) -> + Variable(JavaIFDSProblem.switchParamAndVariableIndex(0, isStaticMethod = true))) + + /** + * The sanitize method is the sanitizer. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = callee.name == "sanitize" + + /** + * We do not sanitize paramters. + */ + override protected def sanitizesParameter(call: DeclaredMethodJavaStatement, in: TaintFact): Boolean = false + + /** + * Create a flow fact, if a source method is called and the returned value is tainted. + * This is done in callToReturnFlow, because it may be the case that the callee never + * terminates. + * In this case, callFlow would never be called and no FlowFact would be created. + */ + override protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[TaintFact], + source: (DeclaredMethod, TaintFact)): Option[FlowFact] = { + if (in.exists { + case Variable(index) => index == call.index + case _ => false + } && icfg.getCalleesIfCallStatement(call).get.exists(_.name == "source")) { + val callChain = currentCallChain(source) + // Avoid infinite loops. + if (!containsHeadTwice(callChain)) + Some(FlowFact(callChain.map(JavaMethod(_)))) + else None + } else None + } + + /** + * When a callee calls the source, we create a FlowFact with the caller's call chain. + */ + override protected def applyFlowFactFromCallee( + calleeFact: FlowFact, + source: (DeclaredMethod, TaintFact) + ): Option[FlowFact] = + Some(FlowFact(currentCallChain(source).map(JavaMethod(_)))) + + /** + * This analysis does not create FlowFacts at the beginning of a method. + * Instead, FlowFacts are created, when the return value of source is tainted. + */ + override protected def createFlowFactAtBeginningOfMethod( + in: Set[TaintFact], + source: (DeclaredMethod, TaintFact) + ): Option[FlowFact] = + None +} + +object BackwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[TaintFact] { + + override def init(p: SomeProject, ps: PropertyStore) = new BackwardTaintAnalysisFixture()(p) + + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, TaintFact] = OldTaint +} +*/ \ No newline at end of file diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala new file mode 100644 index 0000000000..a157fae825 --- /dev/null +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -0,0 +1,68 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.br.Method +import org.opalj.br.analyses.{DeclaredMethodsKey, JavaProjectInformationKeys, SomeProject} +import org.opalj.fpcf.{PropertyBounds, PropertyStore} +import org.opalj.fpcf.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSPropertyMetaInformation} +import org.opalj.si.PropertyStoreKey +import org.opalj.tac.cg.TypeIteratorKey +import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.properties.Taint + +import scala.reflect.ClassTag + +/** + * An analysis that checks, if the return value of the method `source` can flow to the parameter of + * the method `sink`. + * + * @author Mario Trageser + */ +class ForwardTaintAnalysisFixture(project: SomeProject) + extends IFDSAnalysis()(project, new ForwardTaintProblemFixture(project), Taint) + +class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) { + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(Method, TaintFact)] = p.allProjectClassFiles.filter(classFile => + classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") + .flatMap(classFile => classFile.methods) + .filter(method => method.isPublic && outsideAnalysisContext(method).isEmpty) + .map(method => method -> TaintNullFact) + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" + + /** + * We do not sanitize paramters. + */ + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact(callee: Method, call: JavaStatement, + in: TaintFact): Option[FlowFact] = + if (callee.name == "sink" && in == Variable(-2)) Some(FlowFact(Seq(JavaMethod(call.method)))) + else None +} + +object ForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[SomeProject, TaintFact, Method, JavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixture(p) + override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint + override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(Taint)) + override def requiredProjectInformation: JavaProjectInformationKeys = Seq(TypeIteratorKey, DeclaredMethodsKey, PropertyStoreKey) + + override implicit val c: ClassTag[SomeProject] = ClassTag(classOf[SomeProject]) +} diff --git a/TOOLS/hermes/src/main/scala/org/opalj/hermes/FeatureQuery.scala b/TOOLS/hermes/src/main/scala/org/opalj/hermes/FeatureQuery.scala index d7e72c2c28..de9b363c57 100644 --- a/TOOLS/hermes/src/main/scala/org/opalj/hermes/FeatureQuery.scala +++ b/TOOLS/hermes/src/main/scala/org/opalj/hermes/FeatureQuery.scala @@ -3,17 +3,13 @@ package org.opalj package hermes import java.net.URL - import scala.io.Source import scala.io.Codec - import com.github.rjeschke.txtmark.Processor - import javafx.beans.property.ObjectProperty import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.LongProperty import javafx.beans.property.SimpleLongProperty - import org.opalj.io.processSource import org.opalj.br.analyses.Project @@ -40,9 +36,9 @@ abstract class FeatureQuery(implicit hermes: HermesConfig) { * The function which analyzes the project and extracts the feature information. * * @param project A representation of the project. To speed up queries, intermediate - * information that may also be required by other queries can/should be stored in the - * project using the [[org.opalj.fpcf.PropertyStore]] or using a - * [[org.opalj.br.analyses.ProjectInformationKey]]. + * information that may also be required by other queries can/should be stored in the + * project using the [[org.opalj.fpcf.PropertyStore]] or using a + * [[ProjectInformationKey]]. * @param rawClassFiles A direct 1:1 representation of the class files. This makes it possible * to write queries that need to get an understanding of an unprocessed class file; e.g. * that need to analyze the constant pool in detail. diff --git a/build.sbt b/build.sbt index e5c5fe5b5c..d82e68fefa 100644 --- a/build.sbt +++ b/build.sbt @@ -43,7 +43,7 @@ ThisBuild / logBuffered := false ThisBuild / javacOptions ++= Seq("-encoding", "utf8", "-source", "1.8") -ThisBuild /testOptions := { +ThisBuild / testOptions := { baseDirectory .map(bd => Seq(Tests.Argument("-u", bd.getAbsolutePath + "/shippable/testresults"))) .value @@ -60,7 +60,7 @@ ScalaUnidoc / unidoc / scalacOptions := { ScalaUnidoc / unidoc / scalacOptions ++= Opts.doc.sourceUrl( - "https://raw.githubusercontent.com/stg-tud/opal/" + + "https://raw.githubusercontent.com/stg-tud/opal/" + (if (isSnapshot.value) "develop" else "master") + "/€{FILE_PATH}.scala" ) @@ -102,7 +102,10 @@ lazy val buildSettings = PublishingOverwrite.onSnapshotOverwriteSettings ++ Seq(libraryDependencies ++= Dependencies.testlibs) ++ Seq(Defaults.itSettings: _*) ++ - Seq(unmanagedSourceDirectories.withRank(KeyRanks.Invisible) := (Compile / scalaSource).value :: Nil) ++ + Seq( + unmanagedSourceDirectories + .withRank(KeyRanks.Invisible) := (Compile / scalaSource).value :: Nil + ) ++ Seq( Test / unmanagedSourceDirectories := (Test / javaSource).value :: (Test / scalaSource).value :: Nil ) ++ @@ -118,6 +121,9 @@ lazy val buildSettings = // see https://github.com/sbt/sbt-assembly/issues/391 Seq(assembly / assemblyMergeStrategy := { case "module-info.class" => MergeStrategy.discard + case PathList("META-INF", "versions", xs @ _, "module-info.class") => MergeStrategy.discard + case PathList("META-INF", "native-image", xs @ _, "jnijavacpp", "jni-config.json") => MergeStrategy.discard + case PathList("META-INF", "native-image", xs @ _, "jnijavacpp", "reflect-config.json") => MergeStrategy.discard case other => (assembly / assemblyMergeStrategy).value(other) }) @@ -140,6 +146,7 @@ lazy val `OPAL` = (project in file(".")) .settings((Defaults.coreDefaultSettings ++ Seq(publishArtifact := false)): _*) .enablePlugins(ScalaUnidocPlugin) .settings( + javaCppVersion := Dependencies.javaCppVersion, ScalaUnidoc / unidoc / unidocProjectFilter := inAnyProject -- inProjects( hermes, validate, @@ -159,6 +166,7 @@ lazy val `OPAL` = (project in file(".")) tac, de, av, + ll, framework, // bp, (just temporarily...) tools, @@ -177,6 +185,7 @@ lazy val `Common` = (project in file("OPAL/common")) .settings(buildSettings: _*) .settings( name := "Common", + javaCppVersion := Dependencies.javaCppVersion, Compile / doc / scalacOptions := Opts.doc.title("OPAL-Common"), libraryDependencies ++= Dependencies.common(scalaVersion.value) ) @@ -276,6 +285,19 @@ lazy val `AbstractInterpretationFramework` = (project in file("OPAL/ai")) .dependsOn(br % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) +lazy val ifds = `IFDS` +lazy val `IFDS` = (project in file("OPAL/ifds")) + .settings(buildSettings: _*) + .settings( + name := "IFDS", + Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - IFDS"), + fork := true, + libraryDependencies ++= Dependencies.ifds + ) + .dependsOn(si % "it->it;it->test;test->test;compile->compile") + .dependsOn(br % "it->it;it->test;test->test;compile->compile") + .configs(IntegrationTest) + lazy val tac = `ThreeAddressCode` lazy val `ThreeAddressCode` = (project in file("OPAL/tac")) .settings(buildSettings: _*) @@ -288,6 +310,7 @@ lazy val `ThreeAddressCode` = (project in file("OPAL/tac")) run / fork := true ) .dependsOn(ai % "it->it;it->test;test->test;compile->compile") + .dependsOn(ifds % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) lazy val ba = `BytecodeAssembler` @@ -326,6 +349,19 @@ lazy val `ArchitectureValidation` = (project in file("OPAL/av")) .dependsOn(de % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) +lazy val ll = `LLVM` +lazy val `LLVM` = (project in file("OPAL/ll")) + .settings(buildSettings: _*) + .settings( + name := "LLVM", + Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - LLVM"), + fork := true, + javaCppPresetLibs ++= Dependencies.javaCppPresetLibs, + javaCppVersion := Dependencies.javaCppVersion +) + .dependsOn(tac % "it->it;it->test;test->test;compile->compile") + .configs(IntegrationTest) + lazy val framework = `Framework` lazy val `Framework` = (project in file("OPAL/framework")) .settings(buildSettings: _*) @@ -337,7 +373,8 @@ lazy val `Framework` = (project in file("OPAL/framework")) .dependsOn( ba % "it->it;it->test;test->test;compile->compile", av % "it->it;it->test;test->test;compile->compile", - tac % "it->it;it->test;test->test;compile->compile" + tac % "it->it;it->test;test->test;compile->compile", + ll % "it->it;it->test;test->test;compile->compile" ) .configs(IntegrationTest) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index fdb9704cca..c5ff55be67 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -69,7 +69,11 @@ object Dependencies { val si = Seq() val bi = Seq(commonstext) val br = Seq(scalaparsercombinators, scalaxml) + val ifds = Seq() val tools = Seq(txtmark, jacksonDF) val hermes = Seq(txtmark, jacksonDF, javafxBase) + val javaCppVersion = "1.5.7" + val javaCppPresetLibs = Seq("llvm" -> "13.0.1") + } diff --git a/project/plugins.sbt b/project/plugins.sbt index ef74163f62..867adb1f84 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -20,3 +20,6 @@ addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.2") // For the deployment to maven central: addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.7") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") + +// llvm +addSbtPlugin("org.bytedeco" % "sbt-javacpp" % "1.17") \ No newline at end of file diff --git a/src/site/tutorial/Schedulers.md b/src/site/tutorial/Schedulers.md index 1eab30dcdf..558140c05e 100644 --- a/src/site/tutorial/Schedulers.md +++ b/src/site/tutorial/Schedulers.md @@ -18,7 +18,7 @@ This consists of two parts: First, `requiredProjectInformation` gives the [`ProjectInformationKey`s](/library/api/SNAPSHOT/org/opalj/br/analyses/ProjectInformationKey.html) that your analysis uses. ProjectInformationKeys provide aggregated information about a project, such as a call graph or the set of methods that access each field. -Here, we specified the [`FieldAccessInformationKey`](/library/api/SNAPSHOT/org/opalj/br/analyses/FieldAccessInformationKey$.html) that you can use with [`Project.get()`](/library/api/SNAPSHOT/org/opalj/br/analyses/Project.html#get[T%3C:AnyRef](pik:org.opalj.br.analyses.ProjectInformationKey[T,_]):T) to get [`FieldAccessInformation`](/library/api/SNAPSHOT/org/opalj/br/analyses/FieldAccessInformation.html), i.e., information about where each field is read or written. +Here, we specified the [`FieldAccessInformationKey`](/library/api/SNAPSHOT/org/opalj/br/analyses/FieldAccessInformationKey$.html) that you can use with [`Project.get()`](/library/api/SNAPSHOT/org/opalj/br/analyses/Project.html#get[T%3C:AnyRef](pik:org.opalj.si.ProjectInformationKey[T,_]):T) to get [`FieldAccessInformation`](/library/api/SNAPSHOT/org/opalj/br/analyses/FieldAccessInformation.html), i.e., information about where each field is read or written. Second, `uses` gives the results of fixed-point analyses that your analysis requires. Here, our analysis uses upper bounds for both `FieldImmutability` and `ClassImmutability`.