diff --git a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/SystemProperties.scala b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/SystemProperties.scala index c6e1df570e..752e675c32 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/SystemProperties.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/fpcf/properties/SystemProperties.scala @@ -21,6 +21,7 @@ sealed trait SystemPropertiesPropertyMetaInformation extends PropertyMetaInforma type Self = SystemProperties } +// TODO Should probably use a data structure that allows retrieving only the latest entries (for use in continuations) class SystemProperties(val properties: Map[String, Set[String]]) extends Property with SystemPropertiesPropertyMetaInformation { final def key: PropertyKey[SystemProperties] = SystemProperties.key 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..4006c428eb 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 @@ -45,6 +45,7 @@ class SystemPropertiesAnalysisScheduler private[analyses] ( var propertyMap: Map[String, Set[String]] = Map.empty + // TODO Should we be able to distinguish between the System properties and other properties objects? for (stmt <- stmts) stmt match { case VirtualFunctionCallStatement(call) if (call.name == "setProperty" || call.name == "put") && classHierarchy.isSubtypeOf(call.declaringClass, ObjectType("java/util/Properties")) => propertyMap = computeProperties(propertyMap, call.params, stmts) @@ -123,6 +124,7 @@ class SystemPropertiesAnalysisScheduler private[analyses] ( def getPossibleStrings( value: Expr[DUVar[ValueInformation]], stmts: Array[Stmt[DUVar[ValueInformation]]] ): Set[String] = { + // TODO Instead of only using String constants, use StringUtil to make use of points-to data if available value.asVar.definedBy filter { index => index >= 0 && stmts(index).asAssignment.expr.isStringConst } map { stmts(_).asAssignment.expr.asStringConst.value } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MatcherUtil.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MatcherUtil.scala index 047326144e..940e0acca5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MatcherUtil.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MatcherUtil.scala @@ -12,6 +12,7 @@ import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.FieldTypes import org.opalj.br.fpcf.properties.Context +import org.opalj.br.ClassHierarchy object MatcherUtil { @@ -93,6 +94,7 @@ object MatcherUtil { typeIterator: TypeIterator, state: TypeIteratorState, ps: PropertyStore, + classHierarchy: ClassHierarchy, incompleteCallSites: IncompleteCallSites, highSoundness: Boolean ): MethodMatcher = { @@ -131,6 +133,7 @@ object MatcherUtil { typeIterator: TypeIterator, state: TypeIteratorState, ps: PropertyStore, + classHierarchy: ClassHierarchy, incompleteCallSites: IncompleteCallSites, highSoundness: Boolean ): MethodMatcher = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MethodHandlesUtil.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MethodHandlesUtil.scala index b77f16e738..c58066e661 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MethodHandlesUtil.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/MethodHandlesUtil.scala @@ -6,11 +6,13 @@ package analyses package cg package reflection +import org.opalj.fpcf.PropertyStore import org.opalj.br.MethodDescriptor import org.opalj.br.ObjectType import org.opalj.br.ReferenceType import org.opalj.br.VoidType import org.opalj.br.analyses.SomeProject +import org.opalj.br.ClassHierarchy import org.opalj.br.FieldType object MethodHandlesUtil { @@ -66,6 +68,11 @@ object MethodHandlesUtil { isConstructor: Boolean, stmts: Array[Stmt[V]], project: SomeProject + )( + implicit + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): MethodMatcher = { val descriptorsOpt = if (descriptorOpt.isDefined) { @@ -109,6 +116,11 @@ object MethodHandlesUtil { value: Expr[V], stmts: Array[Stmt[V]], project: SomeProject + )( + implicit + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): Option[Iterator[MethodDescriptor]] = { def isMethodType(expr: Expr[V]): Boolean = { @@ -158,6 +170,11 @@ object MethodHandlesUtil { descriptor: MethodDescriptor, stmts: Array[Stmt[V]], project: SomeProject + )( + implicit + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): Option[Iterator[MethodDescriptor]] = { val returnTypesOpt = TypesUtil.getPossibleClasses(params.head, stmts, project) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/StringUtil.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/StringUtil.scala index ec6782bcc6..58c0be23ce 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/StringUtil.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/StringUtil.scala @@ -7,9 +7,14 @@ package cg package reflection import org.opalj.fpcf.Entity +import org.opalj.fpcf.EOptionP import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.UBP import org.opalj.br.ObjectType import org.opalj.br.fpcf.properties.Context +import org.opalj.br.ClassHierarchy +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.properties.SystemProperties object StringUtil { @@ -20,16 +25,17 @@ object StringUtil { def getPossibleStrings[ContextType <: Context]( value: Expr[V], stmts: Array[Stmt[V]] + )( + implicit + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): Option[Set[String]] = { Some(value.asVar.definedBy.map[Set[String]] { index => - if (index >= 0) { - getString(index, stmts) match { - case Some(v) => Set(v) - case _ => - return None; - } - } else { - return None; + getString(index, stmts) match { + case Some(v) => Set(v) + case _ => + return None; } }.flatten) } @@ -48,9 +54,10 @@ object StringUtil { failure: () => Unit )( implicit - typeIterator: TypeIterator, - state: TypeIteratorState, - ps: PropertyStore + typeIterator: TypeIterator, + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): Set[String] = { var strings = Set.empty[String] @@ -68,11 +75,61 @@ object StringUtil { strings } - private[reflection] def getString(stringDefSite: Int, stmts: Array[Stmt[V]]): Option[String] = { - val expr = stmts(stringDefSite).asAssignment.expr - expr match { - case StringConst(_, v) => Some(v) - case _ => None + private[reflection] def getString( + stringDefSite: Int, + stmts: Array[Stmt[V]] + )( + implicit + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy + ): Option[String] = { + if (stringDefSite >= 0) { + val expr = stmts(stringDefSite).asAssignment.expr + expr match { + case StringConst(_, v) => Some(v) + // TODO These don't return anything for now as the necessary dependency handling must be implemented first! + case VirtualFunctionCall(_, declaringClass, _, name, _, _, params) if (name == "getProperty" || name == "get") && classHierarchy.isSubtypeOf(declaringClass, ObjectType("java/util/Properties")) => + getPossiblePropertyValues(params, stmts); None + case StaticFunctionCall(_, ObjectType.System, _, "getProperty", _, params) => + getPossiblePropertyValues(params, stmts); None + case _ => None + } + } else { + None } } + + private[this] def getPossiblePropertyValues( + params: Seq[Expr[V]], + stmts: Array[Stmt[V]] + )( + implicit + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy + ): Option[Set[String]] = { + val possibleKeys = getPossibleStrings(params.head, stmts) // TODO Use points-to based method + + if (possibleKeys.isDefined) { + val defaultValue = if (params.size == 2) + getPossibleStrings(params(1), stmts).getOrElse(Set.empty) // TODO Use points-to based method + else + Set.empty + + val properties: EOptionP[SomeProject, SystemProperties] = + ps(ps.context(classOf[SomeProject]), SystemProperties.key) + + if (properties.isRefinable) + state.addDependency(null /*TODO*/ , properties) + + val propertyValues = properties match { + case UBP(ub) => possibleKeys.get.flatMap(key => ub.properties(key)) + case _ => Set.empty + } + + Some(propertyValues ++ defaultValue) + } else + None + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TypesUtil.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TypesUtil.scala index 1d2a087185..bb16c70f40 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TypesUtil.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/reflection/TypesUtil.scala @@ -16,6 +16,7 @@ import org.opalj.br.Type import org.opalj.br.VoidType import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.properties.Context +import org.opalj.br.ClassHierarchy object TypesUtil { @@ -27,6 +28,11 @@ object TypesUtil { stmts: Array[Stmt[V]], project: SomeProject, onlyObjectTypes: Boolean + )( + implicit + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): Option[Set[ObjectType]] = { StringUtil.getPossibleStrings(className, stmts).map(_.flatMap { cls => try { @@ -59,9 +65,10 @@ object TypesUtil { onlyObjectTypes: Boolean )( implicit - typeIterator: TypeIterator, - state: TypeIteratorState, - ps: PropertyStore + typeIterator: TypeIterator, + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): Set[ObjectType] = { StringUtil.getPossibleStrings(className, context, depender, stmts, failure).flatMap { cls => try { @@ -84,6 +91,11 @@ object TypesUtil { stmts: Array[Stmt[V]], project: SomeProject, onlyObjectTypes: Boolean + )( + implicit + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): Option[ObjectType] = { StringUtil.getString(classNameDefSite, stmts).flatMap { cls => try { @@ -108,6 +120,11 @@ object TypesUtil { stmts: Array[Stmt[V]], project: SomeProject, onlyObjectTypes: Boolean = false + )( + implicit + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): Option[Iterator[Type]] = { def isForName(expr: Expr[V]): Boolean = { // static call to Class.forName @@ -194,9 +211,10 @@ object TypesUtil { onlyObjectTypes: Boolean )( implicit - typeIterator: TypeIterator, - state: TypeIteratorState, - ps: PropertyStore + typeIterator: TypeIterator, + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): Set[Type] = { var possibleTypes: Set[Type] = Set.empty @@ -231,9 +249,10 @@ object TypesUtil { onlyObjectTypes: Boolean )( implicit - typeIterator: TypeIterator, - state: TypeIteratorState, - ps: PropertyStore + typeIterator: TypeIterator, + state: TypeIteratorState, + ps: PropertyStore, + classHierarchy: ClassHierarchy ): Set[Type] = { var possibleTypes: Set[Type] = Set.empty