Skip to content

Fix engine to properly handle hidden fields #647 #627

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 1 commit into from
Aug 3, 2022

Conversation

volivan239
Copy link
Collaborator

@volivan239 volivan239 commented Aug 1, 2022

Description

Now if class has hidden fields, they can be recognized by engine and correct models are built.

Fixes #647

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

How Has This Been Tested?

Automated Testing

Switched org.utbot.examples.objects.HiddenFieldExampleTest#testCheckSuccField to enabled, added org.utbot.examples.objects.HiddenFieldAccessModifiersTest

Manual Scenario

No manual scenario for testing this.

Checklist (remove irrelevant options):

This is the author self-check list

  • The change followed the style guidelines of the UTBot project
  • Self-review of the code is passed
  • The change contains enough commentaries, particularly in hard-to-understand areas
  • New documentation is provided or existed one is altered
  • No new warnings
  • New tests have been added
  • All tests pass locally with my changes

@volivan239
Copy link
Collaborator Author

This PR is derived from #592. See this comment for more details.

@volivan239
Copy link
Collaborator Author

Problems discovered in previous PR that were fixed but the fixes were not reviewed:

  • Changes in IdUtil.kt (naming and logic)
  • Naming problem in Traverser.kt:642

@volivan239 volivan239 requested review from Damtev and dtim August 1, 2022 12:28
@volivan239 volivan239 marked this pull request as ready for review August 1, 2022 12:28
@volivan239 volivan239 changed the title Fixed engine dealing with field hiding Fix engine to properly handle hidden fields Aug 1, 2022
Comment on lines 289 to 294
fun ClassId.findFieldOrNull(fieldName: String): Field? = jClass.findFieldOrNull(fieldName)
fun ClassId.findFieldByIdOrNull(fieldId: FieldId): Field? {
if (isNotSubtypeOf(fieldId.declaringClass)) {
return null
}

return fieldId.safeField
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it a breaking change - the previous version looked for all fields (including inherited) but this version looks only for declared fields (so, skipping inherited)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version looks for inherited fields as well: if field in this is inherited, then this should be a subtype of field's declaring class, so this method will find it.


// 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}")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, "found" should be replaced with "declared"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

else -> {
val id = field.fieldId
val jField = id.field
jField.let { it.withAccessibility { it.get(null) } }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, do not write this in one line

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

internal class HiddenFieldAccessModifiersTest : UtValueTestCaseChecker(testClass = HiddenFieldAccessModifiersExample::class) {
@Test
fun testCheckSuperFieldEqualsOne() {
// TODO: currently, codegen can't handle tests with field hiding
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create an issue and mention it here, please

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

{ o, r -> o.a != 1 && o.b != 2.0 && (o as HiddenFieldSuperClass).b != 3 && r == 4 },
coverage = DoNotCalculate
)
// TODO: currently, codegen can't handle tests with field hiding
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same about issue

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Collaborator

@dtim dtim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally OK, but a fix in UtAbstractStringBuilderWrapper.value() needs correction.

As you fix only the engine part, I think it could be useful to create two separate issues, one for the engine, and another for the codegen ("children" of #563 and linked to it). That way you could reference (and close) the relevant issue and keep another issue open, and then close both the remaining issue and the parent issue with a second PR.

}
.firstOrNull()
/**
* Returns the Field that would be used by compiler when you try to direct access [fieldName] from code, i.e.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"directly access", "from the code"

.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`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that ${this.name}.$fieldName expression may be hard to understand and a bit misguiding (as fieldName is not a variable). Maybe use something simpler, like obj.fieldName?

Copy link
Collaborator Author

@volivan239 volivan239 Aug 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a valuable comment, but I found out that this function is not even needed in this PR, so this is to be fixed in #592. Same to the comment above

@@ -303,11 +310,12 @@ fun ClassId.defaultValueModel(): UtModel = when (this) {
}

// FieldId utils
val FieldId.safeField: Field?
get() = declaringClass.jClass.declaredFields.firstOrNull { it.name == name }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JFYI: theoretically it is possible to have ClassId but not jClass, and to get ClassNotFoundException. We currently have this problem in other jClass-related code (e.g., ClassId.canonicalName, ClassId.simpleName), and it would be a very rare case (at least while lambdas are filtered out), so I think it's OK.

@@ -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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replacement of overriddenClass.valueField with utStringClass.valueField seems incorrect, as we are dealing with UtStringBuilder/UtStringBuffer and not UtString. At the same time, the first argument utStringClass.type seems suspicious.

Copy link
Collaborator Author

@volivan239 volivan239 Aug 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed, now overriddenClass is used in both places

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<Class<*>, Array<String>>()
else -> declaringClass.id.jClass.findField(field.name).let { it.withAccessibility { it.get(null) } }
else -> {
val id = field.fieldId
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code seems not very clear, too many ids. Maybe rename id to a more descriptive name?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replaced with fieldId


// 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}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have a ClassId.jClass, maybe it would be consistent to rename FieldId.field to FieldId.jField?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@volivan239 volivan239 changed the title Fix engine to properly handle hidden fields Fix engine to properly handle hidden fields #647 Aug 2, 2022
@volivan239 volivan239 requested review from dtim and Damtev August 2, 2022 16:38
Copy link
Collaborator

@dtim dtim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@volivan239 volivan239 force-pushed the volivan239/fix_field_hiding_engine_only branch from 1dae4a8 to 96a72a7 Compare August 3, 2022 12:48
@volivan239 volivan239 merged commit affa753 into main Aug 3, 2022
@volivan239 volivan239 deleted the volivan239/fix_field_hiding_engine_only branch August 3, 2022 15:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

Engine treats hidden field and the field it is hidden by as the same field
3 participants