Skip to content

Modificators processing in AssembleModelGenerator and UtBotFieldModificatorsSearcher corrected #1029

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

Merged
merged 6 commits into from
Oct 3, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.arrays;
package org.utbot.examples.assemble;

/**
* A class with array of objects that are arrays of complex fields themselves.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.arrays;
package org.utbot.examples.assemble;

/**
* A class with a two-dimensional array field.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.arrays;
package org.utbot.examples.assemble;

/**
* A class with an array with a default value.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.arrays;
package org.utbot.examples.assemble;

import org.utbot.examples.assemble.PrimitiveFields;

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.constructors;
package org.utbot.examples.assemble;

/**
* A class without default constructor and with complex one.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.constructors;
package org.utbot.examples.assemble;

/**
* A class without default constructor and with complex one,
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.constructors;
package org.utbot.examples.assemble;

public class ConstructorModifyingStatic {

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.defaults;
package org.utbot.examples.assemble;

/**
* A class with a field with default value that is not a default value of type.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.utbot.examples.assemble.defaults;
package org.utbot.examples.assemble;

public class DefaultFieldModifiedInConstructor {
int z;
public int z;

@SuppressWarnings("Unused")
DefaultFieldModifiedInConstructor(int z_) {
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.defaults;
package org.utbot.examples.assemble;

/**
* A class with a field with default value that is not a default value of type.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.defaults;
package org.utbot.examples.assemble;

/**
* A class with a field with setter default value that is not a default value of type.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.utbot.examples.assemble;

/**
* Need to be located at the same package as [AssembleTestUtils]
* because requires a setter for package-private field.
*/
public class DefaultPackagePrivateField {
int z = 10;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.constructors;
package org.utbot.examples.assemble;

/**
* A class with a primitive constructor that inherits a complex constructor.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.constructors;
package org.utbot.examples.assemble;

/**
* A class with a primitive constructor that inherits another primitive constructor.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.arrays;
package org.utbot.examples.assemble;

/**
* A class with an array with elements of primitive type.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.constructors;
package org.utbot.examples.assemble;

/**
* A class without default constructor and with primitive one.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.constructors;
package org.utbot.examples.assemble;

/**
* A class without default constructor and with another one with default field
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.constructors;
package org.utbot.examples.assemble;

/**
* A class with private constructor.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.constructors;
package org.utbot.examples.assemble;

/**
* A class with a constructor that seems to be complex
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.statics;
package org.utbot.examples.assemble;

/**
* A class with primitive constructor and static field
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package org.utbot.examples.assemble.arrays;
package org.utbot.examples.assemble.another;

/**
* A test class with fake method under test.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.utbot.examples.manual.examples;

import org.utbot.examples.assemble.arrays.ArrayOfComplexArrays;
import org.utbot.examples.assemble.ArrayOfComplexArrays;

public class ArrayOfComplexArraysExample {
public int getValue(ArrayOfComplexArrays a) {
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.utbot.examples.manual.examples;

import org.utbot.examples.assemble.arrays.ArrayOfPrimitiveArrays;
import org.utbot.examples.assemble.ArrayOfPrimitiveArrays;

public class ArrayOfPrimitiveArraysExample {
int assign10(ArrayOfPrimitiveArrays a) {
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.utbot.examples.manual.examples;

import org.utbot.examples.assemble.arrays.AssignedArray;
import org.utbot.examples.assemble.AssignedArray;

public class AssignedArrayExample {
public void foo(AssignedArray aa) {
Original file line number Diff line number Diff line change
@@ -9,10 +9,10 @@
import org.utbot.common.PathUtil;
import org.utbot.examples.assemble.DirectAccess;
import org.utbot.examples.assemble.PrimitiveFields;
import org.utbot.examples.assemble.arrays.ArrayOfComplexArrays;
import org.utbot.examples.assemble.arrays.ArrayOfPrimitiveArrays;
import org.utbot.examples.assemble.arrays.AssignedArray;
import org.utbot.examples.assemble.arrays.ComplexArray;
import org.utbot.examples.assemble.ArrayOfComplexArrays;
import org.utbot.examples.assemble.ArrayOfPrimitiveArrays;
import org.utbot.examples.assemble.AssignedArray;
import org.utbot.examples.assemble.ComplexArray;
import org.utbot.examples.manual.examples.*;
import org.utbot.examples.manual.examples.customer.B;
import org.utbot.examples.manual.examples.customer.C;
Original file line number Diff line number Diff line change
@@ -16,27 +16,27 @@ import org.utbot.examples.assemble.ListItem
import org.utbot.examples.assemble.NoModifier
import org.utbot.examples.assemble.PackagePrivateFields
import org.utbot.examples.assemble.PrimitiveFields
import org.utbot.examples.assemble.arrays.ArrayOfComplexArrays
import org.utbot.examples.assemble.arrays.ArrayOfPrimitiveArrays
import org.utbot.examples.assemble.arrays.AssignedArray
import org.utbot.examples.assemble.arrays.ComplexArray
import org.utbot.examples.assemble.arrays.MethodUnderTest
import org.utbot.examples.assemble.arrays.PrimitiveArray
import org.utbot.examples.assemble.constructors.ComplexConstructor
import org.utbot.examples.assemble.constructors.ComplexConstructorWithSetter
import org.utbot.examples.assemble.constructors.ConstructorModifyingStatic
import org.utbot.examples.assemble.constructors.InheritComplexConstructor
import org.utbot.examples.assemble.constructors.InheritPrimitiveConstructor
import org.utbot.examples.assemble.constructors.PrimitiveConstructor
import org.utbot.examples.assemble.constructors.PrimitiveConstructorWithDefaultField
import org.utbot.examples.assemble.constructors.PrivateConstructor
import org.utbot.examples.assemble.constructors.PseudoComplexConstructor
import org.utbot.examples.assemble.defaults.DefaultField
import org.utbot.examples.assemble.defaults.DefaultFieldModifiedInConstructor
import org.utbot.examples.assemble.defaults.DefaultFieldWithDirectAccessor
import org.utbot.examples.assemble.defaults.DefaultFieldWithSetter
import org.utbot.examples.assemble.defaults.DefaultPackagePrivateField
import org.utbot.examples.assemble.statics.StaticField
import org.utbot.examples.assemble.ArrayOfComplexArrays
import org.utbot.examples.assemble.ArrayOfPrimitiveArrays
import org.utbot.examples.assemble.AssignedArray
import org.utbot.examples.assemble.ComplexArray
import org.utbot.examples.assemble.another.MethodUnderTest
import org.utbot.examples.assemble.PrimitiveArray
import org.utbot.examples.assemble.ComplexConstructor
import org.utbot.examples.assemble.ComplexConstructorWithSetter
import org.utbot.examples.assemble.ConstructorModifyingStatic
import org.utbot.examples.assemble.InheritComplexConstructor
import org.utbot.examples.assemble.InheritPrimitiveConstructor
import org.utbot.examples.assemble.PrimitiveConstructor
import org.utbot.examples.assemble.PrimitiveConstructorWithDefaultField
import org.utbot.examples.assemble.PrivateConstructor
import org.utbot.examples.assemble.PseudoComplexConstructor
import org.utbot.examples.assemble.DefaultField
import org.utbot.examples.assemble.DefaultFieldModifiedInConstructor
import org.utbot.examples.assemble.DefaultFieldWithDirectAccessor
import org.utbot.examples.assemble.DefaultFieldWithSetter
import org.utbot.examples.assemble.DefaultPackagePrivateField
import org.utbot.examples.assemble.StaticField
import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.FieldId
@@ -58,7 +58,6 @@ import org.utbot.framework.util.SootUtils
import org.utbot.framework.util.instanceCounter
import org.utbot.framework.util.modelIdCounter
import kotlin.reflect.full.functions
import org.utbot.examples.assemble.*
import org.utbot.framework.codegen.model.constructor.util.arrayTypeOf

/**
@@ -150,8 +149,7 @@ class AssembleModelGeneratorTests {
fields(testClassId, "a" to 5, "b" to 3)
)

val methodFromAnotherPackage =
MethodUnderTest::class.functions.first()
val methodFromAnotherPackage = MethodUnderTest::class.functions.first()

createModelAndAssert(compositeModel, null, methodFromAnotherPackage.executableId)
}
@@ -413,7 +411,7 @@ class AssembleModelGeneratorTests {
val baseClassId = PrimitiveFields::class.id

val thisFields = fields(inheritedFieldClassId, "i" to 5, "d" to 3.0)
val baseFields = fields(baseClassId, "a" to 2, "b" to 4)
val baseFields = fields(baseClassId, "b" to 4)

val compositeModel = UtCompositeModel(
modelIdCounter.incrementAndGet(),
@@ -425,7 +423,6 @@ class AssembleModelGeneratorTests {
val v1 = statementsChain.addExpectedVariableDecl<InheritedField>()
statementsChain.add("$v1." + ("i" `=` 5))
statementsChain.add("$v1." + ("d" `=` 3.0))
statementsChain.add("$v1." + addExpectedSetter("a", 2))
statementsChain.add("$v1." + ("b" `=` 4))

val expectedRepresentation = printExpectedModel(inheritedFieldClassId.simpleName, v1, statementsChain)
@@ -1448,9 +1445,9 @@ class AssembleModelGeneratorTests {
private fun createModelsAndAssert(
models: List<UtModel>,
expectedModelRepresentations: List<String?>,
assembleTestUtils: ExecutableId = AssembleTestUtils::class.id.allMethods.first(),
assembleTestDummyMethod: ExecutableId = AssembleTestUtils::class.id.allMethods.first(),
) {
val modelsMap = AssembleModelGenerator(assembleTestUtils.classId.packageName).createAssembleModels(models)
val modelsMap = AssembleModelGenerator(assembleTestDummyMethod.classId.packageName).createAssembleModels(models)
//we sort values to fix order of models somehow (IdentityHashMap does not guarantee the order)
val assembleModels = modelsMap.values
.filterIsInstance<UtAssembleModel>()
Original file line number Diff line number Diff line change
@@ -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, PrimitiveModifications::class.java.packageName)
fieldsModificatorsSearcher.findModificators(analysisMode)
.map { (key, value) ->
val modificatorNames = value.filterNot { it.name.startsWith("direct_set_") }.map { it.name }
key.name to modificatorNames.toSortedSet()
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import org.utbot.framework.plugin.api.UtArrayModel
import org.utbot.framework.plugin.api.UtClassRefModel
import org.utbot.framework.plugin.api.UtCompositeModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.util.executableId
import org.utbot.framework.plugin.api.util.id
import java.lang.reflect.Field
import java.lang.reflect.Method
Original file line number Diff line number Diff line change
@@ -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 [methodUnderTest].
* 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 methodPackageName: 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> =
@@ -171,7 +172,7 @@ class AssembleModelGenerator(private val methodPackageName: 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
}
@@ -256,7 +257,7 @@ class AssembleModelGenerator(private val methodPackageName: String) {
if (fieldId.isFinal) {
throw AssembleException("Final field $fieldId can't be set in an object of the class $classId")
}
if (!fieldId.type.isAccessibleFrom(methodPackageName)) {
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"
)
@@ -265,7 +266,8 @@ class AssembleModelGenerator(private val methodPackageName: String) {
if (fieldId in constructorInfo.affectedFields ||
(fieldId !in constructorInfo.setFields && !fieldModel.hasDefaultValue())
) {
val modifierCall = modifierCall(this, fieldId, assembleModel(fieldModel))
val assembledModel = assembleModel(fieldModel)
val modifierCall = modifierCall(this, fieldId, assembledModel)
callChain.add(modifierCall)
}
}
@@ -397,10 +399,10 @@ class AssembleModelGenerator(private val methodPackageName: String) {
}

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

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

/**
* Creates setter or direct setter call to set a field.
@@ -414,7 +416,7 @@ class AssembleModelGenerator(private val methodPackageName: String) {
): UtStatementModel {
val declaringClassId = fieldId.declaringClass

val modifiers = getOrFindSettersAndDirectAccessors(declaringClassId)
val modifiers = getOrFindSettersAndDirectAccessors(instance.classId)
val modifier = modifiers[fieldId]
?: throw AssembleException("No setter for field ${fieldId.name} of class ${declaringClassId.name}")

@@ -439,9 +441,7 @@ class AssembleModelGenerator(private val methodPackageName: String) {
* Finds setters and direct accessors for fields of particular class.
*/
private fun findSettersAndDirectAccessors(classId: ClassId): Map<FieldId, StatementId> {
val allModificatorsOfClass = modificatorsSearcher
.findModificators(SettersAndDirectAccessors, methodPackageName)
.map { it.key to it.value.filter { st -> st.classId == classId } }
val allModificatorsOfClass = modificatorsSearcher.findModificators(SettersAndDirectAccessors)

return allModificatorsOfClass
.mapNotNull { (fieldId, possibleModificators) ->
@@ -457,9 +457,12 @@ class AssembleModelGenerator(private val methodPackageName: String) {
*/
private fun chooseModificator(
fieldId: FieldId,
settersAndDirectAccessors: List<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(
@@ -468,7 +471,9 @@ class AssembleModelGenerator(private val methodPackageName: 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(" ")}"
)
Original file line number Diff line number Diff line change
@@ -129,6 +129,10 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA
)
}
// we can access the field only via reflection

// NOTE that current implementation works only if field access is located
// in the right part of the assignment. However, obtaining this construction
// as an "l-value" seems to be an error in assemble models or somewhere else.
is ReflectionOnly -> fieldId.accessWithReflection(this)
}
}
Original file line number Diff line number Diff line change
@@ -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
@@ -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 =
@@ -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
}

/**
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import org.utbot.framework.UtSettings
import org.utbot.framework.assemble.AssembleModelGenerator
import org.utbot.framework.plugin.api.Coverage
import org.utbot.framework.plugin.api.EnvironmentModels
import org.utbot.framework.plugin.api.ExecutableId
import org.utbot.framework.plugin.api.FieldId
import org.utbot.framework.plugin.api.Instruction
import org.utbot.framework.plugin.api.MissingState
@@ -98,9 +99,7 @@ class UtConcreteExecutionResult(
*
* @return [UtConcreteExecutionResult] with converted models.
*/
fun convertToAssemble(
packageName: String
): UtConcreteExecutionResult {
fun convertToAssemble(packageName: String): UtConcreteExecutionResult {
val allModels = collectAllModels()

val modelsToAssembleModels = AssembleModelGenerator(packageName).createAssembleModels(allModels)
Original file line number Diff line number Diff line change
@@ -17,19 +17,24 @@ 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> {
val clazz = classId.jClass
private fun collectFields(classId: ClassId): Set<FieldId> {
var clazz = classId.jClass

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


return fieldIds.map { it.fieldId }.toSet()
}
Original file line number Diff line number Diff line change
@@ -16,24 +16,8 @@ class UtBotFieldsModificatorsSearcher {
* Finds field modificators.
*
* @param analysisMode represents which type of modificators (e.g. setters) are considered.
* @param packageName describes a location of package-private methods that need to be considered.
*/
fun findModificators(analysisMode: AnalysisMode, packageName: String? = null): Map<FieldId, Set<StatementId>> {
val modificators = findModificators(analysisMode)
if (packageName == null) {
return modificators
}

val filteredModifications = mutableMapOf<FieldId, Set<StatementId>>()
for ((fieldId, statements) in modificators) {
val filteredStmts = statements.filter { it.classId.packageName.startsWith(packageName) }.toSet()
filteredModifications[fieldId] = filteredStmts
}

return filteredModifications
}

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