Skip to content

Commit bb9faff

Browse files
committed
Replace Kotlin getters/setters with property access in codegen
1 parent f734c96 commit bb9faff

File tree

7 files changed

+85
-13
lines changed

7 files changed

+85
-13
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,12 @@ fun ClassId.defaultValueModel(): UtModel = when (this) {
387387
else -> UtNullModel(this)
388388
}
389389

390+
val ClassId.allDeclaredFieldIds: Sequence<FieldId>
391+
get() =
392+
generateSequence(this.jClass) { it.superclass }
393+
.flatMap { it.declaredFields.asSequence() }
394+
.map { it.fieldId }
395+
390396
// FieldId utils
391397
val FieldId.safeJField: Field?
392398
get() = declaringClass.jClass.declaredFields.firstOrNull { it.name == name }

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA
293293

294294
private fun FieldId.accessSuitability(accessor: CgExpression?): FieldAccessorSuitability {
295295
// Check field accessibility.
296-
if (!isAccessibleFrom(testClassPackageName)) {
296+
if (!isAccessibleFrom(context)) {
297297
return ReflectionOnly
298298
}
299299

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ internal class CgFieldStateManagerImpl(val context: CgContext)
182182
for ((index, fieldPathElement) in path.withIndex()) {
183183
when (fieldPathElement) {
184184
is FieldAccess -> {
185-
if (!fieldPathElement.field.isAccessibleFrom(testClassPackageName)) {
185+
if (!fieldPathElement.field.isAccessibleFrom(context)) {
186186
lastAccessibleIndex = index - 1
187187
break
188188
}
@@ -246,7 +246,7 @@ internal class CgFieldStateManagerImpl(val context: CgContext)
246246

247247
private fun variableForStaticFieldState(owner: ClassId, fieldPath: FieldPath, customName: String?): CgVariable {
248248
val firstField = (fieldPath.elements.first() as FieldAccess).field
249-
val firstAccessor = if (owner.isAccessibleFrom(testClassPackageName) && firstField.isAccessibleFrom(testClassPackageName)) {
249+
val firstAccessor = if (owner.isAccessibleFrom(testClassPackageName) && firstField.isAccessibleFrom(context)) {
250250
owner[firstField]
251251
} else {
252252
// TODO: there is a function getClassOf() for these purposes, but it is not accessible from here for now

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
218218
val accessibleStaticFields = statics.accessibleFields()
219219
for ((field, _) in accessibleStaticFields) {
220220
val declaringClass = field.declaringClass
221-
val fieldAccessible = field.isAccessibleFrom(testClassPackageName)
221+
val fieldAccessible = field.isAccessibleFrom(context)
222222

223223
// prevValue is nullable if not accessible because of getStaticFieldValue(..) : Any?
224224
val prevValue = newVar(
@@ -243,7 +243,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
243243
val accessibleStaticFields = statics.accessibleFields()
244244
for ((field, model) in accessibleStaticFields) {
245245
val declaringClass = field.declaringClass
246-
val fieldAccessible = field.canBeSetIn(testClassPackageName)
246+
val fieldAccessible = field.canBeSetIn(context)
247247

248248
val fieldValue = if (isParametrized) {
249249
currentMethodParameters[CgParameterKind.Statics(model)]
@@ -264,7 +264,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
264264

265265
private fun recoverStaticFields() {
266266
for ((field, prevValue) in prevStaticFieldValues.accessibleFields()) {
267-
if (field.canBeSetIn(testClassPackageName)) {
267+
if (field.canBeSetIn(context)) {
268268
field.declaringClass[field] `=` prevValue
269269
} else {
270270
val declaringClass = getClassOf(field.declaringClass)
@@ -1039,7 +1039,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
10391039
private fun FieldId.getAccessExpression(variable: CgVariable): CgExpression =
10401040
// Can directly access field only if it is declared in variable class (or in its ancestors)
10411041
// and is accessible from current package
1042-
if (variable.type.hasField(this) && isAccessibleFrom(testClassPackageName)) {
1042+
if (variable.type.hasField(this) && isAccessibleFrom(context)) {
10431043
if (jField.isStatic) CgStaticFieldAccess(this) else CgFieldAccess(variable, this)
10441044
} else {
10451045
utilsClassId[getFieldValue](variable, this.declaringClass.name, this.name)

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import org.utbot.framework.codegen.model.constructor.util.isDefaultValueOf
1414
import org.utbot.framework.codegen.model.constructor.util.isNotDefaultValueOf
1515
import org.utbot.framework.codegen.model.constructor.util.typeCast
1616
import org.utbot.framework.codegen.model.tree.CgAllocateArray
17+
import org.utbot.framework.codegen.model.tree.CgAssignment
1718
import org.utbot.framework.codegen.model.tree.CgDeclaration
1819
import org.utbot.framework.codegen.model.tree.CgEnumConstantAccess
1920
import org.utbot.framework.codegen.model.tree.CgExecutableCall
@@ -22,18 +23,22 @@ import org.utbot.framework.codegen.model.tree.CgFieldAccess
2223
import org.utbot.framework.codegen.model.tree.CgGetJavaClass
2324
import org.utbot.framework.codegen.model.tree.CgLiteral
2425
import org.utbot.framework.codegen.model.tree.CgMethodCall
26+
import org.utbot.framework.codegen.model.tree.CgStatement
2527
import org.utbot.framework.codegen.model.tree.CgStaticFieldAccess
2628
import org.utbot.framework.codegen.model.tree.CgValue
2729
import org.utbot.framework.codegen.model.tree.CgVariable
2830
import org.utbot.framework.codegen.model.util.at
2931
import org.utbot.framework.codegen.model.util.canBeSetIn
32+
import org.utbot.framework.codegen.model.util.fieldThisIsGetterFor
33+
import org.utbot.framework.codegen.model.util.fieldThisIsSetterFor
3034
import org.utbot.framework.codegen.model.util.inc
3135
import org.utbot.framework.codegen.model.util.isAccessibleFrom
3236
import org.utbot.framework.codegen.model.util.lessThan
3337
import org.utbot.framework.codegen.model.util.nullLiteral
3438
import org.utbot.framework.codegen.model.util.resolve
3539
import org.utbot.framework.plugin.api.BuiltinClassId
3640
import org.utbot.framework.plugin.api.ClassId
41+
import org.utbot.framework.plugin.api.CodegenLanguage
3742
import org.utbot.framework.plugin.api.ConstructorId
3843
import org.utbot.framework.plugin.api.MethodId
3944
import org.utbot.framework.plugin.api.UtArrayModel
@@ -188,7 +193,7 @@ internal class CgVariableConstructor(val context: CgContext) :
188193
// byteBuffer is field of type ByteBuffer and upper line is incorrect
189194
val canFieldBeDirectlySetByVariableAndFieldTypeRestrictions =
190195
fieldFromVariableSpecifiedType != null && fieldFromVariableSpecifiedType.type.id == variableForField.type
191-
if (canFieldBeDirectlySetByVariableAndFieldTypeRestrictions && fieldId.canBeSetIn(testClassPackageName)) {
196+
if (canFieldBeDirectlySetByVariableAndFieldTypeRestrictions && fieldId.canBeSetIn(context)) {
192197
// TODO: check if it is correct to use declaringClass of a field here
193198
val fieldAccess = if (field.isStatic) CgStaticFieldAccess(fieldId) else CgFieldAccess(obj, fieldId)
194199
fieldAccess `=` variableForField
@@ -212,7 +217,8 @@ internal class CgVariableConstructor(val context: CgContext) :
212217
instance[statementModel.fieldId] `=` declareOrGet(statementModel.fieldModel)
213218
}
214219
is UtExecutableCallModel -> {
215-
+createCgExecutableCallFromUtExecutableCall(statementModel)
220+
val call = createCgExecutableCallFromUtExecutableCall(statementModel)
221+
+replaceCgExecutableCallWithFieldAccessIfNeeded(call)
216222
}
217223
}
218224
}
@@ -236,6 +242,7 @@ internal class CgVariableConstructor(val context: CgContext) :
236242
val initExpr = if (isPrimitiveWrapperOrString(type)) {
237243
cgLiteralForWrapper(params)
238244
} else {
245+
// TODO: if instantiation chain could be a setter call, we need to replace it in Kotlin
239246
createCgExecutableCallFromUtExecutableCall(executableCall)
240247
}
241248
newVar(type, model, baseName) {
@@ -261,6 +268,23 @@ internal class CgVariableConstructor(val context: CgContext) :
261268
return cgCall
262269
}
263270

271+
private fun replaceCgExecutableCallWithFieldAccessIfNeeded(call: CgExecutableCall): CgStatement {
272+
if (call !is CgMethodCall || context.codegenLanguage != CodegenLanguage.KOTLIN)
273+
return call
274+
275+
val caller = call.caller ?: return call
276+
277+
caller.type.fieldThisIsSetterFor(call.executableId)?.let {
278+
return CgAssignment(caller[it], call.arguments.single())
279+
}
280+
caller.type.fieldThisIsGetterFor(call.executableId)?.let {
281+
require(call.arguments.isEmpty())
282+
return caller[it]
283+
}
284+
285+
return call
286+
}
287+
264288
/**
265289
* Makes a replacement of constructor call to instantiate a primitive wrapper
266290
* with direct setting of the value. The reason is that in Kotlin constructors

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package org.utbot.framework.codegen.model.util
22

33
import org.utbot.framework.plugin.api.ClassId
4+
import org.utbot.framework.plugin.api.FieldId
5+
import org.utbot.framework.plugin.api.MethodId
6+
import org.utbot.framework.plugin.api.util.allDeclaredFieldIds
47
import org.utbot.framework.plugin.api.util.id
58
import org.utbot.framework.plugin.api.util.isArray
69

@@ -28,4 +31,16 @@ infix fun ClassId.isAccessibleFrom(packageName: String): Boolean {
2831
} else {
2932
isPublic || (this.packageName == packageName && (isPackagePrivate || isProtected))
3033
}
31-
}
34+
}
35+
36+
/**
37+
* Returns field of [this], such that [methodId] is a getter for it (or null if methodId doesn't represent a getter)
38+
*/
39+
internal fun ClassId.fieldThisIsGetterFor(methodId: MethodId): FieldId? =
40+
allDeclaredFieldIds.firstOrNull { it.getter == methodId }
41+
42+
/**
43+
* Returns field of [this], such that [methodId] is a setter for it (or null if methodId doesn't represent a setter)
44+
*/
45+
internal fun ClassId.fieldThisIsSetterFor(methodId: MethodId): FieldId? =
46+
allDeclaredFieldIds.firstOrNull { it.setter == methodId }
Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
package org.utbot.framework.codegen.model.util
22

3+
import org.utbot.framework.codegen.model.constructor.context.CgContext
4+
import org.utbot.framework.plugin.api.CodegenLanguage
35
import org.utbot.framework.plugin.api.FieldId
6+
import org.utbot.framework.plugin.api.MethodId
7+
import org.utbot.framework.plugin.api.util.voidClassId
48

59
/**
610
* For now we will count field accessible if it is not private and its class is also accessible,
711
* because we generate tests in the same package with the class under test,
812
* which means we can access public, protected and package-private fields
913
*
10-
* @param packageName name of the package we check accessibility from
14+
* @param context context in which code is generated (it is needed because the method needs to know package and language)
1115
*/
12-
infix fun FieldId.isAccessibleFrom(packageName: String): Boolean {
16+
// TODO: change parameter from packageName: String to context: CgContext in ClassId.isAccessibleFrom and ExecutableId.isAccessibleFrom ?
17+
internal infix fun FieldId.isAccessibleFrom(context: CgContext): Boolean {
18+
if (context.codegenLanguage == CodegenLanguage.KOTLIN) {
19+
// Here we call field accessible iff its getter is accessible, checks for setter are made in FieldId.canBeSetIn
20+
return declaringClass.allMethods.contains(getter) && getter.isAccessibleFrom(context.testClassPackageName)
21+
}
22+
val packageName = context.testClassPackageName
1323
val isClassAccessible = declaringClass.isAccessibleFrom(packageName)
1424
val isAccessibleByVisibility = isPublic || (declaringClass.packageName == packageName && (isPackagePrivate || isProtected))
1525
val isAccessibleFromPackageByModifiers = isAccessibleByVisibility && !isSynthetic
@@ -20,4 +30,21 @@ infix fun FieldId.isAccessibleFrom(packageName: String): Boolean {
2030
/**
2131
* Whether or not a field can be set without reflection
2232
*/
23-
fun FieldId.canBeSetIn(packageName: String): Boolean = isAccessibleFrom(packageName) && !isFinal
33+
internal fun FieldId.canBeSetIn(context: CgContext): Boolean {
34+
if (context.codegenLanguage == CodegenLanguage.KOTLIN) {
35+
return declaringClass.allMethods.contains(setter) && setter.isAccessibleFrom(context.testClassPackageName)
36+
}
37+
return isAccessibleFrom(context) && !isFinal
38+
}
39+
40+
/**
41+
* The default getter method for field (the one which is generated by Kotlin compiler)
42+
*/
43+
val FieldId.getter: MethodId
44+
get() = MethodId(declaringClass, "get${name.replaceFirstChar { it.uppercase() } }", type, emptyList())
45+
46+
/**
47+
* The default setter method for field (the one which is generated by Kotlin compiler)
48+
*/
49+
val FieldId.setter: MethodId
50+
get() = MethodId(declaringClass, "set${name.replaceFirstChar { it.uppercase() } }", voidClassId, listOf(type))

0 commit comments

Comments
 (0)