Skip to content

Commit affa753

Browse files
authored
Fix engine to properly handle hidden fields #647 (#627)
1 parent 2757d01 commit affa753

File tree

24 files changed

+133
-99
lines changed

24 files changed

+133
-99
lines changed

utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,11 @@
11
package org.utbot.common
22

3-
import java.lang.reflect.Field
43
import java.lang.reflect.InvocationTargetException
54
import java.lang.reflect.Method
65

76

87
val Class<*>.packageName: String get() = `package`?.name?:""
98

10-
fun Class<*>.findField(name: String): Field =
11-
findFieldOrNull(name) ?: error("Can't find field $name in $this")
12-
13-
fun Class<*>.findFieldOrNull(name: String): Field? = generateSequence(this) { it.superclass }
14-
.mapNotNull {
15-
try {
16-
it.getField(name)
17-
} catch (e: NoSuchFieldException) {
18-
try {
19-
it.getDeclaredField(name)
20-
} catch (e: NoSuchFieldException) {
21-
null
22-
}
23-
}
24-
}
25-
.firstOrNull()
26-
279
fun Method.invokeCatching(obj: Any?, args: List<Any?>) = try {
2810
Result.success(invoke(obj, *args.toTypedArray()))
2911
} catch (e: InvocationTargetException) {

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import org.utbot.framework.plugin.api.util.charClassId
2020
import org.utbot.framework.plugin.api.util.constructor
2121
import org.utbot.framework.plugin.api.util.doubleClassId
2222
import org.utbot.framework.plugin.api.util.executableId
23-
import org.utbot.framework.plugin.api.util.findFieldOrNull
2423
import org.utbot.framework.plugin.api.util.floatClassId
2524
import org.utbot.framework.plugin.api.util.id
2625
import org.utbot.framework.plugin.api.util.intClassId
@@ -30,6 +29,7 @@ import org.utbot.framework.plugin.api.util.jClass
3029
import org.utbot.framework.plugin.api.util.longClassId
3130
import org.utbot.framework.plugin.api.util.method
3231
import org.utbot.framework.plugin.api.util.primitiveTypeJvmNameOrNull
32+
import org.utbot.framework.plugin.api.util.safeJField
3333
import org.utbot.framework.plugin.api.util.shortClassId
3434
import org.utbot.framework.plugin.api.util.toReferenceTypeBytecodeSignature
3535
import org.utbot.framework.plugin.api.util.voidClassId
@@ -366,7 +366,7 @@ data class UtCompositeModel(
366366
if (fields.isNotEmpty()) {
367367
append(" ")
368368
append(fields.entries.joinToString(", ", "{", "}") { (field, value) ->
369-
if (value.classId != classId || value.isNull()) "${field.name}: $value" else "${field.name}: not evaluated"
369+
if (value.classId != classId || value.isNull()) "(${field.declaringClass}) ${field.name}: $value" else "${field.name}: not evaluated"
370370
}) // TODO: here we can get an infinite recursion if we have cyclic dependencies.
371371
}
372372
if (mocks.isNotEmpty()) {
@@ -876,7 +876,7 @@ open class FieldId(val declaringClass: ClassId, val name: String) {
876876
return result
877877
}
878878

879-
override fun toString() = declaringClass.findFieldOrNull(name).toString()
879+
override fun toString() = safeJField.toString()
880880
}
881881

882882
inline fun <T> withReflection(block: () -> T): T {

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/impl/FieldIdStrategies.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package org.utbot.framework.plugin.api.impl
33
import org.utbot.framework.plugin.api.ClassId
44
import org.utbot.framework.plugin.api.FieldId
55
import org.utbot.framework.plugin.api.classId
6-
import org.utbot.framework.plugin.api.util.field
6+
import org.utbot.framework.plugin.api.util.jField
77
import org.utbot.framework.plugin.api.util.id
88
import java.lang.reflect.Modifier
99
import soot.Scene
@@ -32,25 +32,25 @@ interface FieldIdStrategy {
3232
class FieldIdReflectionStrategy(val fieldId: FieldId) : FieldIdStrategy {
3333

3434
override val isPublic: Boolean
35-
get() = Modifier.isPublic(fieldId.field.modifiers)
35+
get() = Modifier.isPublic(fieldId.jField.modifiers)
3636

3737
override val isProtected: Boolean
38-
get() = Modifier.isProtected(fieldId.field.modifiers)
38+
get() = Modifier.isProtected(fieldId.jField.modifiers)
3939

4040
override val isPrivate: Boolean
41-
get() = Modifier.isPrivate(fieldId.field.modifiers)
41+
get() = Modifier.isPrivate(fieldId.jField.modifiers)
4242

4343
override val isFinal: Boolean
44-
get() = Modifier.isFinal(fieldId.field.modifiers)
44+
get() = Modifier.isFinal(fieldId.jField.modifiers)
4545

4646
override val isStatic: Boolean
47-
get() = Modifier.isStatic(fieldId.field.modifiers)
47+
get() = Modifier.isStatic(fieldId.jField.modifiers)
4848

4949
override val isSynthetic: Boolean
50-
get() = fieldId.field.isSynthetic
50+
get() = fieldId.jField.isSynthetic
5151

5252
override val type: ClassId
53-
get() = fieldId.field.type.id
53+
get() = fieldId.jField.type.id
5454
}
5555

5656
class FieldIdSootStrategy(val declaringClass: ClassId, val fieldId: FieldId) : FieldIdStrategy {

utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.utbot.framework.plugin.api.util
22

3-
import org.utbot.common.findFieldOrNull
43
import org.utbot.framework.plugin.api.BuiltinClassId
54
import org.utbot.framework.plugin.api.BuiltinMethodId
65
import org.utbot.framework.plugin.api.ClassId
@@ -286,9 +285,17 @@ val ClassId.isMap: Boolean
286285
val ClassId.isIterableOrMap: Boolean
287286
get() = isIterable || isMap
288287

289-
fun ClassId.findFieldOrNull(fieldName: String): Field? = jClass.findFieldOrNull(fieldName)
288+
fun ClassId.findFieldByIdOrNull(fieldId: FieldId): Field? {
289+
if (isNotSubtypeOf(fieldId.declaringClass)) {
290+
return null
291+
}
292+
293+
return fieldId.safeJField
294+
}
290295

291-
fun ClassId.hasField(fieldName: String): Boolean = findFieldOrNull(fieldName) != null
296+
fun ClassId.hasField(fieldId: FieldId): Boolean {
297+
return findFieldByIdOrNull(fieldId) != null
298+
}
292299

293300
fun ClassId.defaultValueModel(): UtModel = when (this) {
294301
intClassId -> UtPrimitiveModel(0)
@@ -303,11 +310,12 @@ fun ClassId.defaultValueModel(): UtModel = when (this) {
303310
}
304311

305312
// FieldId utils
313+
val FieldId.safeJField: Field?
314+
get() = declaringClass.jClass.declaredFields.firstOrNull { it.name == name }
306315

307316
// TODO: maybe cache it somehow in the future
308-
val FieldId.field: Field
309-
get() = declaringClass.jClass.declaredFields.firstOrNull { it.name == name }
310-
?: error("Field $name is not found in class ${declaringClass.jClass.name}")
317+
val FieldId.jField: Field
318+
get() = safeJField ?: error("Field $name is not declared in class ${declaringClass.jClass.name}")
311319

312320
// https://docstore.mik.ua/orelly/java-ent/jnut/ch03_13.htm
313321
val FieldId.isInnerClassEnclosingClassReference: Boolean

utbot-framework/src/main/kotlin/org/utbot/engine/Hierarchy.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ class Hierarchy(private val typeRegistry: TypeRegistry) {
2828
type as? RefType ?: error("$type is not a refType")
2929

3030
val realType = typeRegistry.findRealType(type) as RefType
31+
val realFieldDeclaringType = typeRegistry.findRealType(field.declaringClass.type) as RefType
3132

32-
val ancestorType = ancestors(realType.sootClass.id).lastOrNull { it.declaresField(field.subSignature) }?.type
33-
?: error("No such field ${field.subSignature} found in ${realType.sootClass.name}")
34-
return ChunkId("$ancestorType", field.name)
33+
if (realFieldDeclaringType.sootClass !in ancestors(realType.sootClass.id)) {
34+
error("No such field ${field.subSignature} found in ${realType.sootClass.name}")
35+
}
36+
return ChunkId("$realFieldDeclaringType", field.name)
3537
}
3638

3739
/**

utbot-framework/src/main/kotlin/org/utbot/engine/Strings.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ sealed class UtAbstractStringBuilderWrapper(className: String) : BaseOverriddenW
313313

314314
val arrayValuesChunkId = typeRegistry.arrayChunkId(charArrayType)
315315

316-
val valuesFieldChunkId = hierarchy.chunkIdForField(utStringClass.type, overriddenClass.valueField)
316+
val valuesFieldChunkId = hierarchy.chunkIdForField(overriddenClass.type, overriddenClass.valueField)
317317
val valuesArrayAddrDescriptor = MemoryChunkDescriptor(valuesFieldChunkId, wrapper.type, charType)
318318
val valuesArrayAddr = findArray(valuesArrayAddrDescriptor, MemoryState.CURRENT).select(wrapper.addr)
319319

utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import kotlinx.collections.immutable.toPersistentMap
88
import kotlinx.collections.immutable.toPersistentSet
99
import org.utbot.common.WorkaroundReason.HACK
1010
import org.utbot.common.WorkaroundReason.REMOVE_ANONYMOUS_CLASSES
11-
import org.utbot.common.findField
1211
import org.utbot.common.unreachableBranch
1312
import org.utbot.common.withAccessibility
1413
import org.utbot.common.workaround
@@ -90,8 +89,9 @@ import org.utbot.framework.plugin.api.MethodId
9089
import org.utbot.framework.plugin.api.UtMethod
9190
import org.utbot.framework.plugin.api.classId
9291
import org.utbot.framework.plugin.api.id
93-
import org.utbot.framework.plugin.api.util.id
92+
import org.utbot.framework.plugin.api.util.jField
9493
import org.utbot.framework.plugin.api.util.jClass
94+
import org.utbot.framework.plugin.api.util.id
9595
import org.utbot.framework.plugin.api.util.signature
9696
import org.utbot.framework.plugin.api.util.utContext
9797
import org.utbot.framework.util.executableId
@@ -582,7 +582,7 @@ class Traverser(
582582
declaringClass: SootClass,
583583
stmt: Stmt
584584
): SymbolicStateUpdate {
585-
val concreteValue = extractConcreteValue(field, declaringClass)
585+
val concreteValue = extractConcreteValue(field)
586586
val (symbolicResult, symbolicStateUpdate) = toMethodResult(concreteValue, field.type)
587587
val symbolicValue = (symbolicResult as SymbolicSuccess).value
588588

@@ -634,12 +634,20 @@ class Traverser(
634634
// Some fields are inaccessible with reflection, so we have to instantiate it by ourselves.
635635
// Otherwise, extract it from the class.
636636
// TODO JIRA:1593
637-
private fun extractConcreteValue(field: SootField, declaringClass: SootClass): Any? =
637+
private fun extractConcreteValue(field: SootField): Any? =
638638
when (field.signature) {
639639
SECURITY_FIELD_SIGNATURE -> SecurityManager()
640640
FIELD_FILTER_MAP_FIELD_SIGNATURE -> mapOf(Reflection::class to arrayOf("fieldFilterMap", "methodFilterMap"))
641641
METHOD_FILTER_MAP_FIELD_SIGNATURE -> emptyMap<Class<*>, Array<String>>()
642-
else -> declaringClass.id.jClass.findField(field.name).let { it.withAccessibility { it.get(null) } }
642+
else -> {
643+
val fieldId = field.fieldId
644+
val jField = fieldId.jField
645+
jField.let {
646+
it.withAccessibility {
647+
it.get(null)
648+
}
649+
}
650+
}
643651
}
644652

645653
private fun isStaticInstanceInMethodResult(id: ClassId, methodResult: MethodResult?) =

utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,6 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy
7070
hierarchy
7171
.ancestors(type.sootClass.id)
7272
.flatMap { it.fields }
73-
.asReversed() // to take fields of the farthest parent in the distinctBy
74-
.distinctBy { it.name } // TODO we lose hidden fields here JIRA:315
7573
}
7674

7775
/**

utbot-framework/src/main/kotlin/org/utbot/engine/ValueConstructor.kt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.utbot.engine
22

3-
import org.utbot.common.findField
4-
import org.utbot.common.findFieldOrNull
53
import org.utbot.common.invokeCatching
64
import org.utbot.common.withAccessibility
75
import org.utbot.framework.plugin.api.ClassId
@@ -37,6 +35,7 @@ import org.utbot.framework.plugin.api.UtValueExecutionState
3735
import org.utbot.framework.plugin.api.UtVoidModel
3836
import org.utbot.framework.plugin.api.isMockModel
3937
import org.utbot.framework.plugin.api.util.constructor
38+
import org.utbot.framework.plugin.api.util.jField
4039
import org.utbot.framework.plugin.api.util.jClass
4140
import org.utbot.framework.plugin.api.util.method
4241
import org.utbot.framework.plugin.api.util.utContext
@@ -212,9 +211,8 @@ class ValueConstructor {
212211
val classInstance = javaClass.anyInstance
213212
constructedObjects[model] = classInstance
214213

215-
model.fields.forEach { (field, fieldModel) ->
216-
val declaredField =
217-
javaClass.findFieldOrNull(field.name) ?: error("Can't find field: $field for $javaClass")
214+
model.fields.forEach { (fieldId, fieldModel) ->
215+
val declaredField = fieldId.jField
218216
val accessible = declaredField.isAccessible
219217

220218
try {
@@ -228,7 +226,7 @@ class ValueConstructor {
228226
fieldModel.classId.name,
229227
model.classId.name,
230228
UtConcreteValue(classInstance),
231-
field.name
229+
fieldId.name
232230
)
233231
}
234232
val value = construct(fieldModel, target).value
@@ -360,7 +358,7 @@ class ValueConstructor {
360358
val instanceClassId = instanceModel.classId
361359
val fieldModel = directSetterModel.fieldModel
362360

363-
val field = instance::class.java.findField(directSetterModel.fieldId.name)
361+
val field = directSetterModel.fieldId.jField
364362
val isAccessible = field.isAccessible
365363

366364
try {

utbot-framework/src/main/kotlin/org/utbot/engine/util/statics/concrete/EnumConcreteUtils.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import org.utbot.engine.pc.select
1010
import org.utbot.engine.symbolic.SymbolicStateUpdate
1111
import org.utbot.engine.symbolic.asHardConstraint
1212
import org.utbot.framework.plugin.api.FieldId
13-
import org.utbot.framework.plugin.api.util.field
13+
import org.utbot.framework.plugin.api.util.jField
1414
import soot.SootClass
1515
import soot.SootField
1616
import soot.SootMethod
@@ -43,7 +43,7 @@ fun associateEnumSootFieldsWithConcreteValues(
4343
enumConstants: List<Enum<*>>
4444
): List<Pair<SootField, List<Any>>> =
4545
enumFields.map { enumSootField ->
46-
val enumField = enumSootField.fieldId.field
46+
val enumField = enumSootField.fieldId.jField
4747

4848
val fieldValues = if (enumSootField.isStatic) {
4949
val staticFieldValue = enumField.withAccessibility { enumField.get(null) }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ internal class CgFieldStateManagerImpl(val context: CgContext)
177177
}
178178

179179
// if previous field has type that does not have current field, this field is inaccessible
180-
if (index > 0 && !path[index - 1].type.hasField(fieldPathElement.field.name)) {
180+
if (index > 0 && !path[index - 1].type.hasField(fieldPathElement.field)) {
181181
lastAccessibleIndex = index - 1
182182
break
183183
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ import org.utbot.framework.plugin.api.util.doubleArrayClassId
121121
import org.utbot.framework.plugin.api.util.doubleClassId
122122
import org.utbot.framework.plugin.api.util.doubleWrapperClassId
123123
import org.utbot.framework.plugin.api.util.executable
124-
import org.utbot.framework.plugin.api.util.field
124+
import org.utbot.framework.plugin.api.util.jField
125125
import org.utbot.framework.plugin.api.util.floatArrayClassId
126126
import org.utbot.framework.plugin.api.util.floatClassId
127127
import org.utbot.framework.plugin.api.util.floatWrapperClassId
@@ -873,8 +873,8 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
873873
private fun FieldId.getAccessExpression(variable: CgVariable): CgExpression =
874874
// Can directly access field only if it is declared in variable class (or in its ancestors)
875875
// and is accessible from current package
876-
if (variable.type.hasField(name) && isAccessibleFrom(testClassPackageName)) {
877-
if (field.isStatic) CgStaticFieldAccess(this) else CgFieldAccess(variable, this)
876+
if (variable.type.hasField(this) && isAccessibleFrom(testClassPackageName)) {
877+
if (jField.isStatic) CgStaticFieldAccess(this) else CgFieldAccess(variable, this)
878878
} else {
879879
testClassThisInstance[getFieldValue](variable, stringLiteral(name))
880880
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ import org.utbot.framework.plugin.api.UtPrimitiveModel
4747
import org.utbot.framework.plugin.api.UtReferenceModel
4848
import org.utbot.framework.plugin.api.UtVoidModel
4949
import org.utbot.framework.plugin.api.util.defaultValueModel
50-
import org.utbot.framework.plugin.api.util.field
51-
import org.utbot.framework.plugin.api.util.findFieldOrNull
50+
import org.utbot.framework.plugin.api.util.jField
51+
import org.utbot.framework.plugin.api.util.findFieldByIdOrNull
5252
import org.utbot.framework.plugin.api.util.id
5353
import org.utbot.framework.plugin.api.util.intClassId
5454
import org.utbot.framework.plugin.api.util.isArray
@@ -128,9 +128,9 @@ internal class CgVariableConstructor(val context: CgContext) :
128128
}
129129

130130
for ((fieldId, fieldModel) in model.fields) {
131-
val field = fieldId.field
131+
val field = fieldId.jField
132132
val variableForField = getOrCreateVariable(fieldModel)
133-
val fieldFromVariableSpecifiedType = obj.type.findFieldOrNull(field.name)
133+
val fieldFromVariableSpecifiedType = obj.type.findFieldByIdOrNull(fieldId)
134134

135135
// we cannot set field directly if variable declared type does not have such field
136136
// or we cannot directly create variable for field with the specified type (it is private, for example)

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

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package org.utbot.framework.concrete
22

3-
import org.utbot.common.findField
4-
import org.utbot.common.findFieldOrNull
53
import org.utbot.common.invokeCatching
64
import org.utbot.framework.plugin.api.ClassId
75
import org.utbot.framework.plugin.api.ConstructorId
@@ -33,6 +31,7 @@ import org.utbot.framework.plugin.api.UtVoidModel
3331
import org.utbot.framework.plugin.api.isMockModel
3432
import org.utbot.framework.plugin.api.util.constructor
3533
import org.utbot.framework.plugin.api.util.executableId
34+
import org.utbot.framework.plugin.api.util.jField
3635
import org.utbot.framework.plugin.api.util.jClass
3736
import org.utbot.framework.plugin.api.util.method
3837
import org.utbot.framework.plugin.api.util.utContext
@@ -169,17 +168,16 @@ class MockValueConstructor(
169168
mockInstance
170169
}
171170

172-
model.fields.forEach { (field, fieldModel) ->
173-
val declaredField =
174-
javaClass.findFieldOrNull(field.name) ?: error("Can't find field: $field for $javaClass")
171+
model.fields.forEach { (fieldId, fieldModel) ->
172+
val declaredField = fieldId.jField
175173
val accessible = declaredField.isAccessible
176174
declaredField.isAccessible = true
177175

178176
val modifiersField = Field::class.java.getDeclaredField("modifiers")
179177
modifiersField.isAccessible = true
180178

181179
val target = mockTarget(fieldModel) {
182-
FieldMockTarget(fieldModel.classId.name, model.classId.name, UtConcreteValue(classInstance), field.name)
180+
FieldMockTarget(fieldModel.classId.name, model.classId.name, UtConcreteValue(classInstance), fieldId.name)
183181
}
184182
val value = construct(fieldModel, target).value
185183
val instance = if (Modifier.isStatic(declaredField.modifiers)) null else classInstance
@@ -394,7 +392,7 @@ class MockValueConstructor(
394392
val instanceClassId = instanceModel.classId
395393
val fieldModel = directSetterModel.fieldModel
396394

397-
val field = instance::class.java.findField(directSetterModel.fieldId.name)
395+
val field = directSetterModel.fieldId.jField
398396
val isAccessible = field.isAccessible
399397

400398
try {

0 commit comments

Comments
 (0)