diff --git a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt index 1f6bb848a5..6b55115fa7 100644 --- a/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt +++ b/utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt @@ -7,22 +7,14 @@ import java.lang.reflect.Method val Class<*>.packageName: String get() = `package`?.name?:"" -fun Class<*>.findField(name: String): Field = - findFieldOrNull(name) ?: error("Can't find field $name in $this") - -fun Class<*>.findFieldOrNull(name: String): Field? = generateSequence(this) { it.superclass } - .mapNotNull { - try { - it.getField(name) - } catch (e: NoSuchFieldException) { - try { - it.getDeclaredField(name) - } catch (e: NoSuchFieldException) { - null - } - } - } - .firstOrNull() +/** + * Returns the Field that would be used by compiler when you try to direct access [fieldName] from code, i.e. + * when you write `${this.name}.$fieldName`. + * If there is no field named [fieldName] in the class, null is returned + */ +fun Class<*>.findDirectAccessedFieldOrNull(fieldName: String): Field? = generateSequence(this) { it.superclass } + .flatMap { it.declaredFields.asSequence() } + .firstOrNull { it.name == fieldName } fun Method.invokeCatching(obj: Any?, args: List) = try { Result.success(invoke(obj, *args.toTypedArray())) diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt index e7c2e0bbc7..94104a2f36 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/Api.kt @@ -20,7 +20,7 @@ import org.utbot.framework.plugin.api.util.charClassId import org.utbot.framework.plugin.api.util.constructor import org.utbot.framework.plugin.api.util.doubleClassId import org.utbot.framework.plugin.api.util.executableId -import org.utbot.framework.plugin.api.util.findFieldOrNull +import org.utbot.framework.plugin.api.util.field import org.utbot.framework.plugin.api.util.floatClassId import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.intClassId @@ -30,6 +30,7 @@ import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.longClassId import org.utbot.framework.plugin.api.util.method import org.utbot.framework.plugin.api.util.primitiveTypeJvmNameOrNull +import org.utbot.framework.plugin.api.util.safeField import org.utbot.framework.plugin.api.util.shortClassId import org.utbot.framework.plugin.api.util.toReferenceTypeBytecodeSignature import org.utbot.framework.plugin.api.util.voidClassId @@ -347,7 +348,7 @@ data class UtCompositeModel( if (fields.isNotEmpty()) { append(" ") append(fields.entries.joinToString(", ", "{", "}") { (field, value) -> - if (value.classId != classId || value.isNull()) "${field.name}: $value" else "${field.name}: not evaluated" + if (value.classId != classId || value.isNull()) "(${field.declaringClass}) ${field.name}: $value" else "${field.name}: not evaluated" }) // TODO: here we can get an infinite recursion if we have cyclic dependencies. } if (mocks.isNotEmpty()) { @@ -519,6 +520,7 @@ data class UtDirectSetFieldModel( val fieldId: FieldId, val fieldModel: UtModel, ) : UtStatementModel(instance) { + override fun toString(): String = withToStringThreadLocalReentrancyGuard { val modelRepresentation = when (fieldModel) { is UtAssembleModel -> fieldModel.modelName @@ -855,7 +857,7 @@ open class FieldId(val declaringClass: ClassId, val name: String) { return result } - override fun toString() = declaringClass.findFieldOrNull(name).toString() + override fun toString() = safeField.toString() } inline fun withReflection(block: () -> T): T { diff --git a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt index 02779a6e93..c3532bcbaf 100644 --- a/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt +++ b/utbot-framework-api/src/main/kotlin/org/utbot/framework/plugin/api/util/IdUtil.kt @@ -1,6 +1,5 @@ package org.utbot.framework.plugin.api.util -import org.utbot.common.findFieldOrNull import org.utbot.framework.plugin.api.BuiltinClassId import org.utbot.framework.plugin.api.BuiltinMethodId import org.utbot.framework.plugin.api.ClassId @@ -286,9 +285,17 @@ val ClassId.isMap: Boolean val ClassId.isIterableOrMap: Boolean get() = isIterable || isMap -fun ClassId.findFieldOrNull(fieldName: String): Field? = jClass.findFieldOrNull(fieldName) +fun ClassId.findFieldByIdOrNull(fieldId: FieldId): Field? { + if (isNotSubtypeOf(fieldId.declaringClass)) { + return null + } + + return fieldId.safeField +} -fun ClassId.hasField(fieldName: String): Boolean = findFieldOrNull(fieldName) != null +fun ClassId.hasField(fieldId: FieldId): Boolean { + return findFieldByIdOrNull(fieldId) != null +} fun ClassId.defaultValueModel(): UtModel = when (this) { intClassId -> UtPrimitiveModel(0) @@ -303,11 +310,11 @@ fun ClassId.defaultValueModel(): UtModel = when (this) { } // FieldId utils +val FieldId.safeField: Field? + get() = declaringClass.jClass.declaredFields.firstOrNull { it.name == name } -// TODO: maybe cache it somehow in the future val FieldId.field: Field - get() = declaringClass.jClass.declaredFields.firstOrNull { it.name == name } - ?: error("Field $name is not found in class ${declaringClass.jClass.name}") + get() = safeField ?: error("Field $name is not found in class ${declaringClass.jClass.name}") // https://docstore.mik.ua/orelly/java-ent/jnut/ch03_13.htm val FieldId.isInnerClassEnclosingClassReference: Boolean diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Hierarchy.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Hierarchy.kt index 113485b18a..c610ab822c 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Hierarchy.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Hierarchy.kt @@ -28,10 +28,12 @@ class Hierarchy(private val typeRegistry: TypeRegistry) { type as? RefType ?: error("$type is not a refType") val realType = typeRegistry.findRealType(type) as RefType + val realFieldDeclaringType = typeRegistry.findRealType(field.declaringClass.type) as RefType - val ancestorType = ancestors(realType.sootClass.id).lastOrNull { it.declaresField(field.subSignature) }?.type - ?: error("No such field ${field.subSignature} found in ${realType.sootClass.name}") - return ChunkId("$ancestorType", field.name) + if (realFieldDeclaringType.sootClass !in ancestors(realType.sootClass.id)) { + error("No such field ${field.subSignature} found in ${realType.sootClass.name}") + } + return ChunkId("$realFieldDeclaringType", field.name) } /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Strings.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Strings.kt index cb0e1fba1d..4c31f32c8f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Strings.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Strings.kt @@ -313,7 +313,7 @@ sealed class UtAbstractStringBuilderWrapper(className: String) : BaseOverriddenW val arrayValuesChunkId = typeRegistry.arrayChunkId(charArrayType) - val valuesFieldChunkId = hierarchy.chunkIdForField(utStringClass.type, overriddenClass.valueField) + val valuesFieldChunkId = hierarchy.chunkIdForField(utStringClass.type, utStringClass.valueField) val valuesArrayAddrDescriptor = MemoryChunkDescriptor(valuesFieldChunkId, wrapper.type, charType) val valuesArrayAddr = findArray(valuesArrayAddrDescriptor, MemoryState.CURRENT).select(wrapper.addr) diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt index 4563012cb4..1a4f4c984b 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/Traverser.kt @@ -8,7 +8,6 @@ import kotlinx.collections.immutable.toPersistentMap import kotlinx.collections.immutable.toPersistentSet import org.utbot.common.WorkaroundReason.HACK import org.utbot.common.WorkaroundReason.REMOVE_ANONYMOUS_CLASSES -import org.utbot.common.findField import org.utbot.common.unreachableBranch import org.utbot.common.withAccessibility import org.utbot.common.workaround @@ -90,8 +89,9 @@ import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.UtMethod import org.utbot.framework.plugin.api.classId import org.utbot.framework.plugin.api.id -import org.utbot.framework.plugin.api.util.id +import org.utbot.framework.plugin.api.util.field import org.utbot.framework.plugin.api.util.jClass +import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.signature import org.utbot.framework.plugin.api.util.utContext import org.utbot.framework.util.executableId @@ -582,7 +582,7 @@ class Traverser( declaringClass: SootClass, stmt: Stmt ): SymbolicStateUpdate { - val concreteValue = extractConcreteValue(field, declaringClass) + val concreteValue = extractConcreteValue(field) val (symbolicResult, symbolicStateUpdate) = toMethodResult(concreteValue, field.type) val symbolicValue = (symbolicResult as SymbolicSuccess).value @@ -634,12 +634,12 @@ class Traverser( // Some fields are inaccessible with reflection, so we have to instantiate it by ourselves. // Otherwise, extract it from the class. // TODO JIRA:1593 - private fun extractConcreteValue(field: SootField, declaringClass: SootClass): Any? = + private fun extractConcreteValue(field: SootField): Any? = when (field.signature) { SECURITY_FIELD_SIGNATURE -> SecurityManager() FIELD_FILTER_MAP_FIELD_SIGNATURE -> mapOf(Reflection::class to arrayOf("fieldFilterMap", "methodFilterMap")) METHOD_FILTER_MAP_FIELD_SIGNATURE -> emptyMap, Array>() - else -> declaringClass.id.jClass.findField(field.name).let { it.withAccessibility { it.get(null) } } + else -> field.fieldId.field.let { it.withAccessibility { it.get(null) } } } private fun isStaticInstanceInMethodResult(id: ClassId, methodResult: MethodResult?) = diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt index 95565eb600..323dd552e6 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/TypeResolver.kt @@ -70,8 +70,6 @@ class TypeResolver(private val typeRegistry: TypeRegistry, private val hierarchy hierarchy .ancestors(type.sootClass.id) .flatMap { it.fields } - .asReversed() // to take fields of the farthest parent in the distinctBy - .distinctBy { it.name } // TODO we lose hidden fields here JIRA:315 } /** diff --git a/utbot-framework/src/main/kotlin/org/utbot/engine/ValueConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/engine/ValueConstructor.kt index 06928beda1..d8537eca97 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/engine/ValueConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/engine/ValueConstructor.kt @@ -1,7 +1,5 @@ package org.utbot.engine -import org.utbot.common.findField -import org.utbot.common.findFieldOrNull import org.utbot.common.invokeCatching import org.utbot.common.withAccessibility import org.utbot.framework.plugin.api.ClassId @@ -37,6 +35,7 @@ import org.utbot.framework.plugin.api.UtValueExecutionState import org.utbot.framework.plugin.api.UtVoidModel import org.utbot.framework.plugin.api.isMockModel import org.utbot.framework.plugin.api.util.constructor +import org.utbot.framework.plugin.api.util.field import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.method import org.utbot.framework.plugin.api.util.utContext @@ -212,9 +211,8 @@ class ValueConstructor { val classInstance = javaClass.anyInstance constructedObjects[model] = classInstance - model.fields.forEach { (field, fieldModel) -> - val declaredField = - javaClass.findFieldOrNull(field.name) ?: error("Can't find field: $field for $javaClass") + model.fields.forEach { (fieldId, fieldModel) -> + val declaredField = fieldId.field val accessible = declaredField.isAccessible try { @@ -228,7 +226,7 @@ class ValueConstructor { fieldModel.classId.name, model.classId.name, UtConcreteValue(classInstance), - field.name + fieldId.name ) } val value = construct(fieldModel, target).value @@ -360,7 +358,7 @@ class ValueConstructor { val instanceClassId = instanceModel.classId val fieldModel = directSetterModel.fieldModel - val field = instance::class.java.findField(directSetterModel.fieldId.name) + val field = directSetterModel.fieldId.field val isAccessible = field.isAccessible try { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgFieldStateManager.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgFieldStateManager.kt index 570dbe3a6f..64a50e17a8 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgFieldStateManager.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgFieldStateManager.kt @@ -177,7 +177,7 @@ internal class CgFieldStateManagerImpl(val context: CgContext) } // if previous field has type that does not have current field, this field is inaccessible - if (index > 0 && !path[index - 1].type.hasField(fieldPathElement.field.name)) { + if (index > 0 && !path[index - 1].type.hasField(fieldPathElement.field)) { lastAccessibleIndex = index - 1 break } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt index c9b7f2f231..ddb69f7e0e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgMethodConstructor.kt @@ -43,7 +43,6 @@ import org.utbot.framework.codegen.model.tree.CgEqualTo import org.utbot.framework.codegen.model.tree.CgErrorTestMethod import org.utbot.framework.codegen.model.tree.CgExecutableCall import org.utbot.framework.codegen.model.tree.CgExpression -import org.utbot.framework.codegen.model.tree.CgFieldAccess import org.utbot.framework.codegen.model.tree.CgGetJavaClass import org.utbot.framework.codegen.model.tree.CgIfStatement import org.utbot.framework.codegen.model.tree.CgLiteral @@ -928,8 +927,8 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c private fun FieldId.getAccessExpression(variable: CgVariable): CgExpression = // Can directly access field only if it is declared in variable class (or in its ancestors) // and is accessible from current package - if (variable.type.hasField(name) && isAccessibleFrom(testClassPackageName)) { - if (field.isStatic) CgStaticFieldAccess(this) else CgFieldAccess(variable, this) + if (variable.type.hasField(this) && isAccessibleFrom(testClassPackageName)) { + if (field.isStatic) CgStaticFieldAccess(this) else variable[this] } else { testClassThisInstance[getFieldValue](variable, stringLiteral(name)) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt index e66b988daa..4b8213eaec 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/tree/CgVariableConstructor.kt @@ -15,10 +15,8 @@ import org.utbot.framework.codegen.model.tree.CgAllocateInitializedArray import org.utbot.framework.codegen.model.tree.CgDeclaration import org.utbot.framework.codegen.model.tree.CgEnumConstantAccess import org.utbot.framework.codegen.model.tree.CgExpression -import org.utbot.framework.codegen.model.tree.CgFieldAccess import org.utbot.framework.codegen.model.tree.CgGetJavaClass import org.utbot.framework.codegen.model.tree.CgLiteral -import org.utbot.framework.codegen.model.tree.CgNotNullAssertion import org.utbot.framework.codegen.model.tree.CgStaticFieldAccess import org.utbot.framework.codegen.model.tree.CgValue import org.utbot.framework.codegen.model.tree.CgVariable @@ -47,8 +45,7 @@ import org.utbot.framework.plugin.api.UtPrimitiveModel import org.utbot.framework.plugin.api.UtReferenceModel import org.utbot.framework.plugin.api.UtVoidModel import org.utbot.framework.plugin.api.util.defaultValueModel -import org.utbot.framework.plugin.api.util.field -import org.utbot.framework.plugin.api.util.findFieldOrNull +import org.utbot.framework.plugin.api.util.findFieldByIdOrNull import org.utbot.framework.plugin.api.util.id import org.utbot.framework.plugin.api.util.intClassId import org.utbot.framework.plugin.api.util.isArray @@ -128,9 +125,8 @@ internal class CgVariableConstructor(val context: CgContext) : } for ((fieldId, fieldModel) in model.fields) { - val field = fieldId.field val variableForField = getOrCreateVariable(fieldModel) - val fieldFromVariableSpecifiedType = obj.type.findFieldOrNull(field.name) + val field = obj.type.findFieldByIdOrNull(fieldId) // we cannot set field directly if variable declared type does not have such field // or we cannot directly create variable for field with the specified type (it is private, for example) @@ -138,11 +134,8 @@ internal class CgVariableConstructor(val context: CgContext) : // Object heapByteBuffer = createInstance("java.nio.HeapByteBuffer"); // branchRegisterRequest.byteBuffer = heapByteBuffer; // byteBuffer is field of type ByteBuffer and upper line is incorrect - val canFieldBeDirectlySetByVariableAndFieldTypeRestrictions = - fieldFromVariableSpecifiedType != null && fieldFromVariableSpecifiedType.type.id == variableForField.type - if (canFieldBeDirectlySetByVariableAndFieldTypeRestrictions && fieldId.canBeSetIn(testClassPackageName)) { - // TODO: check if it is correct to use declaringClass of a field here - val fieldAccess = if (field.isStatic) CgStaticFieldAccess(fieldId) else CgFieldAccess(obj, fieldId) + if (field != null && field.type.id == variableForField.type && fieldId.canBeSetIn(testClassPackageName)) { + val fieldAccess = if (field.isStatic) CgStaticFieldAccess(fieldId) else obj[fieldId] fieldAccess `=` variableForField } else { // composite models must not have info about static fields, hence only non-static fields are set here diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/DslUtil.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/DslUtil.kt index 5edbf60ec6..81720dccf7 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/DslUtil.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/util/DslUtil.kt @@ -1,5 +1,6 @@ package org.utbot.framework.codegen.model.util +import org.utbot.common.findDirectAccessedFieldOrNull import org.utbot.framework.codegen.model.constructor.tree.CgCallableAccessManager import org.utbot.framework.codegen.model.tree.CgArrayElementAccess import org.utbot.framework.codegen.model.tree.CgDecrement @@ -16,6 +17,7 @@ import org.utbot.framework.codegen.model.tree.CgLessThan import org.utbot.framework.codegen.model.tree.CgLiteral import org.utbot.framework.codegen.model.tree.CgStaticFieldAccess import org.utbot.framework.codegen.model.tree.CgThisInstance +import org.utbot.framework.codegen.model.tree.CgTypeCast import org.utbot.framework.codegen.model.tree.CgVariable import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage @@ -25,9 +27,11 @@ import org.utbot.framework.plugin.api.util.booleanClassId import org.utbot.framework.plugin.api.util.byteClassId import org.utbot.framework.plugin.api.util.charClassId import org.utbot.framework.plugin.api.util.doubleClassId +import org.utbot.framework.plugin.api.util.field import org.utbot.framework.plugin.api.util.floatClassId import org.utbot.framework.plugin.api.util.intClassId import org.utbot.framework.plugin.api.util.isArray +import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.longClassId import org.utbot.framework.plugin.api.util.objectClassId import org.utbot.framework.plugin.api.util.shortClassId @@ -72,7 +76,11 @@ fun stringLiteral(string: String) = CgLiteral(stringClassId, string) // non-static fields operator fun CgExpression.get(fieldId: FieldId): CgFieldAccess = - CgFieldAccess(this, fieldId) + if (type.jClass.findDirectAccessedFieldOrNull(fieldId.name) != fieldId.field) { + CgFieldAccess(CgTypeCast(fieldId.declaringClass, this), fieldId) + } else { + CgFieldAccess(this, fieldId) + } // static fields // TODO: unused receiver diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt index 28f5626f16..b1db6eb9d3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/visitor/CgKotlinRenderer.kt @@ -121,7 +121,9 @@ internal class CgKotlinRenderer(context: CgContext, printer: CgPrinter = CgPrint // Property access override fun visit(element: CgFieldAccess) { + if (element.caller is CgTypeCast) print("(") element.caller.accept(this) + if (element.caller is CgTypeCast) print(")") renderAccess(element.caller) print(element.fieldId.name) } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/MockValueConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/MockValueConstructor.kt index d1452b2cef..6baa5de0aa 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/MockValueConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/concrete/MockValueConstructor.kt @@ -1,7 +1,5 @@ package org.utbot.framework.concrete -import org.utbot.common.findField -import org.utbot.common.findFieldOrNull import org.utbot.common.invokeCatching import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.ConstructorId @@ -33,6 +31,7 @@ import org.utbot.framework.plugin.api.UtVoidModel import org.utbot.framework.plugin.api.isMockModel import org.utbot.framework.plugin.api.util.constructor import org.utbot.framework.plugin.api.util.executableId +import org.utbot.framework.plugin.api.util.field import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.method import org.utbot.framework.plugin.api.util.utContext @@ -169,9 +168,8 @@ class MockValueConstructor( mockInstance } - model.fields.forEach { (field, fieldModel) -> - val declaredField = - javaClass.findFieldOrNull(field.name) ?: error("Can't find field: $field for $javaClass") + model.fields.forEach { (fieldId, fieldModel) -> + val declaredField = fieldId.field val accessible = declaredField.isAccessible declaredField.isAccessible = true @@ -179,7 +177,7 @@ class MockValueConstructor( modifiersField.isAccessible = true val target = mockTarget(fieldModel) { - FieldMockTarget(fieldModel.classId.name, model.classId.name, UtConcreteValue(classInstance), field.name) + FieldMockTarget(fieldModel.classId.name, model.classId.name, UtConcreteValue(classInstance), fieldId.name) } val value = construct(fieldModel, target).value val instance = if (Modifier.isStatic(declaredField.modifiers)) null else classInstance @@ -394,7 +392,7 @@ class MockValueConstructor( val instanceClassId = instanceModel.classId val fieldModel = directSetterModel.fieldModel - val field = instance::class.java.findField(directSetterModel.fieldId.name) + val field = directSetterModel.fieldId.field val isAccessible = field.isAccessible try { diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/UtModelTestCaseChecker.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/UtModelTestCaseChecker.kt index 101ff6735f..c05ae8522a 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/UtModelTestCaseChecker.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/UtModelTestCaseChecker.kt @@ -6,10 +6,10 @@ import org.utbot.common.ClassLocation import org.utbot.common.FileUtil.findPathToClassFiles import org.utbot.common.FileUtil.locateClass import org.utbot.common.WorkaroundReason.HACK -import org.utbot.common.findField import org.utbot.common.workaround import org.utbot.engine.prettify import org.utbot.framework.UtSettings.checkSolverTimeoutMillis +import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.CodegenLanguage import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.MockStrategyApi @@ -27,8 +27,6 @@ import org.utbot.framework.plugin.api.getOrThrow import org.utbot.framework.plugin.api.util.UtContext import org.utbot.framework.plugin.api.util.defaultValueModel import org.utbot.framework.plugin.api.util.executableId -import org.utbot.framework.plugin.api.util.fieldId -import org.utbot.framework.plugin.api.util.jClass import org.utbot.framework.plugin.api.util.withUtContext import java.nio.file.Path import kotlin.reflect.KClass @@ -167,8 +165,8 @@ internal abstract class UtModelTestCaseChecker( /** * Finds field model in [UtCompositeModel] and [UtAssembleModel]. For assemble model supports direct field access only. */ - protected fun UtModel.findField(fieldName: String): UtModel = - findField(this.classId.jClass.findField(fieldName).fieldId) + protected fun UtModel.findField(fieldName: String, declaringClass: ClassId = this.classId): UtModel = + findField(FieldId(declaringClass, fieldName)) /** * Finds field model in [UtCompositeModel] and [UtAssembleModel]. For assemble model supports direct field access only. diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/enums/ClassWithEnumTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/enums/ClassWithEnumTest.kt index 48fb11b8ba..8aa815c289 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/enums/ClassWithEnumTest.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/enums/ClassWithEnumTest.kt @@ -1,6 +1,5 @@ package org.utbot.examples.enums -import org.utbot.common.findField import org.utbot.examples.UtValueTestCaseChecker import org.utbot.examples.DoNotCalculate import org.utbot.examples.enums.ClassWithEnum.StatusEnum.ERROR @@ -13,6 +12,7 @@ import org.utbot.framework.plugin.api.FieldId import org.utbot.framework.plugin.api.util.id import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test +import org.utbot.framework.plugin.api.util.field class ClassWithEnumTest : UtValueTestCaseChecker(testClass = ClassWithEnum::class) { @Test @@ -111,7 +111,7 @@ class ClassWithEnumTest : UtValueTestCaseChecker(testClass = ClassWithEnum::clas eq(1), { t, staticsAfter, r -> // for some reasons x is inaccessible - val x = t.javaClass.findField("x").get(t) as Int + val x = FieldId(t.javaClass.id, "x").field.get(t) as Int val y = staticsAfter[FieldId(ClassWithEnum.ClassWithStaticField::class.id, "y")]!!.value as Int diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/objects/HiddenFieldAccessModifiersTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/objects/HiddenFieldAccessModifiersTest.kt new file mode 100644 index 0000000000..8bd16696cf --- /dev/null +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/objects/HiddenFieldAccessModifiersTest.kt @@ -0,0 +1,19 @@ +package org.utbot.examples.objects + +import org.utbot.examples.UtValueTestCaseChecker +import org.utbot.examples.DoNotCalculate +import org.utbot.examples.eq +import org.junit.jupiter.api.Test + +internal class HiddenFieldAccessModifiersTest : UtValueTestCaseChecker(testClass = HiddenFieldAccessModifiersExample::class) { + @Test + fun testCheckSuperFieldEqualsOne() { + check( + HiddenFieldAccessModifiersExample::checkSuperFieldEqualsOne, + eq(3), + { o, _ -> o == null }, + { _, r -> r == true }, + { _, r -> r == false}, + ) + } +} \ No newline at end of file diff --git a/utbot-framework/src/test/kotlin/org/utbot/examples/objects/HiddenFieldExampleTest.kt b/utbot-framework/src/test/kotlin/org/utbot/examples/objects/HiddenFieldExampleTest.kt index 70664a6359..293f1e1a0a 100644 --- a/utbot-framework/src/test/kotlin/org/utbot/examples/objects/HiddenFieldExampleTest.kt +++ b/utbot-framework/src/test/kotlin/org/utbot/examples/objects/HiddenFieldExampleTest.kt @@ -3,12 +3,10 @@ package org.utbot.examples.objects import org.utbot.examples.UtValueTestCaseChecker import org.utbot.examples.DoNotCalculate import org.utbot.examples.eq -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test internal class HiddenFieldExampleTest : UtValueTestCaseChecker(testClass = HiddenFieldExample::class) { @Test - // Engine creates HiddenFieldSuccClass instead of HiddenFieldSuperClass, feels wrong field and matchers fail fun testCheckHiddenField() { check( HiddenFieldExample::checkHiddenField, @@ -22,8 +20,6 @@ internal class HiddenFieldExampleTest : UtValueTestCaseChecker(testClass = Hidde } @Test - @Disabled("SAT-315 Engine cannot work with hidden fields") - // Engine translates calls to super.b as calls to succ.b fun testCheckSuccField() { check( HiddenFieldExample::checkSuccField, @@ -33,7 +29,6 @@ internal class HiddenFieldExampleTest : UtValueTestCaseChecker(testClass = Hidde { o, r -> o.a != 1 && o.b == 2.0 && r == 2 }, { o, r -> o.a != 1 && o.b != 2.0 && (o as HiddenFieldSuperClass).b == 3 && r == 3 }, { o, r -> o.a != 1 && o.b != 2.0 && (o as HiddenFieldSuperClass).b != 3 && r == 4 }, - coverage = DoNotCalculate ) } } \ No newline at end of file diff --git a/utbot-sample/src/main/java/org/utbot/examples/objects/HiddenFieldAccessModifiersExample.java b/utbot-sample/src/main/java/org/utbot/examples/objects/HiddenFieldAccessModifiersExample.java new file mode 100644 index 0000000000..16cf7f50b9 --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/objects/HiddenFieldAccessModifiersExample.java @@ -0,0 +1,7 @@ +package org.utbot.examples.objects; + +public class HiddenFieldAccessModifiersExample { + public boolean checkSuperFieldEqualsOne(HiddenFieldAccessModifiersSucc b) { + return b.getF() == 1; + } +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/objects/HiddenFieldAccessModifiersSucc.java b/utbot-sample/src/main/java/org/utbot/examples/objects/HiddenFieldAccessModifiersSucc.java new file mode 100644 index 0000000000..ba200f176f --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/objects/HiddenFieldAccessModifiersSucc.java @@ -0,0 +1,5 @@ +package org.utbot.examples.objects; + +public class HiddenFieldAccessModifiersSucc extends HiddenFieldAccessModifiersSuper { + private int f; +} diff --git a/utbot-sample/src/main/java/org/utbot/examples/objects/HiddenFieldAccessModifiersSuper.java b/utbot-sample/src/main/java/org/utbot/examples/objects/HiddenFieldAccessModifiersSuper.java new file mode 100644 index 0000000000..1d819d711d --- /dev/null +++ b/utbot-sample/src/main/java/org/utbot/examples/objects/HiddenFieldAccessModifiersSuper.java @@ -0,0 +1,11 @@ +package org.utbot.examples.objects; + +public class HiddenFieldAccessModifiersSuper { + public int f; + public int getF() { + return this.f; + } + public void setF(int val) { + this.f = val; + } +}