/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj package value import scala.annotation.switch import scala.util.boundary import scala.util.boundary.break import org.opalj.br.ArrayType import org.opalj.br.BaseType import org.opalj.br.BooleanType import org.opalj.br.ByteType import org.opalj.br.CharType import org.opalj.br.ClassHierarchy import org.opalj.br.ClassType import org.opalj.br.ComputationalType import org.opalj.br.ComputationalTypeReference import org.opalj.br.ComputationalTypeReturnAddress import org.opalj.br.DoubleType import org.opalj.br.DoubleVariableInfo import org.opalj.br.FloatType import org.opalj.br.FloatVariableInfo import org.opalj.br.IntegerType import org.opalj.br.IntegerVariableInfo import org.opalj.br.LongType import org.opalj.br.LongVariableInfo import org.opalj.br.NullVariableInfo import org.opalj.br.ObjectVariableInfo import org.opalj.br.ReferenceType import org.opalj.br.ShortType import org.opalj.br.TopVariableInfo import org.opalj.br.Type import org.opalj.br.VerificationTypeInfo import org.opalj.br.VoidType import org.opalj.collection.immutable.UIDSet import org.opalj.collection.immutable.UIDSet1 import org.opalj.util.elidedAssert /** * Encapsulates the available type information about a `DomainValue`. * * @author Michael Eichberg */ trait ValueInformation { /** * Returns `true` iff this value is not a legal value according to the JVM specification. * Such values cannot be used to perform any computations and will generally not occur * in static analyses unless the analysis or the bytecode is buggy. * * @note An [[IsIllegalValue]] can always be distinguished from a void value. */ def isIllegalValue: Boolean /** * Returns `true` if this value represents void. */ def isVoid: Boolean /** * Returns `true` in case of a value with primitive type. * * @throws IllegalStateException if this value is illegal. */ def isPrimitiveValue: Boolean def asPrimitiveValue: IsPrimitiveValue[? <: BaseType, ? <: AnyVal] = throw new ClassCastException(); /** * Returns `true` if the value has a reference type. * * @throws IllegalStateException if this value is illegal. */ def isReferenceValue: Boolean def asReferenceValue: IsReferenceValue = throw new ClassCastException(); /** * Returns `Yes` if the value is _not null_ and the least upper type bound is an `ArrayType`; * the value is `Unknown` if the least upper type bound is `ArrayType` but the value may be null; * in all other cases `No` is returned; in particular if the value is known to be null. `No` * is also returned if the value's type is `Object` or `Serializable` or `Cloneable`. */ def isArrayValue: Answer /** * The computational type of the value if this object represents a legal value. * * The precise computational type is, e.g., needed to calculate the effect * of generic stack manipulation instructions (e.g., `DUP_...` and `SWAP`) * on the stack as well as to calculate the jump targets of `RET` instructions * and to determine which values are actually copied by, e.g., the `dup_XX` * instructions. * * @note The computational type has to be precise/correct. * * @throws IllegalStateException if this value is illegal or void. */ def computationalType: ComputationalType /** * Returns `true` if and only if the value has the computational type 2; `false` in all * other cases (including the case where this value is illegal!). */ def hasCategory2ComputationalType: Boolean /** * The type of this value as used by the [[org.opalj.br.StackMapTable]] attribute. * * @throws IllegalStateException if this ''value represents void'' or a return address value. */ def verificationTypeInfo: VerificationTypeInfo /** * Returns a `ValueInformation` object that just captures the basic information as defined * by this `value` framework. The returned value information object will be independent of * the underlying representation from which it was derived. */ def toCanonicalForm: ValueInformation } object ValueInformation { /** * Creates a new [[ValueInformation]] object for a proper value. In case of a reference value * the value is either null or a value that is properly initialized; i.e., it cannot be an * uninitialized this/value. */ def forProperValue(t: Type)(implicit classHierarchy: ClassHierarchy): ValueInformation = { (t.id: @switch) match { case VoidType.id => TheVoidValue case BooleanType.id => ABooleanValue case ByteType.id => AByteValue case CharType.id => ACharValue case ShortType.id => AShortValue case IntegerType.id => AnIntegerValue case LongType.id => ALongValue case FloatType.id => AFloatValue case DoubleType.id => ADoubleValue case _ /*referenceTypeId*/ => if (t.isClassType) { val classType = t.asClassType AProperSObjectValue( Unknown, isPrecise = classHierarchy.isKnownToBeFinal(classType), classType ) } else { val arrayType = t.asArrayType ASArrayValue( Unknown, isPrecise = classHierarchy.isKnownToBeFinal(arrayType), arrayType ) } } } } trait IsIllegalValue extends ValueInformation { override final def isIllegalValue: Boolean = true override final def isVoid: Boolean = false override final def isPrimitiveValue: Boolean = throw new IllegalStateException("illegal value") override final def isReferenceValue: Boolean = throw new IllegalStateException("illegal value") override final def isArrayValue: Answer = No override final def computationalType: ComputationalType = { throw new IllegalStateException("illegal value") } override final def hasCategory2ComputationalType: Boolean = false override final def verificationTypeInfo: VerificationTypeInfo = TopVariableInfo override final def toCanonicalForm: ValueInformation = IsIllegalValue } case object IsIllegalValue extends IsIllegalValue sealed trait KnownValue extends ValueInformation { override final def isIllegalValue: Boolean = false } object TheVoidValue extends KnownValue { override def isVoid: Boolean = true override def isPrimitiveValue: Boolean = false override def isReferenceValue: Boolean = false override def isArrayValue: Answer = No override def computationalType: ComputationalType = throw new IllegalStateException("void") override def hasCategory2ComputationalType: Boolean = false override def verificationTypeInfo: VerificationTypeInfo = { throw new IllegalStateException("void") } override def toCanonicalForm: ValueInformation = this } /** * A value with a well-defined computational type. */ trait KnownTypedValue extends KnownValue { override final def isVoid: Boolean = false } trait IsReturnAddressValue extends KnownTypedValue { override final def isPrimitiveValue: Boolean = false override final def isReferenceValue: Boolean = false override final def isArrayValue: Answer = No override final def computationalType: ComputationalType = ComputationalTypeReturnAddress override final def hasCategory2ComputationalType: Boolean = false override final def verificationTypeInfo: VerificationTypeInfo = { throw new IllegalStateException("return address value - cf. JVM spec.: StackMapTableAttribute") } override def toCanonicalForm: IsReturnAddressValue = AReturnAddressValue } case object AReturnAddressValue extends IsReturnAddressValue /** * A value for which the information is available if it is a constant – and if so which – value. */ trait ConstantValueInformationProvider[T] { /** * The constant value that this variable takes - if it takes a single constant value! * I.e., if the variable may take multiple different values at runtime `constantValue` will * return `None`. */ def constantValue: Option[T] def asConstantBoolean: Boolean = throw new UnsupportedOperationException("not a boolean const") def asConstantByte: Byte = throw new UnsupportedOperationException("not a byte const") def asConstantShort: Short = throw new UnsupportedOperationException("not a short const") def asConstantChar: Char = throw new UnsupportedOperationException("not a char const") def asConstantInteger: Integer = throw new UnsupportedOperationException("not a integer const") def asConstantLong: Long = throw new UnsupportedOperationException("not a long const") def asConstantFloat: Float = throw new UnsupportedOperationException("not a float const") def asConstantDouble: Double = throw new UnsupportedOperationException("not a double const") } /** * The value has the primitive type. */ sealed trait IsPrimitiveValue[T <: BaseType, V <: AnyVal] extends KnownTypedValue with ConstantValueInformationProvider[V] { override final def isReferenceValue: Boolean = false override final def isPrimitiveValue: Boolean = true override final def asPrimitiveValue: IsPrimitiveValue[T, V] = this override final def isArrayValue: Answer = No def primitiveType: T override final def computationalType: ComputationalType = primitiveType.computationalType } object IsPrimitiveValue { def unapply[T <: BaseType, V <: AnyVal](underlying: IsPrimitiveValue[T, V]): Some[T] = { Some(underlying.primitiveType) } } sealed trait IsIntegerLikeValue[T <: BaseType, V <: AnyVal] extends IsPrimitiveValue[T, V] { override final def verificationTypeInfo: VerificationTypeInfo = IntegerVariableInfo } trait IsBooleanValue extends IsIntegerLikeValue[BooleanType, Boolean] { override final def primitiveType: BooleanType = BooleanType override final def hasCategory2ComputationalType: Boolean = false override def toCanonicalForm: ValueInformation = ABooleanValue override def asConstantBoolean: Boolean = constantValue.get // Expected to be overridden! } case object ABooleanValue extends IsBooleanValue { override def constantValue: Option[Boolean] = None } case object BooleanValueTrue extends IsBooleanValue { override def constantValue: Option[Boolean] = Some(true) override def asConstantBoolean: Boolean = true override def toCanonicalForm: ValueInformation = this } case object BooleanValueFalse extends IsBooleanValue { override def constantValue: Option[Boolean] = Some(false) override def asConstantBoolean: Boolean = false override def toCanonicalForm: ValueInformation = this } trait IsByteValue extends IsIntegerLikeValue[ByteType, Byte] { override final def primitiveType: ByteType = ByteType override final def hasCategory2ComputationalType: Boolean = false override def toCanonicalForm: ValueInformation = AByteValue override def asConstantByte: Byte = constantValue.get // Expected to be overridden! } case object AByteValue extends IsByteValue { override def constantValue: Option[Byte] = None } case class TheByteValue(value: Byte) extends IsByteValue { override def constantValue: Option[Byte] = Some(value) override def asConstantByte: Byte = value override def toCanonicalForm: ValueInformation = this } trait IsCharValue extends IsIntegerLikeValue[CharType, Char] { override final def primitiveType: CharType = CharType override final def hasCategory2ComputationalType: Boolean = false override def toCanonicalForm: ValueInformation = ACharValue override def asConstantChar: Char = constantValue.get // Expected to be overridden! } case object ACharValue extends IsCharValue { override def constantValue: Option[Char] = None } case class TheCharValue(value: Char) extends IsCharValue { override def constantValue: Option[Char] = Some(value) override def asConstantChar: Char = value override def toCanonicalForm: ValueInformation = this } trait IsShortValue extends IsIntegerLikeValue[ShortType, Short] { override final def primitiveType: ShortType = ShortType override final def hasCategory2ComputationalType: Boolean = false override def toCanonicalForm: ValueInformation = AShortValue override def asConstantShort: Short = constantValue.get // Expected to be overridden! } case object AShortValue extends IsShortValue { override def constantValue: Option[Short] = None } case class TheShortValue(value: Short) extends IsShortValue { override def constantValue: Option[Short] = Some(value) override def asConstantShort: Short = value override def toCanonicalForm: ValueInformation = this } trait IsIntegerValue extends IsIntegerLikeValue[IntegerType, Int] { override final def primitiveType: IntegerType = IntegerType override final def hasCategory2ComputationalType: Boolean = false override def toCanonicalForm: ValueInformation = { if (constantValue.isDefined) TheIntegerValue(constantValue.get) else AnIntegerValue } override def asConstantInteger: Integer = constantValue.get // Expected to be overridden! def lowerBound: Int def upperBound: Int } case object AnIntegerValue extends IsIntegerValue { override final def constantValue: Option[Int] = None override final def lowerBound: Int = Int.MinValue override final def upperBound: Int = Int.MaxValue } case class TheIntegerValue(value: Int) extends IsIntegerValue { override final def constantValue: Option[Int] = Some(value) override def asConstantInteger: Integer = value override final def lowerBound: Int = value override final def upperBound: Int = value override def toCanonicalForm: ValueInformation = this } trait IsFloatValue extends IsPrimitiveValue[FloatType, Float] { override final def primitiveType: FloatType = FloatType override final def hasCategory2ComputationalType: Boolean = false override final def verificationTypeInfo: VerificationTypeInfo = FloatVariableInfo override def toCanonicalForm: ValueInformation = AFloatValue override def asConstantFloat: Float = constantValue.get // Expected to be overridden! } case object AFloatValue extends IsFloatValue { override def constantValue: Option[Float] = None } case class TheFloatValue(value: Float) extends IsFloatValue { override def constantValue: Option[Float] = Some(value) override def asConstantFloat: Float = value override def toCanonicalForm: ValueInformation = this } trait IsLongValue extends IsPrimitiveValue[LongType, Long] { override final def primitiveType: LongType = LongType override final def hasCategory2ComputationalType: Boolean = true override final def verificationTypeInfo: VerificationTypeInfo = LongVariableInfo override def toCanonicalForm: ValueInformation = ALongValue override def asConstantLong: Long = constantValue.get // Expected to be overridden! } case object ALongValue extends IsLongValue { override def constantValue: Option[Long] = None } case class TheLongValue(value: Long) extends IsLongValue { override def constantValue: Option[Long] = Some(value) override def asConstantLong: Long = value override def toCanonicalForm: ValueInformation = this } trait IsDoubleValue extends IsPrimitiveValue[DoubleType, Double] { override final def primitiveType: DoubleType = DoubleType override final def hasCategory2ComputationalType: Boolean = true override final def verificationTypeInfo: VerificationTypeInfo = DoubleVariableInfo override def toCanonicalForm: ValueInformation = ADoubleValue override def asConstantDouble: Double = constantValue.get // Expected to be overridden! } case object ADoubleValue extends IsDoubleValue { override def constantValue: Option[Double] = None } case class TheDoubleValue(value: Double) extends IsDoubleValue { override def constantValue: Option[Double] = Some(value) override def asConstantDouble: Double = value override def toCanonicalForm: ValueInformation = this } /** * Describes the essential properties of a reference value in a program. * * For example, in the following: * {{{ * val o = If(...) new Object() else "STRING" * }}} * o is a reference value (`IsReferenceValue`) that (may) refers to two "simple" base values: * `new Object()` and `"STRING"`; however, it is a decision of the underlying domain whether * the information about the base values is made available or not. Furthermore, if the base values * are actually used, the constraints in effect for the overall abstraction should be considered * to get the most precise result. * * @author Michael Eichberg */ trait IsReferenceValue extends KnownTypedValue { override final def isPrimitiveValue: Boolean = false override final def isReferenceValue: Boolean = true override final def asReferenceValue: IsReferenceValue = this override final def computationalType: ComputationalType = ComputationalTypeReference override final def hasCategory2ComputationalType: Boolean = false /** * The upper bound of the value's type. The upper bound is empty if this * value is `null` (i.e., `isNull == Yes`). The upper bound is only guaranteed to contain * exactly one type if the type is precise. (i.e., `isPrecise == true`). Otherwise, * the upper type bound may contain one or more types that are not known to be * in an inheritance relation, but which will ''correctly'' approximate the runtime * type. * * @note If only a part of a project is analyzed, the class hierarchy may be * fragmented and it may happen that two classes that are indeed in an * inheritance relation – if we would analyze the complete project – are part * of the upper type bound. */ def upperTypeBound: UIDSet[? <: ReferenceType] /** * If `Yes` the value is known to always be `null` at runtime. In this * case the upper bound is (has to be) empty. If the answer is `Unknown` then the * analysis was not able to statically determine whether the value is `null` or * is not `null`. In this case the upper bound is expected to be non-empty. * If the answer is `No` then the value is statically known not to be `null`. In this * case, the upper bound may precisely identify the runtime type or still just identify * an upper bound. */ def isNull: Answer /** * Returns `true` if the type information is precise. I.e., the type returned by * `upperTypeBound` precisely models the runtime type of the value. * If, `isPrecise` returns true, the type of this value can * generally be assumed to represent a class type (not an interface type) or * an array type. However, this domain also supports the case that `isPrecise` * returns `true` even though the associated type identifies an interface type * or an abstract class type. The later case may be interesting in the context * of classes that are generated at run time. */ def isPrecise: Boolean /** * Returns '''the type of the upper type bound''' if the upper type bound contains * exactly one element. That is, the function is only always defined iff the type * is precise. */ final def asReferenceType: ReferenceType = { if (!upperTypeBound.isSingletonSet) { throw new ClassCastException(s"$upperTypeBound.size >= 1"); } upperTypeBound.head } /** * The least upper type bound of the value. * * `None` if and only if the underlying value is `null`. */ def leastUpperType: Option[ReferenceType] /** * Tests if the type of this value is potentially a subtype of the specified * reference type under the assumption that this value is not `null`. * This test takes the precision of the type information into account. * That is, if the currently available type information is not precise and * the given type has a subtype that is always a subtype of the current * upper type bound, then `Unknown` is returned. Given that it may be * computationally intensive to determine whether two types have a common subtype * it may be better to just return `Unknown` in case that this type and the * given type are not in a direct inheritance relationship. * * Basically, this method implements the same semantics as the `ClassHierarchy`'s * `isSubtypeOf` method, but it additionally checks if the type of this value * ''could be a subtype'' of the given supertype. I.e., if this value's type * identifies a supertype of the given `supertype` and that type is not known * to be precise, the answer is `Unknown`. * * For example, assume that the type of this reference value is * `java.util.Collection` and we know/have to assume that this is only an * upper bound. In this case an answer is `No` if and only if it is impossible * that the runtime type is a subtype of the given supertype. This * condition holds, for example, for `java.io.File` which is not a subclass * of `java.util.Collection` and which does not have any further subclasses (in * the JDK). I.e., the classes `java.io.File` and `java.util.Collection` are * not in an inheritance relationship. However, if the specified supertype would * be `java.util.List` the answer would be unknown. * * @note The function `isValueASubtypeOf` is not defined if `isNull` returns `Yes`; * if `isNull` is `Unknown` then the result is given under the * assumption that the value is not `null` at runtime. * In other words, if this value represents `null` this method is not supported. * If the value is null, the effect/interpretation of a subtype of query is * context dependent (isInstanceOf/checkCast). */ def isValueASubtypeOf( referenceType: ReferenceType )( implicit classHierarchy: ClassHierarchy ): Answer /** * In general an `IsReferenceValue` abstracts over all potential values and this information is * sufficient for subsequent analyses; but in some cases, analyzing the set of underlying values * may increase the overall precision and this set is returned by this function. In other * words: if `baseValues` is nonEmpty, then the properties returned by `this` value are derived * from the base values, but still maybe more specific. For example, * {{{ * Object o = _; * if(...) o = f() else o = g(); * // when we reach this point, we generally don't know if the values returned by f and g * // are non-null; hence, o is potentially null. * if(o != null) * // Now, we know that o is not null, but we still don't know if the values returned * // by f OR g were null and we cannot establish that when we don't know to which value * // o is actually referring to. * u(o); * }}} * * @note A reference value which belongs to the base values by some other reference value * '''never''' has itself as a '''direct''' base value. * * @return The set of values this reference value abstracts over. The set is empty if this * value is already a base value and it does not abstract over other values. */ def baseValues: Iterable[IsReferenceValue] // ... technically a set of IsBaseReferenceValue /** * The set of base values (`IsReferenceValue`) this value abstracts over. * This set is never empty and contains this value if this value does not (further) abstract * over other reference values; otherwise it only contains the base values, * but not `this` value. * * @note Primarily defined as a convenience interface. */ def allValues: Iterable[IsReferenceValue] override def toCanonicalForm: IsReferenceValue } trait IsBaseReferenceValue extends IsReferenceValue { override final def baseValues: Iterable[this.type] = Nil override final def allValues: Iterable[this.type] = List(this) override def toCanonicalForm: IsBaseReferenceValue } trait IsNullValue extends IsBaseReferenceValue { override final def isArrayValue: Answer = No override final def isNull: Answer = Yes override final def isPrecise: Boolean = true override final def upperTypeBound: UIDSet[? <: ReferenceType] = UIDSet.empty override final def leastUpperType: None.type = None override final def verificationTypeInfo: VerificationTypeInfo = NullVariableInfo override final def isValueASubtypeOf( referenceType: ReferenceType )( implicit classHierarchy: ClassHierarchy ): Answer = { throw new IllegalStateException("null value") } override def toCanonicalForm: IsNullValue = IsNullValue } case object IsNullValue extends IsNullValue { def unapply(rv: IsReferenceValue): Boolean = rv.isNull == Yes override def toString: String = "NullValue" } trait IsMObjectValue extends IsBaseReferenceValue { /** * All types from which the (precise, but unknown) type of the represented value inherits. * I.e., the value represented by this domain value is known to have a type that * (in)directly inherits from all given types at the same time. Hence, the upperTypeBound * may contain at most one class type. */ override def upperTypeBound: UIDSet[ClassType] override final def isArrayValue: Answer = No elidedAssert(upperTypeBound.size > 1) override final def verificationTypeInfo: VerificationTypeInfo = { ObjectVariableInfo(leastUpperType.get) } // Non-final to enable override def isPrecise: Boolean = false /** * Determines if this value is a subtype of the given supertype by * delegating to the `isSubtypeOf(ReferenceType,ReferenceType)` method of the * domain. * * @note This is a very basic implementation that cannot determine that this * value is '''not''' a subtype of the given type as this implementation * does not distinguish between class types and interface types. */ override def isValueASubtypeOf( supertype: ReferenceType )( implicit classHierarchy: ClassHierarchy ): Answer = boundary { var isASubtypeOf: Answer = No upperTypeBound foreach { anUpperTypeBound => classHierarchy.isASubtypeOf(anUpperTypeBound, supertype) match { case Yes => break(Yes); // <= Shortcut evaluation case Unknown => isASubtypeOf = Unknown case No => /*nothing to do*/ } } /* No | Unknown*/ // In general, we could check whether a type exists that is a // proper subtype of the type identified by this value's type bounds // and that is also a subtype of the given `supertype`. // // If such a type does not exist the answer is truly `no` (if we // assume that we know the complete type hierarchy); // if we don't know the complete hierarchy or if we currently // analyze a library the answer generally has to be `Unknown` // unless we also consider the classes that are final or .... isASubtypeOf match { // Yes is not possible here! case No if (supertype.isArrayType && upperTypeBound != ClassType.SerializableAndCloneable) => // even if the upper bound is not precise we are now 100% sure // that this value is not a subtype of the given supertype No case _ => Unknown } } override def toCanonicalForm: IsBaseReferenceValue = { AProperMObjectValue(isNull, isPrecise, upperTypeBound, leastUpperType) } } /** * Represents an object value which is either null or properly initialized. */ case class AProperMObjectValue( override val isNull: Answer, override val isPrecise: Boolean, upperTypeBound: UIDSet[ClassType], leastUpperType: Option[ReferenceType] // actually always Some[ClassType] ) extends IsMObjectValue { override def toCanonicalForm: IsMObjectValue = this override def toString: String = { "ProperMObjectValue(" + s"type=${upperTypeBound.map(_.toJava).toList.sorted.mkString(" with ")}," + s"isNull=$isNull,isPrecise=$isPrecise)" } } trait IsSReferenceValue[T <: ReferenceType] extends IsBaseReferenceValue { def theUpperTypeBound: T override final def leastUpperType: Option[ReferenceType] = Some(theUpperTypeBound) override final def upperTypeBound: UIDSet[T] = UIDSet1(theUpperTypeBound) } trait IsSObjectValue extends IsSReferenceValue[ClassType] { override final def isArrayValue: Answer = No override def isValueASubtypeOf( supertype: ReferenceType )( implicit classHierarchy: ClassHierarchy ): Answer = { val subtype = theUpperTypeBound classHierarchy.isASubtypeOf(subtype, supertype) match { case Yes => Yes case No if isPrecise || ( supertype.isArrayType && // and it is impossible that this value is actually an array... (subtype ne ClassType.Object) && (subtype ne ClassType.Serializable) && (subtype ne ClassType.Cloneable) ) || ( // If both types represent class types and it is not // possible that some value of this type may be a subtype // of the given supertype, the answer "No" is correct. supertype.isClassType && classHierarchy.isKnown(supertype.asClassType) && classHierarchy.isKnown(subtype) && classHierarchy.isInterface(supertype.asClassType).isNo && classHierarchy.isInterface(subtype).isNo && classHierarchy.isASubtypeOf(supertype, subtype).isNo ) => No case _ if isPrecise && // Note "reflexivity" is already captured by the first isSubtypeOf call classHierarchy.isSubtypeOf(supertype, subtype) => No case _ => Unknown } } // Non-final to enable subclasses to identify cases of uninitializedThis/Variable cases. override def verificationTypeInfo: VerificationTypeInfo = { ObjectVariableInfo(theUpperTypeBound) } // Non-final to enable subclasses to provide more detailed information. override def toCanonicalForm: IsBaseReferenceValue = { ASObjectValue(isNull, isPrecise, theUpperTypeBound) } } /** * Represents some object value which may be null and may even not be properly initialized yet. */ case class ASObjectValue( isNull: Answer, override val isPrecise: Boolean, theUpperTypeBound: ClassType ) extends IsSObjectValue { override def toCanonicalForm: IsSObjectValue = this override def toString: String = { s"SObjectValue(type=${theUpperTypeBound.toJava},isNull=$isNull,isPrecise=$isPrecise)" } } /** * Represents an object value which is either null or properly initialized. */ case class AProperSObjectValue( isNull: Answer, override val isPrecise: Boolean, theUpperTypeBound: ClassType ) extends IsSObjectValue { override def toCanonicalForm: IsSObjectValue = this override def toString: String = { s"ProperSObjectValue(type=${theUpperTypeBound.toJava},isNull=$isNull,isPrecise=$isPrecise)" } } trait IsSArrayValue extends IsSReferenceValue[ArrayType] { override final def isArrayValue: Answer = isNull.negate // isNull is either "No" or "Unknown" override def isValueASubtypeOf( supertype: ReferenceType )( implicit classHierarchy: ClassHierarchy ): Answer = { classHierarchy.isASubtypeOf(theUpperTypeBound, supertype) match { case Yes => Yes case No if isPrecise || // the array's supertypes: Object, Serializable and Cloneable // are handled by domain.isASubtypeOf supertype.isClassType || theUpperTypeBound.elementType.isBaseType || ( supertype.isArrayType && supertype.asArrayType.elementType.isBaseType && ( theUpperTypeBound.dimensions >= supertype.asArrayType.dimensions || (theUpperTypeBound.componentType ne ClassType.Object) ) ) => No case _ => Unknown } } // Non-final to enable subclasses to identify cases of uninitializedThis/Variable cases. override final def verificationTypeInfo: VerificationTypeInfo = { ObjectVariableInfo(theUpperTypeBound) } } case class ASArrayValue( override val isNull: Answer, override val isPrecise: Boolean, theUpperTypeBound: ArrayType ) extends IsSArrayValue { override def toCanonicalForm: IsSArrayValue = this override def toString: String = { "SArrayValue(" + s"type=${theUpperTypeBound.toJava}," + s"isNull=$isNull,isPrecise=$isPrecise,length=)" } } case class ASArrayWithLengthValue( length: Int, theUpperTypeBound: ArrayType ) extends IsSArrayValue { override def isNull: Answer = No override def isPrecise: Boolean = true override def toCanonicalForm: IsSArrayValue = this override def toString: String = { s"SArrayValue(" + s"type=${theUpperTypeBound.toJava}," + s"isNull=$isNull,isPrecise=$isPrecise,length=$length)" } } private[value] trait IsPreciseNonNullReferenceValue extends IsSObjectValue { override def isNull: No.type = No override def isPrecise: Boolean = true } trait IsStringValue extends IsPreciseNonNullReferenceValue with ConstantValueInformationProvider[String] { def value: String override final def theUpperTypeBound: ClassType = ClassType.String override final def verificationTypeInfo: VerificationTypeInfo = { ObjectVariableInfo(ClassType.String) } override final def isValueASubtypeOf( supertype: ReferenceType )( implicit classHierarchy: ClassHierarchy ): Answer = { supertype.id match { case ClassType.ObjectId | ClassType.SerializableId | ClassType.CloneableId | ClassType.ComparableId | ClassType.StringId => Yes case _ => No } } override def constantValue: Option[String] = Some(value) } /** * Represents a constant string value. If the string is not yet completely initialized `value` * is `null`. This can never be the case for the parameters and return values of methods and * also for the values stored in fields. */ case class TheStringValue(value: String) extends IsStringValue { override def toCanonicalForm: TheStringValue = this } trait IsClassValue extends IsPreciseNonNullReferenceValue with ConstantValueInformationProvider[Type] { def value: Type override final def theUpperTypeBound: ClassType = ClassType.Class override final def verificationTypeInfo: VerificationTypeInfo = { ObjectVariableInfo(ClassType.Class) } override final def isValueASubtypeOf( supertype: ReferenceType )( implicit classHierarchy: ClassHierarchy ): Answer = { supertype.id match { case ClassType.ObjectId | ClassType.ClassId | ClassType.SerializableId | ClassType.AnnotatedElement.id | ClassType.Type.id | ClassType.GenericDeclaration.id => Yes case _ => No } } override def constantValue: Option[Type] = Some(value) } /** * Represents a constant class value. If the class is not yet completely initialized `value` * is `null`. This can never be the case for the parameters and return values of methods and * also for the values stored in fields. */ case class TheClassValue(value: Type) extends IsClassValue { override def toCanonicalForm: TheClassValue = this } /** * Extractor for instances of `IsReferenceValue` objects. * * @author Michael Eichberg */ object TypeOfReferenceValue { def unapply(rv: IsReferenceValue): Some[UIDSet[? <: ReferenceType]] = Some(rv.upperTypeBound) } trait IsMultipleReferenceValue extends IsReferenceValue { elidedAssert(baseValues.nonEmpty) override def allValues: Iterable[IsReferenceValue] = this.baseValues override def verificationTypeInfo: VerificationTypeInfo = { if (isNull.isYes) { NullVariableInfo } else { ObjectVariableInfo(leastUpperType.get) } } override def isValueASubtypeOf( supertype: ReferenceType )( implicit classHierarchy: ClassHierarchy ): Answer = boundary { // Recall that the client has to make an "isNull" check before calling // isValueASubtypeOf. Hence, at least one of the possible reference values // has to be non-null and this value's upper type bound has to be non-empty. // It may be the case that the subtype relation of each individual value – // when compared with supertype – is Unknown, but that the type of the // value as a whole is still known to be a subtype val isASubtypeOf = classHierarchy.isASubtypeOf(this.upperTypeBound, supertype) if (isASubtypeOf eq Yes) return Yes; if ((isASubtypeOf eq No) && isPrecise) return No; // Recall that the runtime type of this value can still be a subtype of supertype // even if this upperTypeBound is not a subtype of supertype. val values = baseValues.iterator.filter(_.isNull.isNoOrUnknown) var answer: Answer = values.next().isValueASubtypeOf(supertype) values foreach { value => /* the first value is already removed */ if (answer eq Unknown) break(answer); // isSubtype answer = answer.join(value.isValueASubtypeOf(supertype)) } answer } override def toCanonicalForm: IsReferenceValue = { var uniqueBaseValues = Set.empty[IsReferenceValue] uniqueBaseValues = baseValues.foldLeft(uniqueBaseValues)(_ + _.toCanonicalForm) // ...toSet is required because we potentially drop domain specific information // and afterward the values are identical. if (uniqueBaseValues.size == 1 && uniqueBaseValues.head.isNull == this.isNull && uniqueBaseValues.head.isPrecise == this.isPrecise && uniqueBaseValues.head.upperTypeBound == this.upperTypeBound ) { uniqueBaseValues.head } else { AMultipleReferenceValue( uniqueBaseValues, isNull, isPrecise, upperTypeBound, leastUpperType ) } } } case class AMultipleReferenceValue( baseValues: Iterable[IsReferenceValue], isNull: Answer, isPrecise: Boolean, upperTypeBound: UIDSet[? <: ReferenceType], leastUpperType: Option[ReferenceType] // None in case of "null" ) extends IsMultipleReferenceValue { elidedAssert((isNull.isYes && leastUpperType.isEmpty) || (isNull.isNoOrUnknown && leastUpperType.isDefined)) elidedAssert(baseValues.forall(_.getClass.getPackage.getName == ("org.opalj.value"))) override final def isArrayValue: Answer = { leastUpperType match { case Some(_: ArrayType) => isNull.negate // isNull is either No or unknown case _ => No } } override def toCanonicalForm: IsReferenceValue = this override def toString: String = { "MultipleReferenceValue(" + s"type=${upperTypeBound.map(_.toJava).toList.sorted.mkString(" with ")}," + s"isNull=$isNull,isPrecise=$isPrecise," + s"baseValues=${baseValues.map(_.toString).mkString("{ ", ", ", " }")})" } } /** * Defines an extractor method for instances of `IsReferenceValue` objects. * * @note To ensure that the generic type can be matched, it may be necessary to first cast * a ''generic'' `org.opalj.ai.ValuesDomain.DomainValue` to an * `org.opalj.ai.ValuesDomain.DomainReferenceValue`. * {{{ * val d : Domain = ... * val d.DomainReferenceValue(v) = /*some domain value; e.g., operands.head*/ * val BaseReferenceValues(values) = v * values... * }}} * * @author Michael Eichberg */ object BaseReferenceValues { def unapply(rv: IsReferenceValue): Some[Iterable[IsReferenceValue]] = Some(rv.allValues) }