Skip to content

Commit

Permalink
Additional corrections on a call
Browse files Browse the repository at this point in the history
  • Loading branch information
EgorkaKulikov committed Sep 30, 2022
1 parent 8b587a9 commit 32a3394
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ internal class UtBotFieldModificatorsTest {

//We use sorting here to make comparing with sorted in advance expected collections easier
private fun runFieldModificatorsSearch(analysisMode: AnalysisMode) =
fieldsModificatorsSearcher.findModificators(analysisMode, null)
fieldsModificatorsSearcher.findModificators(analysisMode)
.map { (key, value) ->
val modificatorNames = value.filterNot { it.name.startsWith("direct_set_") }.map { it.name }
key.name to modificatorNames.toSortedSet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,21 @@ import org.utbot.framework.plugin.api.hasDefaultValue
import org.utbot.framework.plugin.api.isMockModel
import org.utbot.framework.plugin.api.util.defaultValueModel
import org.utbot.framework.plugin.api.util.executableId
import org.utbot.framework.plugin.api.util.isSubtypeOf
import org.utbot.framework.plugin.api.util.jClass
import org.utbot.framework.util.nextModelName
import java.lang.reflect.Constructor
import java.util.IdentityHashMap

/**
* Creates [UtAssembleModel] from any [UtModel] or it's inner models if possible
* during generation test for [packageName].
tmp * during generation test for [basePackageName].
*
* Needs utContext be set and Soot be initialized.
*
* Note: Caches class related information, can be reused if classes don't change.
*/
class AssembleModelGenerator(private val packageName: String) {
class AssembleModelGenerator(private val basePackageName: String) {

//Instantiated models are stored to avoid cyclic references during reference graph analysis
private val instantiatedModels: IdentityHashMap<UtModel, UtReferenceModel> =
Expand Down Expand Up @@ -100,8 +101,7 @@ class AssembleModelGenerator(private val packageName: String) {
* Note: Two identity equal [UtModel]s are represented by one instance model.
*/
fun createAssembleModels(models: List<UtModel>): IdentityHashMap<UtModel, UtModel> {
val allModelsAreLocatedInPackage = models.all { it.classId.packageName.startsWith(packageName)}
if (!UtSettings.useAssembleModelGenerator || !allModelsAreLocatedInPackage) {
if (!UtSettings.useAssembleModelGenerator) {
return IdentityHashMap<UtModel, UtModel>().apply { models.forEach { put(it, it) } }
}

Expand Down Expand Up @@ -172,7 +172,7 @@ class AssembleModelGenerator(private val packageName: String) {
private fun assembleModel(utModel: UtModel): UtModel {
val collectedCallChain = callChain.toMutableList()

// we cannot create an assemble model for an anonymous class instance
// We cannot create an assemble model for an anonymous class instance
if (utModel.classId.isAnonymous) {
return utModel
}
Expand Down Expand Up @@ -257,7 +257,7 @@ class AssembleModelGenerator(private val packageName: String) {
if (fieldId.isFinal) {
throw AssembleException("Final field $fieldId can't be set in an object of the class $classId")
}
if (!fieldId.type.isAccessibleFrom(packageName)) {
if (!fieldId.type.isAccessibleFrom(basePackageName)) {
throw AssembleException(
"Field $fieldId can't be set in an object of the class $classId because its type is inaccessible"
)
Expand Down Expand Up @@ -399,10 +399,10 @@ class AssembleModelGenerator(private val packageName: String) {
}

private val ClassId.isVisible : Boolean
get() = this.isPublic || !this.isPrivate && this.packageName.startsWith(this@AssembleModelGenerator.packageName)
get() = this.isPublic || !this.isPrivate && this.packageName.startsWith(basePackageName)

private val Constructor<*>.isVisible : Boolean
get() = this.isPublic || !this.isPrivate && this.declaringClass.packageName.startsWith(packageName)
get() = this.isPublic || !this.isPrivate && this.declaringClass.packageName.startsWith(basePackageName)

/**
* Creates setter or direct setter call to set a field.
Expand Down Expand Up @@ -441,7 +441,7 @@ class AssembleModelGenerator(private val packageName: String) {
* Finds setters and direct accessors for fields of particular class.
*/
private fun findSettersAndDirectAccessors(classId: ClassId): Map<FieldId, StatementId> {
val allModificatorsOfClass = modificatorsSearcher.findModificators(SettersAndDirectAccessors, classId)
val allModificatorsOfClass = modificatorsSearcher.findModificators(SettersAndDirectAccessors)

return allModificatorsOfClass
.mapNotNull { (fieldId, possibleModificators) ->
Expand All @@ -457,9 +457,12 @@ class AssembleModelGenerator(private val packageName: String) {
*/
private fun chooseModificator(
fieldId: FieldId,
settersAndDirectAccessors: Set<StatementId>
settersAndDirectAccessors: Set<StatementId>,
): StatementId? {
val directAccessors = settersAndDirectAccessors.filterIsInstance<DirectFieldAccessId>()
val directAccessors = settersAndDirectAccessors
.filterIsInstance<DirectFieldAccessId>()
.filter {it.fieldId.isAccessibleFrom(basePackageName) }

if (directAccessors.any()) {
return directAccessors.singleOrNull()
?: throw AssembleException(
Expand All @@ -468,7 +471,9 @@ class AssembleModelGenerator(private val packageName: String) {
}

if (settersAndDirectAccessors.any()) {
return settersAndDirectAccessors.singleOrNull()
return settersAndDirectAccessors
.filterIsInstance<ExecutableId>()
.singleOrNull { it.isAccessibleFrom(basePackageName) }
?: throw AssembleException(
"Field $fieldId has more than one setter: ${settersAndDirectAccessors.joinToString(" ")}"
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ import org.utbot.framework.plugin.api.util.voidClassId
*
* @param context context in which code is generated (it is needed because the method needs to know package and language)
*/
// TODO: change parameter from packageName: String to context: CgContext in ClassId.isAccessibleFrom and ExecutableId.isAccessibleFrom ?
private fun FieldId.isAccessibleFrom(context: CgContext): Boolean {
val packageName = context.testClassPackageName
fun FieldId.isAccessibleFrom(packageName: String): Boolean {
val isClassAccessible = declaringClass.isAccessibleFrom(packageName)
val isAccessibleByVisibility = isPublic || (declaringClass.packageName == packageName && (isPackagePrivate || isProtected))
val isAccessibleFromPackageByModifiers = isAccessibleByVisibility && !isSynthetic
Expand All @@ -36,7 +34,7 @@ internal infix fun FieldId.canBeReadFrom(context: CgContext): Boolean {
return true
}

return isAccessibleFrom(context)
return isAccessibleFrom(context.testClassPackageName)
}

private fun FieldId.canBeSetViaSetterFrom(context: CgContext): Boolean =
Expand All @@ -49,12 +47,12 @@ internal fun FieldId.canBeSetFrom(context: CgContext): Boolean {
if (context.codegenLanguage == CodegenLanguage.KOTLIN) {
// Kotlin will allow direct write access if both getter and setter is defined
// !isAccessibleFrom(context) is important here because above rule applies to final fields only if they are not accessible in Java terms
if (!isAccessibleFrom(context) && !isStatic && canBeReadViaGetterFrom(context) && canBeSetViaSetterFrom(context)) {
if (!isAccessibleFrom(context.testClassPackageName) && !isStatic && canBeReadViaGetterFrom(context) && canBeSetViaSetterFrom(context)) {
return true
}
}

return isAccessibleFrom(context) && !isFinal
return isAccessibleFrom(context.testClassPackageName) && !isFinal
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,22 @@ class DirectAccessorsAnalyzer {
*/
fun collectDirectAccesses(classIds: Set<ClassId>): Set<DirectFieldAccessId> =
classIds
.flatMap { classId -> collectFieldsInPackage(classId) }
.flatMap { classId -> collectFields(classId) }
.map { fieldId -> DirectFieldAccessId(fieldId.declaringClass, directSetterName(fieldId), fieldId) }
.toSet()

/**
* Collect all fields with different non-private modifiers from class [classId].
* Collect all fields with different non-private modifiers
* from class [classId] or it's base classes.
*/
private fun collectFieldsInPackage(classId: ClassId): Set<FieldId> {
private fun collectFields(classId: ClassId): Set<FieldId> {
var clazz = classId.jClass

val fieldIds = mutableSetOf<Field>()
fieldIds += clazz.declaredFields.filterNot { Modifier.isPrivate(it.modifiers) }
while (clazz.superclass != null) {
clazz = clazz.superclass
fieldIds += clazz.declaredFields.filter { Modifier.isPublic(it.modifiers) || Modifier.isProtected(it.modifiers) }
fieldIds += clazz.declaredFields.filterNot { Modifier.isPrivate(it.modifiers) }
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,43 +14,7 @@ class UtBotFieldsModificatorsSearcher {

fun delete(classIds: Set<ClassId>) = statementsStorage.delete(classIds)

/**
* Finds field modificators.
*
* @param analysisMode represents which type of modificators (e.g. setters) are considered.
* @param methodUnderTest describes an analyzed method an it's location.
*/
fun findModificators(analysisMode: AnalysisMode, classUnderTest: ClassId? = null): Map<FieldId, Set<StatementId>> {
val modificators = findModificators(analysisMode)

if (classUnderTest == null) {
return modificators
}

val filteredModifications = mutableMapOf<FieldId, Set<StatementId>>()
for ((fieldId, statements) in modificators) {
val filteredStmts = statements.filter { stmt -> fieldId.isAccessibleBy(stmt, classUnderTest!!) }
filteredModifications[fieldId] = filteredStmts.toSet()
}

return filteredModifications
}

/**
* Verifies that this field is accessible with [statementId] from the location of [methodUnderTest].
*/
private fun FieldId.isAccessibleBy(statementId: StatementId, classUnderTest: ClassId): Boolean {
val basePackageName = classUnderTest.packageName
val classPackageName = statementId.classId.packageName

if (this.isPublic) return true
if (this.isProtected) return classPackageName == basePackageName || statementId.classId.isSubtypeOf(classUnderTest)
if (this.isPackagePrivate) return classPackageName == basePackageName
if (this.isPrivate) return classUnderTest == statementId.classId
return error("Unexpected modifier for field $this")
}

private fun findModificators(analysisMode: AnalysisMode): Map<FieldId, Set<StatementId>> {
fun findModificators(analysisMode: AnalysisMode): Map<FieldId, Set<StatementId>> {
statementsStorage.updateCaches()
return findModificatorsInCache(analysisMode)
}
Expand Down

0 comments on commit 32a3394

Please sign in to comment.