Skip to content

Commit b12f7c3

Browse files
committed
Improve modificators analysis in UtBotFieldModificatorsSearcher and AssembleModelGenerator
1 parent 7131a3a commit b12f7c3

File tree

12 files changed

+59
-65
lines changed

12 files changed

+59
-65
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.utbot.examples.assemble;
2+
3+
/**
4+
* Need to be located at the same package as [AssembleTestUtils]
5+
* because requires a setter for package-private field.
6+
*/
7+
public class DefaultPackagePrivateField {
8+
int z = 10;
9+
}

utbot-framework-test/src/main/java/org/utbot/examples/assemble/defaults/DefaultFieldModifiedInConstructor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.utbot.examples.assemble.defaults;
22

33
public class DefaultFieldModifiedInConstructor {
4-
int z;
4+
public int z;
55

66
@SuppressWarnings("Unused")
77
DefaultFieldModifiedInConstructor(int z_) {

utbot-framework-test/src/main/java/org/utbot/examples/assemble/defaults/DefaultPackagePrivateField.java

Lines changed: 0 additions & 5 deletions
This file was deleted.

utbot-framework-test/src/test/kotlin/org/utbot/framework/assemble/AssembleModelGeneratorTests.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ import org.utbot.examples.assemble.defaults.DefaultField
3535
import org.utbot.examples.assemble.defaults.DefaultFieldModifiedInConstructor
3636
import org.utbot.examples.assemble.defaults.DefaultFieldWithDirectAccessor
3737
import org.utbot.examples.assemble.defaults.DefaultFieldWithSetter
38-
import org.utbot.examples.assemble.defaults.DefaultPackagePrivateField
38+
import org.utbot.examples.assemble.DefaultPackagePrivateField
3939
import org.utbot.examples.assemble.statics.StaticField
4040
import org.utbot.framework.plugin.api.ClassId
4141
import org.utbot.framework.plugin.api.ExecutableId
@@ -58,7 +58,6 @@ import org.utbot.framework.util.SootUtils
5858
import org.utbot.framework.util.instanceCounter
5959
import org.utbot.framework.util.modelIdCounter
6060
import kotlin.reflect.full.functions
61-
import org.utbot.examples.assemble.*
6261
import org.utbot.framework.codegen.model.constructor.util.arrayTypeOf
6362

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

153-
val methodFromAnotherPackage =
154-
MethodUnderTest::class.functions.first()
152+
val methodFromAnotherPackage = MethodUnderTest::class.functions.first()
155153

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

415413
val thisFields = fields(inheritedFieldClassId, "i" to 5, "d" to 3.0)
416-
val baseFields = fields(baseClassId, "a" to 2, "b" to 4)
414+
val baseFields = fields(baseClassId, "b" to 4)
417415

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

431428
val expectedRepresentation = printExpectedModel(inheritedFieldClassId.simpleName, v1, statementsChain)
@@ -1448,9 +1445,9 @@ class AssembleModelGeneratorTests {
14481445
private fun createModelsAndAssert(
14491446
models: List<UtModel>,
14501447
expectedModelRepresentations: List<String?>,
1451-
assembleTestUtils: ExecutableId = AssembleTestUtils::class.id.allMethods.first(),
1448+
assembleTestDummyMethod: ExecutableId = AssembleTestUtils::class.id.allMethods.first(),
14521449
) {
1453-
val modelsMap = AssembleModelGenerator(assembleTestUtils.classId.packageName).createAssembleModels(models)
1450+
val modelsMap = AssembleModelGenerator(assembleTestDummyMethod.classId.packageName).createAssembleModels(models)
14541451
//we sort values to fix order of models somehow (IdentityHashMap does not guarantee the order)
14551452
val assembleModels = modelsMap.values
14561453
.filterIsInstance<UtAssembleModel>()

utbot-framework-test/src/test/kotlin/org/utbot/framework/modificators/UtBotFieldModificatorsTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ internal class UtBotFieldModificatorsTest {
192192

193193
//We use sorting here to make comparing with sorted in advance expected collections easier
194194
private fun runFieldModificatorsSearch(analysisMode: AnalysisMode) =
195-
fieldsModificatorsSearcher.findModificators(analysisMode, PrimitiveModifications::class.java.packageName)
195+
fieldsModificatorsSearcher.findModificators(analysisMode)
196196
.map { (key, value) ->
197197
val modificatorNames = value.filterNot { it.name.startsWith("direct_set_") }.map { it.name }
198198
key.name to modificatorNames.toSortedSet()

utbot-framework/src/main/kotlin/org/utbot/external/api/UtModelFactory.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import org.utbot.framework.plugin.api.UtArrayModel
88
import org.utbot.framework.plugin.api.UtClassRefModel
99
import org.utbot.framework.plugin.api.UtCompositeModel
1010
import org.utbot.framework.plugin.api.UtModel
11+
import org.utbot.framework.plugin.api.util.executableId
1112
import org.utbot.framework.plugin.api.util.id
1213
import java.lang.reflect.Field
1314
import java.lang.reflect.Method

utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,21 @@ import org.utbot.framework.plugin.api.hasDefaultValue
3636
import org.utbot.framework.plugin.api.isMockModel
3737
import org.utbot.framework.plugin.api.util.defaultValueModel
3838
import org.utbot.framework.plugin.api.util.executableId
39+
import org.utbot.framework.plugin.api.util.isSubtypeOf
3940
import org.utbot.framework.plugin.api.util.jClass
4041
import org.utbot.framework.util.nextModelName
4142
import java.lang.reflect.Constructor
4243
import java.util.IdentityHashMap
4344

4445
/**
4546
* Creates [UtAssembleModel] from any [UtModel] or it's inner models if possible
46-
* during generation test for [methodUnderTest].
47+
* during generation test for [basePackageName].
4748
*
4849
* Needs utContext be set and Soot be initialized.
4950
*
5051
* Note: Caches class related information, can be reused if classes don't change.
5152
*/
52-
class AssembleModelGenerator(private val methodPackageName: String) {
53+
class AssembleModelGenerator(private val basePackageName: String) {
5354

5455
//Instantiated models are stored to avoid cyclic references during reference graph analysis
5556
private val instantiatedModels: IdentityHashMap<UtModel, UtReferenceModel> =
@@ -171,7 +172,7 @@ class AssembleModelGenerator(private val methodPackageName: String) {
171172
private fun assembleModel(utModel: UtModel): UtModel {
172173
val collectedCallChain = callChain.toMutableList()
173174

174-
// we cannot create an assemble model for an anonymous class instance
175+
// We cannot create an assemble model for an anonymous class instance
175176
if (utModel.classId.isAnonymous) {
176177
return utModel
177178
}
@@ -256,7 +257,7 @@ class AssembleModelGenerator(private val methodPackageName: String) {
256257
if (fieldId.isFinal) {
257258
throw AssembleException("Final field $fieldId can't be set in an object of the class $classId")
258259
}
259-
if (!fieldId.type.isAccessibleFrom(methodPackageName)) {
260+
if (!fieldId.type.isAccessibleFrom(basePackageName)) {
260261
throw AssembleException(
261262
"Field $fieldId can't be set in an object of the class $classId because its type is inaccessible"
262263
)
@@ -265,7 +266,8 @@ class AssembleModelGenerator(private val methodPackageName: String) {
265266
if (fieldId in constructorInfo.affectedFields ||
266267
(fieldId !in constructorInfo.setFields && !fieldModel.hasDefaultValue())
267268
) {
268-
val modifierCall = modifierCall(this, fieldId, assembleModel(fieldModel))
269+
val assembledModel = assembleModel(fieldModel)
270+
val modifierCall = modifierCall(this, fieldId, assembledModel)
269271
callChain.add(modifierCall)
270272
}
271273
}
@@ -397,10 +399,10 @@ class AssembleModelGenerator(private val methodPackageName: String) {
397399
}
398400

399401
private val ClassId.isVisible : Boolean
400-
get() = this.isPublic || !this.isPrivate && this.packageName.startsWith(methodPackageName)
402+
get() = this.isPublic || !this.isPrivate && this.packageName.startsWith(basePackageName)
401403

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

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

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

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

446446
return allModificatorsOfClass
447447
.mapNotNull { (fieldId, possibleModificators) ->
@@ -457,9 +457,12 @@ class AssembleModelGenerator(private val methodPackageName: String) {
457457
*/
458458
private fun chooseModificator(
459459
fieldId: FieldId,
460-
settersAndDirectAccessors: List<StatementId>
460+
settersAndDirectAccessors: Set<StatementId>,
461461
): StatementId? {
462-
val directAccessors = settersAndDirectAccessors.filterIsInstance<DirectFieldAccessId>()
462+
val directAccessors = settersAndDirectAccessors
463+
.filterIsInstance<DirectFieldAccessId>()
464+
.filter {it.fieldId.isAccessibleFrom(basePackageName) }
465+
463466
if (directAccessors.any()) {
464467
return directAccessors.singleOrNull()
465468
?: throw AssembleException(
@@ -468,7 +471,9 @@ class AssembleModelGenerator(private val methodPackageName: String) {
468471
}
469472

470473
if (settersAndDirectAccessors.any()) {
471-
return settersAndDirectAccessors.singleOrNull()
474+
return settersAndDirectAccessors
475+
.filterIsInstance<ExecutableId>()
476+
.singleOrNull { it.isAccessibleFrom(basePackageName) }
472477
?: throw AssembleException(
473478
"Field $fieldId has more than one setter: ${settersAndDirectAccessors.joinToString(" ")}"
474479
)

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgCallableAccessManager.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA
129129
)
130130
}
131131
// we can access the field only via reflection
132+
133+
// NOTE that current implementation works only if field access is located
134+
// in the right part of the assignment. However, obtaining this construction
135+
// as an "l-value" seems to be an error in assemble models or somewhere else.
132136
is ReflectionOnly -> fieldId.accessWithReflection(this)
133137
}
134138
}

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/FieldIdUtil.kt

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ import org.utbot.framework.plugin.api.util.voidClassId
1313
*
1414
* @param context context in which code is generated (it is needed because the method needs to know package and language)
1515
*/
16-
// TODO: change parameter from packageName: String to context: CgContext in ClassId.isAccessibleFrom and ExecutableId.isAccessibleFrom ?
17-
private fun FieldId.isAccessibleFrom(context: CgContext): Boolean {
18-
val packageName = context.testClassPackageName
16+
fun FieldId.isAccessibleFrom(packageName: String): Boolean {
1917
val isClassAccessible = declaringClass.isAccessibleFrom(packageName)
2018
val isAccessibleByVisibility = isPublic || (declaringClass.packageName == packageName && (isPackagePrivate || isProtected))
2119
val isAccessibleFromPackageByModifiers = isAccessibleByVisibility && !isSynthetic
@@ -36,7 +34,7 @@ internal infix fun FieldId.canBeReadFrom(context: CgContext): Boolean {
3634
return true
3735
}
3836

39-
return isAccessibleFrom(context)
37+
return isAccessibleFrom(context.testClassPackageName)
4038
}
4139

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

57-
return isAccessibleFrom(context) && !isFinal
55+
return isAccessibleFrom(context.testClassPackageName) && !isFinal
5856
}
5957

6058
/**

utbot-framework/src/main/kotlin/org/utbot/framework/concrete/UtExecutionInstrumentation.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import org.utbot.framework.UtSettings
88
import org.utbot.framework.assemble.AssembleModelGenerator
99
import org.utbot.framework.plugin.api.Coverage
1010
import org.utbot.framework.plugin.api.EnvironmentModels
11+
import org.utbot.framework.plugin.api.ExecutableId
1112
import org.utbot.framework.plugin.api.FieldId
1213
import org.utbot.framework.plugin.api.Instruction
1314
import org.utbot.framework.plugin.api.MissingState
@@ -98,9 +99,7 @@ class UtConcreteExecutionResult(
9899
*
99100
* @return [UtConcreteExecutionResult] with converted models.
100101
*/
101-
fun convertToAssemble(
102-
packageName: String
103-
): UtConcreteExecutionResult {
102+
fun convertToAssemble(packageName: String): UtConcreteExecutionResult {
104103
val allModels = collectAllModels()
105104

106105
val modelsToAssembleModels = AssembleModelGenerator(packageName).createAssembleModels(allModels)

utbot-framework/src/main/kotlin/org/utbot/framework/modifications/DirectAccessorsAnalyzer.kt

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,24 @@ class DirectAccessorsAnalyzer {
1717
*/
1818
fun collectDirectAccesses(classIds: Set<ClassId>): Set<DirectFieldAccessId> =
1919
classIds
20-
.flatMap { classId -> collectFieldsInPackage(classId) }
20+
.flatMap { classId -> collectFields(classId) }
2121
.map { fieldId -> DirectFieldAccessId(fieldId.declaringClass, directSetterName(fieldId), fieldId) }
2222
.toSet()
2323

2424
/**
25-
* Collect all fields with different non-private modifiers from class [classId].
25+
* Collect all fields with different non-private modifiers
26+
* from class [classId] or it's base classes.
2627
*/
27-
private fun collectFieldsInPackage(classId: ClassId): Set<FieldId> {
28-
val clazz = classId.jClass
28+
private fun collectFields(classId: ClassId): Set<FieldId> {
29+
var clazz = classId.jClass
2930

3031
val fieldIds = mutableSetOf<Field>()
31-
fieldIds += clazz.fields
3232
fieldIds += clazz.declaredFields.filterNot { Modifier.isPrivate(it.modifiers) }
33+
while (clazz.superclass != null) {
34+
clazz = clazz.superclass
35+
fieldIds += clazz.declaredFields.filterNot { Modifier.isPrivate(it.modifiers) }
36+
}
37+
3338

3439
return fieldIds.map { it.fieldId }.toSet()
3540
}

utbot-framework/src/main/kotlin/org/utbot/framework/modifications/UtBotFieldsModificatorsSearcher.kt

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package org.utbot.framework.modifications
22

33
import org.utbot.framework.plugin.api.ClassId
4+
import org.utbot.framework.plugin.api.ExecutableId
45
import org.utbot.framework.plugin.api.FieldId
56
import org.utbot.framework.plugin.api.StatementId
7+
import org.utbot.framework.plugin.api.util.isSubtypeOf
68

79
class UtBotFieldsModificatorsSearcher {
810

@@ -12,28 +14,7 @@ class UtBotFieldsModificatorsSearcher {
1214

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

15-
/**
16-
* Finds field modificators.
17-
*
18-
* @param analysisMode represents which type of modificators (e.g. setters) are considered.
19-
* @param packageName describes a location of package-private methods that need to be considered.
20-
*/
21-
fun findModificators(analysisMode: AnalysisMode, packageName: String? = null): Map<FieldId, Set<StatementId>> {
22-
val modificators = findModificators(analysisMode)
23-
if (packageName == null) {
24-
return modificators
25-
}
26-
27-
val filteredModifications = mutableMapOf<FieldId, Set<StatementId>>()
28-
for ((fieldId, statements) in modificators) {
29-
val filteredStmts = statements.filter { it.classId.packageName.startsWith(packageName) }.toSet()
30-
filteredModifications[fieldId] = filteredStmts
31-
}
32-
33-
return filteredModifications
34-
}
35-
36-
private fun findModificators(analysisMode: AnalysisMode): Map<FieldId, Set<StatementId>> {
17+
fun findModificators(analysisMode: AnalysisMode): Map<FieldId, Set<StatementId>> {
3718
statementsStorage.updateCaches()
3819
return findModificatorsInCache(analysisMode)
3920
}

0 commit comments

Comments
 (0)