Skip to content

Commit f8c21ed

Browse files
Support direct field accesses in call models (#2261)
1 parent 450f1c6 commit f8c21ed

File tree

7 files changed

+150
-75
lines changed

7 files changed

+150
-75
lines changed

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

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ data class UtAssembleModel private constructor(
502502
override val id: Int?,
503503
override val classId: ClassId,
504504
override val modelName: String,
505-
val instantiationCall: UtExecutableCallModel,
505+
val instantiationCall: UtStatementCallModel,
506506
val modificationsChain: List<UtStatementModel>,
507507
val origin: UtCompositeModel?
508508
) : UtReferenceModel(id, classId, modelName) {
@@ -527,7 +527,7 @@ data class UtAssembleModel private constructor(
527527
id: Int?,
528528
classId: ClassId,
529529
modelName: String,
530-
instantiationCall: UtExecutableCallModel,
530+
instantiationCall: UtStatementCallModel,
531531
origin: UtCompositeModel? = null,
532532
modificationsChainProvider: UtAssembleModel.() -> List<UtStatementModel> = { emptyList() }
533533
) : this(id, classId, modelName, instantiationCall, mutableListOf(), origin) {
@@ -691,28 +691,23 @@ sealed class UtStatementModel(
691691
)
692692

693693
/**
694-
* Step of assemble instruction that calls executable.
695-
*
696-
* Contains executable to call, call parameters and an instance model before call.
697-
*
698-
* @param [instance] **must be** `null` for static methods and constructors
694+
* Step of assemble instruction that calls executable or accesses the field.
699695
*/
700-
data class UtExecutableCallModel(
696+
sealed class UtStatementCallModel(
701697
override val instance: UtReferenceModel?,
702-
val executable: ExecutableId,
703-
val params: List<UtModel>,
704-
) : UtStatementModel(instance) {
698+
open val statement: StatementId,
699+
open val params: List<UtModel>,
700+
): UtStatementModel(instance) {
705701
override fun toString() = withToStringThreadLocalReentrancyGuard {
706702
buildString {
707703

708-
val executableName = when (executable) {
709-
is ConstructorId -> executable.classId.name
710-
is MethodId -> executable.name
704+
val executableName = when (statement) {
705+
is ConstructorId -> statement.classId.name
706+
is DirectFieldAccessId -> statement.name
707+
is MethodId -> statement.name
711708
}
712709

713-
if (instance != null) {
714-
append("${instance.modelName}.")
715-
}
710+
instance?.let { append("${it.modelName}.") }
716711

717712
append(executableName)
718713
val paramsRepresentation = params.map { param ->
@@ -726,6 +721,32 @@ data class UtExecutableCallModel(
726721
}
727722
}
728723

724+
/**
725+
* Step of assemble instruction that calls executable.
726+
*
727+
* Contains executable to call, call parameters and an instance model before call.
728+
* @param [instance] **must be** `null` for static methods and constructors
729+
*/
730+
data class UtExecutableCallModel(
731+
override val instance: UtReferenceModel?,
732+
val executable: ExecutableId,
733+
override val params: List<UtModel>,
734+
) : UtStatementCallModel(instance, executable, params) {
735+
override fun toString(): String = super.toString()
736+
}
737+
738+
/**
739+
* Step of assemble instruction that directly accesses a field.
740+
*
741+
* Contains parameter value to set and an instance model before call.
742+
*/
743+
data class UtDirectGetFieldModel(
744+
override val instance: UtReferenceModel,
745+
val fieldAccess: DirectFieldAccessId,
746+
) : UtStatementCallModel(instance, fieldAccess, emptyList()) {
747+
override fun toString(): String = super.toString()
748+
}
749+
729750
/**
730751
* Step of assemble instruction that sets public field with direct setter.
731752
*

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

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import org.utbot.common.invokeCatching
55
import org.utbot.common.withAccessibility
66
import org.utbot.framework.plugin.api.ClassId
77
import org.utbot.framework.plugin.api.ConstructorId
8+
import org.utbot.framework.plugin.api.DirectFieldAccessId
89
import org.utbot.framework.plugin.api.EnvironmentModels
910
import org.utbot.framework.plugin.api.FieldId
1011
import org.utbot.framework.plugin.api.FieldMockTarget
@@ -20,6 +21,7 @@ import org.utbot.framework.plugin.api.UtClassRefModel
2021
import org.utbot.framework.plugin.api.UtCompositeModel
2122
import org.utbot.framework.plugin.api.UtConcreteValue
2223
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
24+
import org.utbot.framework.plugin.api.UtDirectGetFieldModel
2325
import org.utbot.framework.plugin.api.UtEnumConstantModel
2426
import org.utbot.framework.plugin.api.UtExecutableCallModel
2527
import org.utbot.framework.plugin.api.UtExecution
@@ -32,6 +34,7 @@ import org.utbot.framework.plugin.api.UtModel
3234
import org.utbot.framework.plugin.api.UtNullModel
3335
import org.utbot.framework.plugin.api.UtPrimitiveModel
3436
import org.utbot.framework.plugin.api.UtReferenceModel
37+
import org.utbot.framework.plugin.api.UtStatementCallModel
3538
import org.utbot.framework.plugin.api.UtSymbolicExecution
3639
import org.utbot.framework.plugin.api.UtValueExecution
3740
import org.utbot.framework.plugin.api.UtValueExecutionState
@@ -327,15 +330,15 @@ class ValueConstructor {
327330
constructedObjects[assembleModel]?.let { return it }
328331

329332
val instantiationExecutableCall = assembleModel.instantiationCall
330-
val result = updateWithExecutableCallModel(instantiationExecutableCall)
333+
val result = updateWithStatementCallModel(instantiationExecutableCall)
331334
checkNotNull(result) {
332-
"Tracked instance can't be null for call ${instantiationExecutableCall.executable} in model $assembleModel"
335+
"Tracked instance can't be null for call ${instantiationExecutableCall.statement} in model $assembleModel"
333336
}
334337
constructedObjects[assembleModel] = result
335338

336339
assembleModel.modificationsChain.forEach { statementModel ->
337340
when (statementModel) {
338-
is UtExecutableCallModel -> updateWithExecutableCallModel(statementModel)
341+
is UtStatementCallModel -> updateWithStatementCallModel(statementModel)
339342
is UtDirectSetFieldModel -> updateWithDirectSetFieldModel(statementModel)
340343
}
341344
}
@@ -374,19 +377,25 @@ class ValueConstructor {
374377
*
375378
* @return the result of [callModel] invocation
376379
*/
377-
private fun updateWithExecutableCallModel(
378-
callModel: UtExecutableCallModel,
379-
): Any? {
380-
val executable = callModel.executable
381-
val instanceValue = callModel.instance?.let { value(it) }
382-
val params = callModel.params.map { value(it) }
383-
384-
val result = when (executable) {
385-
is MethodId -> executable.call(params, instanceValue)
386-
is ConstructorId -> executable.call(params)
387-
}
380+
private fun updateWithStatementCallModel(callModel: UtStatementCallModel, ): Any? {
381+
when (callModel) {
382+
is UtExecutableCallModel -> {
383+
val executable = callModel.executable
384+
val instanceValue = callModel.instance?.let { value(it) }
385+
val params = callModel.params.map { value(it) }
386+
387+
return when (executable) {
388+
is MethodId -> executable.call(params, instanceValue)
389+
is ConstructorId -> executable.call(params)
390+
}
391+
}
392+
is UtDirectGetFieldModel -> {
393+
val fieldAccess = callModel.fieldAccess
394+
val instanceValue = value(callModel.instance)
388395

389-
return result
396+
return fieldAccess.get(instanceValue)
397+
}
398+
}
390399
}
391400

392401
/**
@@ -440,6 +449,13 @@ class ValueConstructor {
440449
newInstance(*args.toTypedArray())
441450
}
442451

452+
private fun DirectFieldAccessId.get(instance: Any?): Any {
453+
val field = fieldId.jField
454+
return field.withAccessibility {
455+
field.get(instance)
456+
}
457+
}
458+
443459
/**
444460
* Fetches primitive value from NutsModel to create array of primitives.
445461
*/

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import org.utbot.framework.plugin.api.UtArrayModel
2121
import org.utbot.framework.plugin.api.UtAssembleModel
2222
import org.utbot.framework.plugin.api.UtClassRefModel
2323
import org.utbot.framework.plugin.api.UtCompositeModel
24+
import org.utbot.framework.plugin.api.UtDirectGetFieldModel
2425
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
2526
import org.utbot.framework.plugin.api.UtEnumConstantModel
2627
import org.utbot.framework.plugin.api.UtExecutableCallModel
@@ -30,6 +31,7 @@ import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation
3031
import org.utbot.framework.plugin.api.UtNullModel
3132
import org.utbot.framework.plugin.api.UtPrimitiveModel
3233
import org.utbot.framework.plugin.api.UtReferenceModel
34+
import org.utbot.framework.plugin.api.UtStatementCallModel
3335
import org.utbot.framework.plugin.api.UtStatementModel
3436
import org.utbot.framework.plugin.api.UtStaticMethodInstrumentation
3537
import org.utbot.framework.plugin.api.UtVoidModel
@@ -329,12 +331,11 @@ class AssembleModelGenerator(private val basePackageName: String) {
329331
private fun assembleAssembleModel(modelBefore: UtAssembleModel): UtModel {
330332
instantiatedModels[modelBefore]?.let { return it }
331333

332-
333334
return UtAssembleModel(
334335
modelBefore.id,
335336
modelBefore.classId,
336337
modelBefore.modelName,
337-
assembleExecutableCallModel(modelBefore.instantiationCall),
338+
assembleStatementCallModel(modelBefore.instantiationCall),
338339
modelBefore.origin
339340
) {
340341
instantiatedModels[modelBefore] = this
@@ -346,7 +347,7 @@ class AssembleModelGenerator(private val basePackageName: String) {
346347
* Assembles internal structure of [UtStatementModel].
347348
*/
348349
private fun assembleStatementModel(statementModel: UtStatementModel): UtStatementModel = when (statementModel) {
349-
is UtExecutableCallModel -> assembleExecutableCallModel(statementModel)
350+
is UtStatementCallModel -> assembleStatementCallModel(statementModel)
350351
is UtDirectSetFieldModel -> assembleDirectSetFieldModel(statementModel)
351352
}
352353

@@ -356,11 +357,16 @@ class AssembleModelGenerator(private val basePackageName: String) {
356357
fieldModel = assembleModel(statementModel.fieldModel)
357358
)
358359

359-
private fun assembleExecutableCallModel(statementModel: UtExecutableCallModel) =
360-
statementModel.copy(
361-
instance = statementModel.instance?.let { assembleModel(it) as UtReferenceModel },
362-
params = statementModel.params.map { assembleModel(it) }
363-
)
360+
private fun assembleStatementCallModel(statementModel: UtStatementCallModel) =
361+
when (statementModel) {
362+
is UtExecutableCallModel-> statementModel.copy(
363+
instance = statementModel.instance?.let { assembleModel(it) as UtReferenceModel },
364+
params = statementModel.params.map { assembleModel(it) }
365+
)
366+
is UtDirectGetFieldModel -> statementModel.copy(
367+
instance = assembleModel(statementModel.instance) as UtReferenceModel
368+
)
369+
}
364370

365371
/**
366372
* Assembles internal structure of [UtCompositeModel] if it represents a mock.

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import org.utbot.framework.plugin.api.UtLambdaModel
1616
import org.utbot.framework.plugin.api.UtModel
1717
import org.utbot.framework.plugin.api.UtNullModel
1818
import org.utbot.framework.plugin.api.UtPrimitiveModel
19+
import org.utbot.framework.plugin.api.UtStatementCallModel
1920
import org.utbot.framework.plugin.api.UtVoidModel
2021
import org.utbot.framework.plugin.api.isMockModel
2122

@@ -119,7 +120,7 @@ class SpringTestClassModelBuilder(val context: CgContext): TestClassModelBuilder
119120
currentModel.modificationsChain.forEach { stmt ->
120121
stmt.instance?.let { collectRecursively(it, allModels) }
121122
when (stmt) {
122-
is UtExecutableCallModel -> stmt.params.forEach { collectRecursively(it, allModels) }
123+
is UtStatementCallModel -> stmt.params.forEach { collectRecursively(it, allModels) }
123124
is UtDirectSetFieldModel -> collectRecursively(stmt.fieldModel, allModels)
124125
}
125126
}

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

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,13 @@ import org.utbot.framework.plugin.api.BuiltinClassId
3737
import org.utbot.framework.plugin.api.ClassId
3838
import org.utbot.framework.plugin.api.CodegenLanguage
3939
import org.utbot.framework.plugin.api.ConstructorId
40+
import org.utbot.framework.plugin.api.DirectFieldAccessId
4041
import org.utbot.framework.plugin.api.MethodId
4142
import org.utbot.framework.plugin.api.UtArrayModel
4243
import org.utbot.framework.plugin.api.UtAssembleModel
4344
import org.utbot.framework.plugin.api.UtClassRefModel
4445
import org.utbot.framework.plugin.api.UtCompositeModel
46+
import org.utbot.framework.plugin.api.UtDirectGetFieldModel
4547
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
4648
import org.utbot.framework.plugin.api.UtEnumConstantModel
4749
import org.utbot.framework.plugin.api.UtExecutableCallModel
@@ -50,6 +52,7 @@ import org.utbot.framework.plugin.api.UtModel
5052
import org.utbot.framework.plugin.api.UtNullModel
5153
import org.utbot.framework.plugin.api.UtPrimitiveModel
5254
import org.utbot.framework.plugin.api.UtReferenceModel
55+
import org.utbot.framework.plugin.api.UtStatementCallModel
5356
import org.utbot.framework.plugin.api.UtVoidModel
5457
import org.utbot.framework.plugin.api.util.classClassId
5558
import org.utbot.framework.plugin.api.util.defaultValueModel
@@ -215,7 +218,7 @@ open class CgVariableConstructor(val context: CgContext) :
215218
// fields here are supposed to be accessible, so we assign them directly without any checks
216219
instance[statementModel.fieldId] `=` declareOrGet(statementModel.fieldModel)
217220
}
218-
is UtExecutableCallModel -> {
221+
is UtStatementCallModel -> {
219222
val call = createCgExecutableCallFromUtExecutableCall(statementModel)
220223
val equivalentFieldAccess = replaceCgExecutableCallWithFieldAccessIfNeeded(call)
221224
if (equivalentFieldAccess != null)
@@ -231,21 +234,22 @@ open class CgVariableConstructor(val context: CgContext) :
231234

232235
private fun processInstantiationStatement(
233236
model: UtAssembleModel,
234-
executableCall: UtExecutableCallModel,
237+
statementCall: UtStatementCallModel,
235238
baseName: String?
236239
) {
237-
val executable = executableCall.executable
238-
val params = executableCall.params
240+
val executable = statementCall.statement
241+
val params = statementCall.params
239242

240243
val type = when (executable) {
241244
is MethodId -> executable.returnType
245+
is DirectFieldAccessId -> executable.fieldId.type
242246
is ConstructorId -> executable.classId
243247
}
244248
// Don't use redundant constructors for primitives and String
245249
val initExpr = if (isPrimitiveWrapperOrString(type)) {
246250
cgLiteralForWrapper(params)
247251
} else {
248-
createCgExecutableCallFromUtExecutableCall(executableCall)
252+
createCgExecutableCallFromUtExecutableCall(statementCall)
249253
}
250254
newVar(type, model, baseName) {
251255
initExpr
@@ -255,22 +259,31 @@ open class CgVariableConstructor(val context: CgContext) :
255259
}
256260

257261

258-
private fun createCgExecutableCallFromUtExecutableCall(statementModel: UtExecutableCallModel): CgExecutableCall {
259-
val executable = statementModel.executable
260-
val params = statementModel.params
261-
val cgCall = when (executable) {
262-
is MethodId -> {
263-
val caller = statementModel.instance?.let { declareOrGet(it) }
264-
val args = params.map { declareOrGet(it) }
265-
caller[executable](*args.toTypedArray())
262+
private fun createCgExecutableCallFromUtExecutableCall(statementModel: UtStatementCallModel): CgExecutableCall =
263+
when (statementModel) {
264+
is UtExecutableCallModel -> {
265+
val executable = statementModel.executable
266+
val params = statementModel.params
267+
268+
when (executable) {
269+
is MethodId -> {
270+
val caller = statementModel.instance?.let { declareOrGet(it) }
271+
val args = params.map { declareOrGet(it) }
272+
caller[executable](*args.toTypedArray())
273+
}
274+
275+
is ConstructorId -> {
276+
val args = params.map { declareOrGet(it) }
277+
executable(*args.toTypedArray())
278+
}
279+
}
266280
}
267-
is ConstructorId -> {
268-
val args = params.map { declareOrGet(it) }
269-
executable(*args.toTypedArray())
281+
is UtDirectGetFieldModel -> {
282+
val instance = declareOrGet(statementModel.instance)
283+
val fieldAccess = statementModel.fieldAccess
284+
utilsClassId[getFieldValue](instance, fieldAccess.fieldId.declaringClass.canonicalName, fieldAccess.fieldId.name)
270285
}
271286
}
272-
return cgCall
273-
}
274287

275288
/**
276289
* If executable is getter/setter that should be syntactically replaced with field access

utbot-framework/src/main/kotlin/org/utbot/framework/minimization/Minimization.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import org.utbot.framework.plugin.api.UtLambdaModel
1717
import org.utbot.framework.plugin.api.UtModel
1818
import org.utbot.framework.plugin.api.UtNullModel
1919
import org.utbot.framework.plugin.api.UtPrimitiveModel
20+
import org.utbot.framework.plugin.api.UtStatementCallModel
2021
import org.utbot.framework.plugin.api.UtStatementModel
2122
import org.utbot.framework.plugin.api.UtSymbolicExecution
2223
import org.utbot.framework.plugin.api.UtVoidModel
@@ -242,7 +243,7 @@ private fun UtModel.calculateSize(used: MutableSet<UtModel> = mutableSetOf()): I
242243
private fun UtStatementModel.calculateSize(used: MutableSet<UtModel> = mutableSetOf()): Int =
243244
when (this) {
244245
is UtDirectSetFieldModel -> 1 + fieldModel.calculateSize(used)
245-
is UtExecutableCallModel -> 1 + params.sumOf { it.calculateSize(used) }
246+
is UtStatementCallModel -> 1 + params.sumOf { it.calculateSize(used) }
246247
}
247248

248249
/**

0 commit comments

Comments
 (0)