Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve handling of java/lang/System entry points in CG generation #241

Draft
wants to merge 10 commits into
base: develop
Choose a base branch
from
Prev Previous commit
Next Next commit
Formatting, file headers
  • Loading branch information
johannesduesing committed Dec 2, 2024
commit 532bd64057ee20d11ffd3f7f53459d511e1ffac0
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj.br.analyses.cg
johannesduesing marked this conversation as resolved.
Show resolved Hide resolved

import net.ceedubs.ficus.Ficus._
import org.opalj.br.{Field, ReferenceType}
import org.opalj.br.analyses.{ProjectInformationKey, ProjectInformationKeys, SomeProject}
import org.opalj.br.Field
import org.opalj.br.ReferenceType
import org.opalj.br.analyses.ProjectInformationKey
import org.opalj.br.analyses.ProjectInformationKeys
import org.opalj.br.analyses.SomeProject
import org.opalj.collection.immutable.UIDSet

object InitialInstantiatedFieldsKey extends ProjectInformationKey[Iterable[(Field, UIDSet[ReferenceType])], Nothing]{
import net.ceedubs.ficus.Ficus._

/**
* The ProjectInformationKey to iterate all fields that shall be considered to be instantiated by default. Each field
* is associated with a set of ReferenceTypes to be considered instantiated for this field.
*/
object InitialInstantiatedFieldsKey extends ProjectInformationKey[Iterable[(Field, UIDSet[ReferenceType])], Nothing] {

final val ConfigKeyPrefix = "org.opalj.br.analyses.cg.InitialInstantiatedFieldsKey."

Expand All @@ -14,7 +23,7 @@ object InitialInstantiatedFieldsKey extends ProjectInformationKey[Iterable[(Fiel
override def compute(project: SomeProject): Iterable[(Field, UIDSet[ReferenceType])] = {
val key = ConfigKeyPrefix + "analysis"
val configuredAnalysis = project.config.as[Option[String]](key)
if(configuredAnalysis.isEmpty) {
if (configuredAnalysis.isEmpty) {
throw new IllegalArgumentException(
"No InitialInstantiatedFieldsKey configuration available; Instantiated fields cannot be computed!"
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj.br.analyses.cg

import net.ceedubs.ficus.Ficus._
import org.opalj.br.{Field, ObjectType, ReferenceType}
import org.opalj.br.Field
import org.opalj.br.ObjectType
import org.opalj.br.ReferenceType
import org.opalj.br.analyses.SomeProject
import org.opalj.collection.immutable.UIDSet
import org.opalj.log.{LogContext, OPALLogger}
import org.opalj.log.LogContext
import org.opalj.log.OPALLogger

import net.ceedubs.ficus.Ficus._

/**
* This trait represents objects that detect fields that should be considered to be instantiated no matter what. This
Expand Down Expand Up @@ -33,16 +38,14 @@ trait DefaultInstantiatedFieldsFinder extends InstantiatedFieldsFinder {
trait SoundLibraryInstantiatedFieldsFinder extends InstantiatedFieldsFinder {

override def collectInstantiatedFields(project: SomeProject): Iterable[(Field, UIDSet[ReferenceType])] = {
if(!project.libraryClassFilesAreInterfacesOnly)
if (!project.libraryClassFilesAreInterfacesOnly)
return super.collectInstantiatedFields(project);

super.collectInstantiatedFields(project) ++ project
.allLibraryClassFiles
.flatMap(_.fields)
.filter(_.fieldType.isReferenceType)
.map{ field =>
(field, UIDSet(field.fieldType.asReferenceType))
}
.map { field => (field, UIDSet(field.fieldType.asReferenceType)) }
}

}
Expand All @@ -64,74 +67,81 @@ trait ConfigurationInstantiatedFieldsFinder extends InstantiatedFieldsFinder {
implicit val logContext: LogContext = project.logContext
var instantiatedFields = Set.empty[(Field, UIDSet[ReferenceType])]

if(!project.config.hasPath(additionalInstantiatedFieldsKey)){
if (!project.config.hasPath(additionalInstantiatedFieldsKey)) {
OPALLogger.info(
"project configuration",
s"configuration key $additionalInstantiatedFieldsKey is missing; " +
"no additional fields are considered instantiated"
"no additional fields are considered instantiated"
)
return instantiatedFields
}

val fieldDefinitions = try {
project.config.as[List[InstantiatedFieldContainer]](additionalInstantiatedFieldsKey)
} catch {
case e: Throwable =>
OPALLogger.error(
"project configuration - recoverable",
s"configuration key $additionalInstantiatedFieldsKey is invalid; " +
"see InstantiatedFieldsFinder documentation",
e
)
return instantiatedFields;
}
val fieldDefinitions =
try {
project.config.as[List[InstantiatedFieldContainer]](additionalInstantiatedFieldsKey)
} catch {
case e: Throwable =>
OPALLogger.error(
"project configuration - recoverable",
s"configuration key $additionalInstantiatedFieldsKey is invalid; " +
"see InstantiatedFieldsFinder documentation",
e
)
return instantiatedFields;
}

fieldDefinitions foreach { fieldDefinition =>
project.classFile(ObjectType(fieldDefinition.declaringClass)) match {
case Some(cf) if cf.findField(fieldDefinition.name).nonEmpty =>

cf.findField(fieldDefinition.name).foreach{ field =>
fieldDefinition.typeHint match {
case Some(typeHint) if field.fieldType.isReferenceType =>
val considerSubtypes = typeHint.endsWith("+")
val fqn = if(considerSubtypes) typeHint.substring(0, typeHint.length - 1) else typeHint
val hintType = ObjectType(fqn)

if(field.fieldType.isObjectType &&
project.classHierarchy.isASubtypeOf(hintType, field.fieldType.asObjectType).isNo){
// If the given type hint is not a subtype of the field type, we warn and
// fall back to default behavior
OPALLogger.warn(
"project configuration - recoverable",
s"type hint $typeHint for instantiated field not valid"
)
instantiatedFields += ((field, UIDSet(field.fieldType.asReferenceType)))
} else {
if (considerSubtypes)
instantiatedFields += ((field, UIDSet.fromSpecific[ReferenceType](project.classHierarchy.allSubtypes(hintType, reflexive = true))))
else
instantiatedFields += ((field, UIDSet(hintType)))
}

case None if field.fieldType.isReferenceType =>
instantiatedFields += ((field, UIDSet(field.fieldType.asReferenceType)))
case _ =>
project.classFile(ObjectType(fieldDefinition.declaringClass)) match {
case Some(cf) if cf.findField(fieldDefinition.name).nonEmpty =>
cf.findField(fieldDefinition.name).foreach { field =>
fieldDefinition.typeHint match {
case Some(typeHint) if field.fieldType.isReferenceType =>
val considerSubtypes = typeHint.endsWith("+")
val fqn = if (considerSubtypes) typeHint.substring(0, typeHint.length - 1) else typeHint
val hintType = ObjectType(fqn)

if (field.fieldType.isObjectType &&
project.classHierarchy.isASubtypeOf(hintType, field.fieldType.asObjectType).isNo
) {
// If the given type hint is not a subtype of the field type, we warn and
// fall back to default behavior
OPALLogger.warn(
"project configuration - recoverable",
s"type hint $typeHint for instantiated field not valid"
)
instantiatedFields += ((field, UIDSet(field.fieldType.asReferenceType)))
} else {
if (considerSubtypes)
instantiatedFields += ((
field,
UIDSet.fromSpecific[ReferenceType](project.classHierarchy.allSubtypes(
hintType,
reflexive = true
))
))
else
instantiatedFields += ((field, UIDSet(hintType)))
}

case None if field.fieldType.isReferenceType =>
instantiatedFields += ((field, UIDSet(field.fieldType.asReferenceType)))
case _ =>
// This is okay, primitive types don't need to be initialized
}
}

case Some(_) =>
OPALLogger.warn(
"project configuration - recoverable",
s"configured field named ${fieldDefinition.name} not found on" +
s" class ${fieldDefinition.declaringClass}"
)
case None =>
OPALLogger.warn(
"project configuration - recoverable",
s"class ${fieldDefinition.declaringClass} not found for configured instantiated field"
)
}
}
}

case Some(_) =>
OPALLogger.warn(
"project configuration - recoverable",
s"configured field named ${fieldDefinition.name} not found on" +
s" class ${fieldDefinition.declaringClass}"
)
case None =>
OPALLogger.warn(
"project configuration - recoverable",
s"class ${fieldDefinition.declaringClass} not found for configured instantiated field"
)
}
}

super.collectInstantiatedFields(project) ++ instantiatedFields
Expand All @@ -150,6 +160,3 @@ object DefaultInstantiatedFieldsFinder
object SoundLibraryInstantiatedFieldsFinder
extends SoundLibraryInstantiatedFieldsFinder
with ConfigurationInstantiatedFieldsFinder



Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ package cg
package rta

import scala.language.existentials

import org.opalj.br.DeclaredMethod
import org.opalj.br.ObjectType
import org.opalj.br.ReferenceType
import org.opalj.br.analyses.ProjectInformationKeys
import org.opalj.br.analyses.SomeProject
import org.opalj.br.analyses.cg.{InitialInstantiatedFieldsKey, InitialInstantiatedTypesKey}
import org.opalj.br.analyses.cg.InitialInstantiatedFieldsKey
import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey
import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler
import org.opalj.br.fpcf.ContextProviderKey
import org.opalj.br.fpcf.FPCFAnalysis
Expand Down Expand Up @@ -240,7 +242,6 @@ object InstantiatedTypesAnalysisScheduler extends BasicFPCFTriggeredAnalysisSche
val initialInstantiatedTypes = UIDSet[ReferenceType](p.get(InitialInstantiatedTypesKey).toSeq: _*) ++
p.get(InitialInstantiatedFieldsKey).flatMap(_._2)


ps.preInitialize[SomeProject, InstantiatedTypes](p, InstantiatedTypes.key) {
case _: EPK[_, _] => InterimEUBP(p, InstantiatedTypes(initialInstantiatedTypes))
case eps => throw new IllegalStateException(s"unexpected property: $eps")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package cg
package xta

import scala.collection.mutable.ArrayBuffer

import org.opalj.br.ArrayType
import org.opalj.br.DeclaredMethod
import org.opalj.br.Field
Expand All @@ -18,7 +19,10 @@ import org.opalj.br.analyses.DeclaredFieldsKey
import org.opalj.br.analyses.DeclaredMethodsKey
import org.opalj.br.analyses.ProjectInformationKeys
import org.opalj.br.analyses.SomeProject
import org.opalj.br.analyses.cg.{ClosedPackagesKey, InitialEntryPointsKey, InitialInstantiatedFieldsKey, InitialInstantiatedTypesKey}
import org.opalj.br.analyses.cg.ClosedPackagesKey
import org.opalj.br.analyses.cg.InitialEntryPointsKey
import org.opalj.br.analyses.cg.InitialInstantiatedFieldsKey
import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey
import org.opalj.br.fpcf.BasicFPCFTriggeredAnalysisScheduler
import org.opalj.br.fpcf.ContextProviderKey
import org.opalj.br.fpcf.FPCFAnalysis
Expand Down Expand Up @@ -335,7 +339,7 @@ class InstantiatedTypesAnalysisScheduler(

// During system initialization, some native methods are called to set certain fields.
// These fields can be set as instantiated via the configuration, see InitialFieldsFinder.
initialInstantiatedFields.foreach{ case (f: Field, t: UIDSet[ReferenceType]) => initializeField(f, t) }
initialInstantiatedFields.foreach { case (f: Field, t: UIDSet[ReferenceType]) => initializeField(f, t) }

def isRelevantArrayType(rt: Type): Boolean =
rt.isArrayType && rt.asArrayType.elementType.isObjectType
Expand Down