Skip to content

Commit

Permalink
fixed an issue related to uninitialized values
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Eichberg <mail@michael-eichberg.de>
  • Loading branch information
Delors committed Dec 12, 2017
1 parent 56368ee commit 5fa2e9e
Show file tree
Hide file tree
Showing 7 changed files with 241 additions and 195 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* BSD 2-Clause License:
* Copyright (c) 2009 - 2017
* Software Technology Group
* Department of Computer Science
* Technische Universität Darmstadt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.opalj
package ai
package domain
package l0

import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import java.net.URL

import org.opalj.br.Method
import org.opalj.br.analyses.Project

/**
* This system test(suite) just loads a very large number of class files and performs
* an abstract interpretation of all methods using the l1.TypeCheckingDomain. It basically
* tests if we can load and process a large number of different classes without exceptions.
*
* @author Michael Eichberg
*/
@RunWith(classOf[JUnitRunner])
class TypeCheckingDomainTest extends DomainTestInfrastructure("l0.TypeCheckingDomain") {

type AnalyzedDomain = l0.TypeCheckingDomain

def Domain(project: Project[URL], method: Method): l0.TypeCheckingDomain = {
new l0.TypeCheckingDomain(project, method)
}

}
5 changes: 2 additions & 3 deletions OPAL/ai/src/main/scala/org/opalj/ai/AI.scala
Original file line number Diff line number Diff line change
Expand Up @@ -270,10 +270,9 @@ abstract class AI[D <: Domain]( final val IdentifyDeadVariables: Boolean = true)
if (!method.isStatic) {
val thisType = method.classFile.thisType
val thisValue =
if (method.isConstructor &&
(method.classFile.thisType ne ObjectType.Object)) {
if (method.isConstructor && (method.classFile.thisType ne ObjectType.Object)) {
// ... we have an uninitialized this!
domain.NewObject(origin(localVariableIndex), thisType)
domain.UninitializedThis(thisType)
} else {
domain.NonNullObjectValue(origin(localVariableIndex), thisType)
}
Expand Down
25 changes: 19 additions & 6 deletions OPAL/ai/src/main/scala/org/opalj/ai/ReferenceValuesFactory.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,9 @@ trait ReferenceValuesFactory extends ExceptionsFactory { domain ⇒
/**
* Creates a new `DomainValue` that represents ''a new,
* uninitialized instance of an object of the given type''. The object was
* created by the (`NEW`) instruction with the specified program counter or - if value
* origin is -1 - represents this in a constructor call before the call of the super
* constructor.
* created by the (`NEW`) instruction with the specified program counter.
*
* OPAL calls this method when it evaluates `newobject` instructions or creates the initial
* locals of constructors.
* OPAL calls this method when it evaluates `newobject` instructions.
* If the bytecode is valid a call of one of the (super) object's constructors will
* subsequently initialize the object.
*
Expand All @@ -108,14 +105,30 @@ trait ReferenceValuesFactory extends ExceptionsFactory { domain ⇒
* - Initialized: '''no''' (only the memory is allocated for the object)
* - Type: '''precise''' (i.e., this type is not an upper bound,
* the type correctly models the runtime type.)
* - Null: '''No''' (This value is not `null`.)
* - Null: '''no''' (This value is not `null`.)
*
* @note Instances of arrays are created by the `newarray` and
* `multianewarray` instructions and in both cases an exception may be thrown
* (e.g., `NegativeArraySizeException`).
*/
def NewObject(origin: ValueOrigin, objectType: ObjectType): DomainReferenceValue

/**
* Creates a new `DomainValue` that represents ''the `this` value of a constructor before the
* super constructor is called. Hence, the value origin is necessarily always -1.
*
* OPAL calls this method when it creates the initial locals for constructors.
*
* ==Summary==
* The properties of the domain value are:
* - Initialized: '''no''' (only the memory is allocated for the object)
* - Type: '''upper bound'''
* - Null: '''no''' (This value is not `null`.)
*
* @note Instances of arrays are never uninitialized.
*/
def UninitializedThis(objectType: ObjectType): DomainReferenceValue

/**
* Factory method to create a `DomainValue` that represents an '''initialized'''
* reference value of the given type and that was created (explicitly or implicitly)
Expand Down
151 changes: 148 additions & 3 deletions OPAL/ai/src/main/scala/org/opalj/ai/domain/l0/TypeCheckingDomain.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,22 @@ package ai
package domain
package l0

import scala.reflect.ClassTag

import org.opalj.collection.immutable.UIDSet
import org.opalj.br.ArrayType
import org.opalj.br.ObjectType
import org.opalj.br.Method
import org.opalj.br.MethodDescriptor
import org.opalj.br.ClassHierarchy
import org.opalj.br.analyses.SomeProject

/**
* This is the domain that can be used to compute the information required to compute the
* [[org.opalj.br.StackMapTable]]
* Domain that can be used to compute the information required to compute the
* [[org.opalj.br.StackMapTable]]; i.e., we precisely track the information regarding the
* initialization status of references. (This is generally not necessary for the other domains
* because we make the correct bytecode assumption over there and, therefore, never see an
* invalid usage of an uninitialized object reference.)
*/
class TypeCheckingDomain(
val classHierarchy: ClassHierarchy,
Expand All @@ -55,11 +64,147 @@ class TypeCheckingDomain(
with ThrowAllPotentialExceptionsConfiguration
with IgnoreSynchronization
with DefaultTypeLevelHandlingOfMethodResults
with TypeCheckingReferenceValues // we override the handling for invokespecial...
with DefaultTypeLevelReferenceValues
with PostEvaluationMemoryManagement
with DefaultExceptionsFactory
with TheClassHierarchy
with TheMethod {

def this(project: SomeProject, method: Method) {
this(project.classHierarchy, method)
}

type AReferenceValue = ReferenceValue
type DomainReferenceValue = AReferenceValue

final val DomainReferenceValue: ClassTag[DomainReferenceValue] = implicitly

type DomainNullValue = NullValue
type DomainObjectValue = ObjectValue
type DomainArrayValue = ArrayValue

val TheNullValue: DomainNullValue = new NullValue()

// -----------------------------------------------------------------------------------
//
// REPRESENTATION OF REFERENCE VALUES
//
// -----------------------------------------------------------------------------------

protected class InitializedObjectValue(
theUpperTypeBound: ObjectType
) extends SObjectValue(theUpperTypeBound) {
this: DomainObjectValue

// WIDENING OPERATION
override protected def doJoin(pc: PC, other: DomainValue): Update[DomainValue] = {
other match {
case _: UninitializedObjectValue MetaInformationUpdateIllegalValue
case that super.doJoin(pc, that)
}
}
}

/**
* @param vo The origin of the new instruction or -1 if this represents "uninitialized size".
*/
protected case class UninitializedObjectValue(
theType: ObjectType,
vo: ValueOrigin
) extends SObjectValue(theType) {
this: DomainObjectValue

override def isPrecise: Boolean = vo != -1 // we are talking about uninitialized this

// joins of an uninitialized value with null results in an illegal value
override def isNull: Answer = No

// WIDENING OPERATION
override protected def doJoin(pc: PC, other: DomainValue): Update[DomainValue] = {
other match {
case UninitializedObjectValue(`theType`, `vo`) NoUpdate
// this value is not completely useable...
case _ MetaInformationUpdateIllegalValue
}
}

override def abstractsOver(other: DomainValue): Boolean = {
other match {
case that: UninitializedObjectValue if (
(that.theType eq this.theType) && this.vo == that.vo
)
true
case _
false
}
}

override def adapt(target: TargetDomain, origin: ValueOrigin): target.DomainValue = {
target.NewObject(origin, theUpperTypeBound)
}

override def toString: String = {
if (vo == -1)
"UninitializedThis"
else
s"${theType.toJava}(uninitialized;origin=$vo)"
}
}

override def invokespecial(
pc: PC,
declaringClass: ObjectType,
isInterface: Boolean,
name: String,
methodDescriptor: MethodDescriptor,
operands: Operands
): MethodCallResult = {
if (name == "<init>") {
val receiver = operands.last
// the value is now initialized and we have to update the stack/locals
val UninitializedObjectValue(theType, _) = receiver
val initializedObjectValue = new InitializedObjectValue(theType)
updateAfterExecution(receiver, initializedObjectValue, TheIllegalValue)
}
super.invokespecial(pc, declaringClass, isInterface, name, methodDescriptor, operands)
}

// -----------------------------------------------------------------------------------
//
// FACTORY METHODS
//
// -----------------------------------------------------------------------------------

override def NullValue(origin: ValueOrigin): DomainNullValue = TheNullValue

override def NewObject(pc: PC, objectType: ObjectType): DomainObjectValue = {
new UninitializedObjectValue(objectType, pc)
}

override def UninitializedThis(objectType: ObjectType): DomainObjectValue = {
new UninitializedObjectValue(objectType, -1)
}

override def InitializedObjectValue(pc: PC, objectType: ObjectType): DomainObjectValue = {
new InitializedObjectValue(objectType)
}

override def ObjectValue(origin: ValueOrigin, objectType: ObjectType): DomainObjectValue = {
new InitializedObjectValue(objectType)
}

override def ObjectValue(
origin: ValueOrigin,
upperTypeBound: UIDSet[ObjectType]
): DomainObjectValue = {
if (upperTypeBound.isSingletonSet)
ObjectValue(origin, upperTypeBound.head)
else
new MObjectValue(upperTypeBound)
}

override def ArrayValue(origin: ValueOrigin, arrayType: ArrayType): DomainArrayValue = {
new ArrayValue(arrayType)
}

}
Loading

0 comments on commit 5fa2e9e

Please sign in to comment.