-
Notifications
You must be signed in to change notification settings - Fork 46
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
Conversation
Problems discovered in previous PR that were fixed but the fixes were not reviewed:
|
fun ClassId.findFieldOrNull(fieldName: String): Field? = jClass.findFieldOrNull(fieldName) | ||
fun ClassId.findFieldByIdOrNull(fieldId: FieldId): Field? { | ||
if (isNotSubtypeOf(fieldId.declaringClass)) { | ||
return null | ||
} | ||
|
||
return fieldId.safeField | ||
} |
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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}") |
There was a problem hiding this comment.
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"
There was a problem hiding this comment.
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) } } |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same about issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this 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. |
There was a problem hiding this comment.
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`. |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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 } |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 id
s. Maybe rename id
to a more descriptive name?
There was a problem hiding this comment.
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}") |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
1dae4a8
to
96a72a7
Compare
Description
Now if class has hidden fields, they can be recognized by engine and correct models are built.
Fixes #647
Type of Change
How Has This Been Tested?
Automated Testing
Switched
org.utbot.examples.objects.HiddenFieldExampleTest#testCheckSuccField
to enabled, addedorg.utbot.examples.objects.HiddenFieldAccessModifiersTest
Manual Scenario
No manual scenario for testing this.
Checklist (remove irrelevant options):
This is the author self-check list