Skip to content

Commit 9b2619a

Browse files
authored
Improve isAccessible checks (#1754)
Add more checks for isAccessible fieldId method
1 parent 6aad4be commit 9b2619a

File tree

6 files changed

+30
-35
lines changed

6 files changed

+30
-35
lines changed

utbot-framework/src/main/kotlin/org/utbot/framework/assemble/AssembleModelGenerator.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ class AssembleModelGenerator(private val basePackageName: String) {
463463

464464
return allModificatorsOfClass
465465
.mapNotNull { (fieldId, possibleModificators) ->
466-
chooseModificator(fieldId, possibleModificators)?.let { fieldId to it }
466+
chooseModificator(classId, fieldId, possibleModificators)?.let { fieldId to it }
467467
}
468468
.toMap()
469469
}
@@ -474,12 +474,13 @@ class AssembleModelGenerator(private val basePackageName: String) {
474474
* Note: direct accessor is more preferred than setter.
475475
*/
476476
private fun chooseModificator(
477+
callerClassId: ClassId,
477478
fieldId: FieldId,
478479
settersAndDirectAccessors: Set<StatementId>,
479480
): StatementId? {
480481
val directAccessors = settersAndDirectAccessors
481482
.filterIsInstance<DirectFieldAccessId>()
482-
.filter {it.fieldId.isAccessibleFrom(basePackageName) }
483+
.filter {it.fieldId.isAccessibleFrom(basePackageName, callerClassId) }
483484

484485
if (directAccessors.any()) {
485486
return directAccessors.singleOrNull()

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/access/CgCallableAccessManager.kt

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -323,26 +323,19 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA
323323
return caller canBeReceiverOf this
324324
}
325325

326-
private fun FieldId.accessSuitability(accessor: CgExpression?): FieldAccessorSuitability {
326+
private fun FieldId.accessSuitability(accessor: CgExpression): FieldAccessorSuitability {
327327
// Check field accessibility.
328-
if (!canBeReadFrom(context)) {
328+
if (!canBeReadFrom(context, accessor.type)) {
329329
return ReflectionOnly
330330
}
331331

332-
// If field is static, then it may not have an accessor.
333-
if (this.isStatic && accessor == null) {
334-
return Suitable
335-
}
336-
337-
if (this.isStatic && accessor != null && codegenLanguage == CodegenLanguage.KOTLIN) {
332+
if (this.isStatic && codegenLanguage == CodegenLanguage.KOTLIN) {
338333
error("In Kotlin, unlike Java, static fields cannot be accessed by an object: $this")
339334
}
340335

341336
// if field is declared in the current test class
342337
if (this.declaringClass == currentTestClass) {
343338
return when {
344-
// field of the current class can be accessed without explicit accessor
345-
accessor == null -> Suitable
346339
// field of the current class can be accessed with `this` reference
347340
accessor isThisInstanceOf currentTestClass -> Suitable
348341
// field of the current class can be accessed by the instance of this class
@@ -352,10 +345,6 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA
352345
}
353346
}
354347

355-
requireNotNull(accessor) {
356-
"Field access must have a non-null accessor, unless it is the field of the current test class or a static field: $this"
357-
}
358-
359348
if (this.declaringClass == accessor.type) {
360349
// if the field was declared in class `T`, and the accessor is __exactly__ of this type (not a subtype),
361350
// then we can safely access the field

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/services/access/CgFieldStateManager.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ internal class CgFieldStateManagerImpl(val context: CgContext)
180180
for ((index, fieldPathElement) in path.withIndex()) {
181181
when (fieldPathElement) {
182182
is FieldAccess -> {
183-
if (!fieldPathElement.field.canBeReadFrom(context)) {
183+
if (!fieldPathElement.field.canBeReadFrom(context, curType)) {
184184
lastAccessibleIndex = index - 1
185185
break
186186
}
@@ -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.canBeReadFrom(context)) {
249+
val firstAccessor = if (owner.isAccessibleFrom(testClassPackageName) && firstField.canBeReadFrom(context, owner)) {
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/tree/CgMethodConstructor.kt

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ import org.utbot.framework.plugin.api.util.doubleStreamClassId
150150
import org.utbot.framework.plugin.api.util.doubleStreamToArrayMethodId
151151
import org.utbot.framework.plugin.api.util.intStreamClassId
152152
import org.utbot.framework.plugin.api.util.intStreamToArrayMethodId
153-
import org.utbot.framework.plugin.api.util.isPackagePrivate
154153
import org.utbot.framework.plugin.api.util.isSubtypeOf
155154
import org.utbot.framework.plugin.api.util.longStreamClassId
156155
import org.utbot.framework.plugin.api.util.longStreamToArrayMethodId
@@ -236,7 +235,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
236235
val accessibleStaticFields = statics.accessibleFields()
237236
for ((field, _) in accessibleStaticFields) {
238237
val declaringClass = field.declaringClass
239-
val fieldAccessible = field.canBeReadFrom(context)
238+
val fieldAccessible = field.canBeReadFrom(context, declaringClass)
240239

241240
// prevValue is nullable if not accessible because of getStaticFieldValue(..) : Any?
242241
val prevValue = newVar(
@@ -261,7 +260,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
261260
val accessibleStaticFields = statics.accessibleFields()
262261
for ((field, model) in accessibleStaticFields) {
263262
val declaringClass = field.declaringClass
264-
val fieldAccessible = field.canBeSetFrom(context)
263+
val fieldAccessible = field.canBeSetFrom(context, declaringClass)
265264

266265
val fieldValue = if (isParametrized) {
267266
currentMethodParameters[CgParameterKind.Statics(model)]
@@ -282,7 +281,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
282281

283282
protected fun recoverStaticFields() {
284283
for ((field, prevValue) in prevStaticFieldValues.accessibleFields()) {
285-
if (field.canBeSetFrom(context)) {
284+
if (field.canBeSetFrom(context, field.declaringClass)) {
286285
field.declaringClass[field] `=` prevValue
287286
} else {
288287
val declaringClass = getClassOf(field.declaringClass)
@@ -1125,11 +1124,7 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
11251124
private fun FieldId.getAccessExpression(variable: CgVariable): CgExpression =
11261125
// Can directly access field only if it is declared in variable class (or in its ancestors)
11271126
// and is accessible from current package
1128-
if (variable.type.hasField(this)
1129-
//TODO: think about moving variable type checks into [isAccessibleFrom] after contest
1130-
&& (!isPackagePrivate || variable.type.packageName == context.testClassPackageName)
1131-
&& canBeReadFrom(context)
1132-
) {
1127+
if (variable.type.hasField(this) && canBeReadFrom(context, variable.type)) {
11331128
if (jField.isStatic) CgStaticFieldAccess(this) else CgFieldAccess(variable, this)
11341129
} else {
11351130
utilsClassId[getFieldValue](variable, this.declaringClass.name, this.name)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ open class CgVariableConstructor(val context: CgContext) :
192192
// byteBuffer is field of type ByteBuffer and upper line is incorrect
193193
val canFieldBeDirectlySetByVariableAndFieldTypeRestrictions =
194194
fieldFromVariableSpecifiedType != null && fieldFromVariableSpecifiedType.type.id == variableForField.type
195-
if (canFieldBeDirectlySetByVariableAndFieldTypeRestrictions && fieldId.canBeSetFrom(context)) {
195+
if (canFieldBeDirectlySetByVariableAndFieldTypeRestrictions && fieldId.canBeSetFrom(context, obj.type)) {
196196
// TODO: check if it is correct to use declaringClass of a field here
197197
val fieldAccess = if (field.isStatic) CgStaticFieldAccess(fieldId) else CgFieldAccess(obj, fieldId)
198198
fieldAccess `=` variableForField

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

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

33
import org.utbot.framework.codegen.domain.context.CgContext
4+
import org.utbot.framework.plugin.api.ClassId
45
import org.utbot.framework.plugin.api.CodegenLanguage
56
import org.utbot.framework.plugin.api.FieldId
67
import org.utbot.framework.plugin.api.MethodId
@@ -17,10 +18,19 @@ import org.utbot.framework.plugin.api.util.voidClassId
1718
* which means we can access public, protected and package-private fields
1819
*
1920
* @param context context in which code is generated (it is needed because the method needs to know package and language)
21+
* @param callerClassId object on which we try to access the field
2022
*/
21-
fun FieldId.isAccessibleFrom(packageName: String): Boolean {
23+
fun FieldId.isAccessibleFrom(packageName: String, callerClassId: ClassId): Boolean {
2224
val isClassAccessible = declaringClass.isAccessibleFrom(packageName)
23-
val isAccessibleByVisibility = isPublic || (declaringClass.packageName == packageName && (isPackagePrivate || isProtected))
25+
26+
/*
27+
* There is a corner case which we ignore now.
28+
* Protected fields are accessible in nested classes of inheritors.
29+
*/
30+
val isAccessibleByVisibility =
31+
isPublic ||
32+
isPackagePrivate && callerClassId.packageName == packageName && declaringClass.packageName == packageName ||
33+
isProtected && declaringClass.packageName == packageName
2434
val isAccessibleFromPackageByModifiers = isAccessibleByVisibility && !isSynthetic
2535

2636
return isClassAccessible && isAccessibleFromPackageByModifiers
@@ -32,14 +42,14 @@ private fun FieldId.canBeReadViaGetterFrom(context: CgContext): Boolean =
3242
/**
3343
* Returns whether you can read field's value without reflection
3444
*/
35-
internal infix fun FieldId.canBeReadFrom(context: CgContext): Boolean {
45+
internal fun FieldId.canBeReadFrom(context: CgContext, callerClassId: ClassId): Boolean {
3646
if (context.codegenLanguage == CodegenLanguage.KOTLIN) {
3747
// Kotlin will allow direct field access for non-static fields with accessible getter
3848
if (!isStatic && canBeReadViaGetterFrom(context))
3949
return true
4050
}
4151

42-
return isAccessibleFrom(context.testClassPackageName)
52+
return isAccessibleFrom(context.testClassPackageName, callerClassId)
4353
}
4454

4555
private fun FieldId.canBeSetViaSetterFrom(context: CgContext): Boolean =
@@ -48,16 +58,16 @@ private fun FieldId.canBeSetViaSetterFrom(context: CgContext): Boolean =
4858
/**
4959
* Whether or not a field can be set without reflection
5060
*/
51-
internal fun FieldId.canBeSetFrom(context: CgContext): Boolean {
61+
internal fun FieldId.canBeSetFrom(context: CgContext, callerClassId: ClassId): Boolean {
5262
if (context.codegenLanguage == CodegenLanguage.KOTLIN) {
5363
// Kotlin will allow direct write access if both getter and setter is defined
5464
// !isAccessibleFrom(context) is important here because above rule applies to final fields only if they are not accessible in Java terms
55-
if (!isAccessibleFrom(context.testClassPackageName) && !isStatic && canBeReadViaGetterFrom(context) && canBeSetViaSetterFrom(context)) {
65+
if (!isAccessibleFrom(context.testClassPackageName, callerClassId) && !isStatic && canBeReadViaGetterFrom(context) && canBeSetViaSetterFrom(context)) {
5666
return true
5767
}
5868
}
5969

60-
return isAccessibleFrom(context.testClassPackageName) && !isFinal
70+
return isAccessibleFrom(context.testClassPackageName, callerClassId) && !isFinal
6171
}
6272

6373
/**

0 commit comments

Comments
 (0)