diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt index afcf35be57..11eeefdd95 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/Domain.kt @@ -12,7 +12,6 @@ import org.utbot.framework.plugin.api.CodeGenerationSettingItem import org.utbot.framework.plugin.api.MethodId import org.utbot.framework.plugin.api.TypeParameters import org.utbot.framework.plugin.api.UtModel -import org.utbot.framework.plugin.api.idOrNull import org.utbot.framework.plugin.api.isolateCommandLineArgumentsToArgumentFile import org.utbot.framework.plugin.api.util.booleanArrayClassId import org.utbot.framework.plugin.api.util.booleanClassId @@ -730,18 +729,13 @@ object SpringBoot : DependencyInjectionFramework( ) /** - * Extended id of [UtModel], unique for whole test set. + * Extended [UtModel] model with testSet id and execution id. * - * Allows distinguishing models from different executions and test sets, - * even if they have the same value of `UtModel.id` that is allowed. + * Used as a key in [valueByUtModelWrapper]. + * Was introduced primarily for shared among all test methods global variables. */ -data class ModelId private constructor( - private val id: Int?, - private val executionId: Int, - private val testSetId: Int, -) { - companion object { - fun create(model: UtModel, executionId: Int = -1, testSetId: Int = -1) = ModelId(model.idOrNull(), executionId, testSetId) - } -} - +data class UtModelWrapper( + val testSetId: Int, + val executionId: Int, + val model: UtModel +) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt index 18c54e64ec..c4e4784eaa 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/context/CgContext.kt @@ -15,14 +15,13 @@ import org.utbot.framework.codegen.domain.models.CgTestMethod import org.utbot.framework.codegen.domain.models.CgThisInstance import org.utbot.framework.codegen.domain.models.CgValue import org.utbot.framework.codegen.domain.models.CgVariable -import java.util.IdentityHashMap import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.PersistentMap import kotlinx.collections.immutable.PersistentSet import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.persistentSetOf -import org.utbot.framework.codegen.domain.ModelId +import org.utbot.framework.codegen.domain.UtModelWrapper import org.utbot.framework.codegen.domain.ProjectType import org.utbot.framework.codegen.domain.models.CgMethodTestSet import org.utbot.framework.codegen.domain.builtin.TestClassUtilMethodProvider @@ -195,10 +194,30 @@ interface CgContextOwner { val shouldOptimizeImports: Boolean - var valueByModel: IdentityHashMap + /** + * Used for differentiating models in codegen + * because comparisons by [UtModel] are not enough, + * especially for Spring related variable processing. + * + * Default value is -1, meaning that we are not processing any test set. + * + * @see [CgContext.withTestSetIdScope] + */ + var currentTestSetId: Int + + /** + * Used for differentiating models in codegen + * because comparisons by [UtModel] are not enough, + * especially for Spring related variable processing. + * + * Default value is -1, meaning that we are not processing any execution. + * + * @see [CgContext.withExecutionIdScope] + */ + var currentExecutionId: Int - // use it to compare stateBefore and result variables - in case of equality do not create new variable - var valueByModelId: MutableMap + // used for comparing stateBefore and result variables -- in case of equality do not create new variable + var valueByUtModelWrapper: MutableMap // parameters of the method currently being generated val currentMethodParameters: MutableMap @@ -227,12 +246,6 @@ interface CgContextOwner { */ var successfulExecutionsModels: List - /** - * Gives a unique identifier to model in test set. - * Determines which execution current model belongs to. - */ - var modelIds: MutableMap - fun block(init: () -> Unit): Block { val prevBlock = currentBlock return try { @@ -260,6 +273,16 @@ interface CgContextOwner { currentExecutable = executableId } + fun withTestSetIdScope(testSetId: Int, block: () -> R): R { + currentTestSetId = testSetId + return block() + } + + fun withExecutionIdScope(executionId: Int, block: () -> R): R { + currentExecutionId = executionId + return block() + } + fun addExceptionIfNeeded(exception: ClassId) { if (exception !is BuiltinClassId) { require(exception isSubtypeOf Throwable::class.id) { @@ -316,11 +339,10 @@ interface CgContextOwner { fun updateVariableScope(variable: CgVariable, model: UtModel? = null) { model?.let { - valueByModel[it] = variable + valueByUtModelWrapper[it.wrap()] = variable (model as UtReferenceModel).let { refModel -> refModel.id.let { - val modelId = getIdByModel(model) - valueByModelId[modelId] = variable + valueByUtModelWrapper[model.wrap()] = variable } } } @@ -331,8 +353,7 @@ interface CgContextOwner { val prevDeclaredClassRefs = declaredClassRefs val prevDeclaredExecutableRefs = declaredExecutableRefs val prevDeclaredFieldRefs = declaredFieldRefs - val prevValueByModel = IdentityHashMap(valueByModel) - val prevValueByModelId = valueByModelId.toMutableMap() + val prevValueByCgModel = valueByUtModelWrapper.toMutableMap() return try { block() } finally { @@ -340,8 +361,7 @@ interface CgContextOwner { declaredClassRefs = prevDeclaredClassRefs declaredExecutableRefs = prevDeclaredExecutableRefs declaredFieldRefs = prevDeclaredFieldRefs - valueByModel = prevValueByModel - valueByModelId = prevValueByModelId + valueByUtModelWrapper = prevValueByCgModel } } @@ -439,7 +459,7 @@ interface CgContextOwner { val getLambdaMethod: MethodId get() = utilMethodProvider.getLambdaMethodMethodId - fun getIdByModel(model: UtModel): ModelId + fun UtModel.wrap(): UtModelWrapper } /** @@ -491,8 +511,6 @@ class CgContext( override lateinit var actual: CgVariable override lateinit var successfulExecutionsModels: List - override var modelIds: MutableMap = mutableMapOf() - /** * This property cannot be accessed outside of test class file scope * (i.e. outside of [CgContextOwner.withTestClassFileScope]). @@ -570,7 +588,12 @@ class CgContext( } } - override fun getIdByModel(model: UtModel): ModelId = modelIds.getOrPut(model) { ModelId.create(model) } + override fun UtModel.wrap(): UtModelWrapper = + UtModelWrapper( + testSetId = currentTestSetId, + executionId = currentExecutionId, + model = this + ) private fun createClassIdForNestedClass(testClassModel: SimpleTestClassModel): ClassId { val simpleName = "${testClassModel.classUnderTest.simpleName}Test" @@ -588,15 +611,15 @@ class CgContext( importedClasses.clear() testMethods.clear() requiredUtilMethods.clear() - valueByModel.clear() - valueByModelId.clear() - modelIds.clear() + valueByUtModelWrapper.clear() mockFrameworkUsed = false } - override var valueByModel: IdentityHashMap = IdentityHashMap() + override var currentTestSetId: Int = -1 + + override var currentExecutionId: Int = -1 - override var valueByModelId: MutableMap = mutableMapOf() + override var valueByUtModelWrapper: MutableMap = mutableMapOf() override val currentMethodParameters: MutableMap = mutableMapOf() diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/TestClassModel.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/TestClassModel.kt index c7b2e54792..adc2cb074d 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/TestClassModel.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/TestClassModel.kt @@ -1,9 +1,7 @@ package org.utbot.framework.codegen.domain.models +import org.utbot.framework.codegen.domain.UtModelWrapper import org.utbot.framework.plugin.api.ClassId -import org.utbot.framework.plugin.api.UtModel - -typealias ClassModels = Map> /** * Stores method test sets in a structure that replicates structure of their methods in [classUnderTest]. @@ -32,7 +30,7 @@ class SpringTestClassModel( classUnderTest: ClassId, methodTestSets: List, nestedClasses: List, - val injectedMockModels: ClassModels = mapOf(), - val mockedModels: ClassModels = mapOf(), + val injectedMockModels: Map> = mapOf(), + val mockedModels: Map> = mapOf(), ): TestClassModel(classUnderTest, methodTestSets, nestedClasses) diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt index c1cd535eed..2178ecab5e 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/domain/models/builders/SpringTestClassModelBuilder.kt @@ -1,9 +1,8 @@ package org.utbot.framework.codegen.domain.models.builders -import org.utbot.framework.codegen.domain.ModelId +import org.utbot.framework.codegen.domain.UtModelWrapper import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.domain.models.CgMethodTestSet -import org.utbot.framework.codegen.domain.models.ClassModels import org.utbot.framework.codegen.domain.models.SpringTestClassModel import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtArrayModel @@ -27,61 +26,64 @@ class SpringTestClassModelBuilder(val context: CgContext): TestClassModelBuilder val (injectedModels, mockedModels) = collectInjectedAndMockedModels(testSets) return SpringTestClassModel( - baseModel.classUnderTest, - baseModel.methodTestSets, - baseModel.nestedClasses, - injectedModels, - mockedModels, + classUnderTest = baseModel.classUnderTest, + methodTestSets = baseModel.methodTestSets, + nestedClasses = baseModel.nestedClasses, + injectedMockModels = injectedModels, + mockedModels = mockedModels ) } - private fun collectInjectedAndMockedModels(testSets: List): Pair { - val thisInstances = mutableSetOf() - val thisInstancesDependentModels = mutableSetOf() - - for ((testSetIndex, testSet) in testSets.withIndex()) { - for ((executionIndex, execution) in testSet.executions.withIndex()) { - - setOf(execution.stateBefore.thisInstance, execution.stateAfter.thisInstance) - .filterNotNull() - .forEach { model -> - thisInstances += model - thisInstancesDependentModels += collectByThisInstanceModel(model, executionIndex, testSetIndex) + private fun collectInjectedAndMockedModels(testSets: List): Pair>, Map>> { + val thisInstances = mutableSetOf() + val thisInstancesDependentModels = mutableSetOf() + + with(context) { + for ((testSetIndex, testSet) in testSets.withIndex()) { + withTestSetIdScope(testSetIndex) { + for ((executionIndex, execution) in testSet.executions.withIndex()) { + withExecutionIdScope(executionIndex) { + setOf(execution.stateBefore.thisInstance, execution.stateAfter.thisInstance) + .filterNotNull() + .forEach { model -> + thisInstances += model.wrap() + thisInstancesDependentModels += collectByThisInstanceModel(model) + } + } } + } } } val dependentMockModels = - thisInstancesDependentModels.filterTo(mutableSetOf()) { it.isMockModel() && it !in thisInstances } + thisInstancesDependentModels + .filterTo(mutableSetOf()) { cgModel -> + cgModel.model.isMockModel() && cgModel !in thisInstances + } return thisInstances.groupByClassId() to dependentMockModels.groupByClassId() } - private fun collectByThisInstanceModel(model: UtModel, executionIndex: Int, testSetIndex: Int): Set { - context.modelIds[model] = ModelId.create(model, executionIndex, testSetIndex) - - val dependentModels = mutableSetOf() + private fun collectByThisInstanceModel(model: UtModel): Set { + val dependentModels = mutableSetOf() collectRecursively(model, dependentModels) - dependentModels.forEach { model -> - context.modelIds[model] = ModelId.create(model, executionIndex, testSetIndex) - } - return dependentModels } - private fun Set.groupByClassId(): ClassModels { - val classModels = mutableMapOf>() + private fun Set.groupByClassId(): Map> { + val classModels = mutableMapOf>() - for (modelGroup in this.groupBy { it.classId }) { + for (modelGroup in this.groupBy { it.model.classId }) { classModels[modelGroup.key] = modelGroup.value.toSet() } return classModels } - private fun collectRecursively(currentModel: UtModel, allModels: MutableSet) { - if (!allModels.add(currentModel)) { + private fun collectRecursively(currentModel: UtModel, allModels: MutableSet) { + val cgModel = with(context) { currentModel.wrap() } + if (!allModels.add(cgModel)) { return } diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractTestClassConstructor.kt index ca1c65540c..ae4ebe2828 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgAbstractTestClassConstructor.kt @@ -98,7 +98,9 @@ abstract class CgAbstractTestClassConstructor(val context: C } for (i in checkedRange) { - currentTestCaseTestMethods += methodConstructor.createTestMethod(methodUnderTest, testSet.executions[i]) + withExecutionIdScope(i) { + currentTestCaseTestMethods += methodConstructor.createTestMethod(methodUnderTest, testSet.executions[i]) + } } val comments = listOf("Actual number of generated tests (${executionIndices.last - executionIndices.first}) exceeds per-method limit (${UtSettings.maxTestsPerMethodInRegion})", diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt index 9931b21c7d..4903101249 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSimpleTestClassConstructor.kt @@ -49,14 +49,16 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass } } - for (testSet in notYetConstructedTestSets) { + for ((testSetIndex, testSet) in notYetConstructedTestSets.withIndex()) { updateCurrentExecutable(testSet.executableId) - val currentMethodUnderTestRegions = constructTestSet(testSet) ?: continue - val executableUnderTestCluster = CgMethodsCluster( - "Test suites for executable $currentExecutable", - currentMethodUnderTestRegions - ) - methodRegions += executableUnderTestCluster + withTestSetIdScope(testSetIndex) { + val currentMethodUnderTestRegions = constructTestSet(testSet) ?: return@withTestSetIdScope + val executableUnderTestCluster = CgMethodsCluster( + "Test suites for executable $currentExecutable", + currentMethodUnderTestRegions + ) + methodRegions += executableUnderTestCluster + } } val currentTestClassDataProviderMethods = currentTestClassContext.cgDataProviderMethods @@ -159,8 +161,11 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass testSet.executions .filterIsInstance() - .forEach { execution -> - testMethods += methodConstructor.createTestMethod(testSet.executableId, execution) + .withIndex() + .forEach { (index, execution) -> + withExecutionIdScope(index) { + testMethods += methodConstructor.createTestMethod(testSet.executableId, execution) + } } return testMethods @@ -176,8 +181,11 @@ open class CgSimpleTestClassConstructor(context: CgContext): CgAbstractTestClass testSet.executions .filterIsInstance() .filter { it.containsMocking } - .forEach { execution -> - testMethods += methodConstructor.createTestMethod(testSet.executableId, execution) + .withIndex() + .forEach { (index, execution) -> + withExecutionIdScope(index) { + testMethods += methodConstructor.createTestMethod(testSet.executableId, execution) + } } return testMethods diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringTestClassConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringTestClassConstructor.kt index 3604f71261..58381d8fc3 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringTestClassConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringTestClassConstructor.kt @@ -1,5 +1,6 @@ package org.utbot.framework.codegen.tree +import org.utbot.framework.codegen.domain.UtModelWrapper import org.utbot.framework.codegen.domain.builtin.TestClassUtilMethodProvider import org.utbot.framework.codegen.domain.builtin.closeMethodId import org.utbot.framework.codegen.domain.builtin.injectMocksClassId @@ -20,7 +21,6 @@ import org.utbot.framework.codegen.domain.models.CgSimpleRegion import org.utbot.framework.codegen.domain.models.CgStatementExecutableCall import org.utbot.framework.codegen.domain.models.CgStaticsRegion import org.utbot.framework.codegen.domain.models.CgVariable -import org.utbot.framework.codegen.domain.models.ClassModels import org.utbot.framework.codegen.domain.models.SpringTestClassModel import org.utbot.framework.plugin.api.ClassId import org.utbot.framework.plugin.api.UtCompositeModel @@ -51,14 +51,16 @@ class CgSpringTestClassConstructor(context: CgContext): CgAbstractTestClassConst methodRegions += closeableMethods } - for (testSet in testClassModel.methodTestSets) { + for ((testSetIndex, testSet) in testClassModel.methodTestSets.withIndex()) { updateCurrentExecutable(testSet.executableId) - val currentMethodUnderTestRegions = constructTestSet(testSet) ?: continue - val executableUnderTestCluster = CgMethodsCluster( - "Test suites for executable $currentExecutable", - currentMethodUnderTestRegions - ) - methodRegions += executableUnderTestCluster + withTestSetIdScope(testSetIndex) { + val currentMethodUnderTestRegions = constructTestSet(testSet) ?: return@withTestSetIdScope + val executableUnderTestCluster = CgMethodsCluster( + "Test suites for executable $currentExecutable", + currentMethodUnderTestRegions + ) + methodRegions += executableUnderTestCluster + } } if (currentTestClass == outerMostTestClass) { @@ -91,7 +93,7 @@ class CgSpringTestClassConstructor(context: CgContext): CgAbstractTestClassConst } private fun constructClassFields( - groupedModelsByClassId: ClassModels, + groupedModelsByClassId: Map>, annotationClassId: ClassId ): MutableList { require(annotationClassId == injectMocksClassId || annotationClassId == mockClassId) { @@ -103,15 +105,19 @@ class CgSpringTestClassConstructor(context: CgContext): CgAbstractTestClassConst val constructedDeclarations = mutableListOf() for ((classId, listOfUtModels) in groupedModelsByClassId) { val model = listOfUtModels.firstOrNull() ?: continue - val createdVariable = variableConstructor.getOrCreateVariable(model) as? CgVariable + val createdVariable = variableConstructor.getOrCreateVariable(model.model) as? CgVariable ?: error("`UtCompositeModel` model was expected, but $model was found") val declaration = CgDeclaration(classId, variableName = createdVariable.name, initializer = null) constructedDeclarations += CgFieldDeclaration(ownerClassId = currentTestClass, declaration, annotation) + listOfUtModels.forEach { key -> + valueByUtModelWrapper[key] = createdVariable + } + when (annotationClassId) { - injectMocksClassId -> variableConstructor.injectedMocksModelsVariables += listOfUtModels to createdVariable - mockClassId -> variableConstructor.mockedModelsVariables += listOfUtModels to createdVariable + injectMocksClassId -> variableConstructor.injectedMocksModelsVariables += listOfUtModels + mockClassId -> variableConstructor.mockedModelsVariables += listOfUtModels } } @@ -133,11 +139,11 @@ class CgSpringTestClassConstructor(context: CgContext): CgAbstractTestClassConst listOf( variableConstructor.mockedModelsVariables, variableConstructor.injectedMocksModelsVariables - ).flatMap { modelSet -> modelSet.keys.map { models -> context.getIdByModel(models.first()) } } + ).flatten() - valueByModelId + valueByUtModelWrapper .filter { it.key !in whiteListOfModels } - .forEach { valueByModelId.remove(it.key) } + .forEach { valueByUtModelWrapper.remove(it.key) } } private fun constructMockitoCloseables(): Pair { diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt index d96ce453b2..f0497328fc 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgSpringVariableConstructor.kt @@ -1,5 +1,6 @@ package org.utbot.framework.codegen.tree +import org.utbot.framework.codegen.domain.UtModelWrapper import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.domain.models.CgValue import org.utbot.framework.codegen.domain.models.CgVariable @@ -9,8 +10,8 @@ import org.utbot.framework.plugin.api.UtModel import org.utbot.framework.plugin.api.isMockModel class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(context) { - val injectedMocksModelsVariables: MutableMap, CgValue> = mutableMapOf() - val mockedModelsVariables: MutableMap, CgValue> = mutableMapOf() + val injectedMocksModelsVariables: MutableSet = mutableSetOf() + val mockedModelsVariables: MutableSet = mutableSetOf() override fun getOrCreateVariable(model: UtModel, name: String?): CgValue { val alreadyCreatedInjectMocks = findCgValueByModel(model, injectedMocksModelsVariables) @@ -41,13 +42,8 @@ class CgSpringVariableConstructor(context: CgContext) : CgVariableConstructor(co return super.getOrCreateVariable(model, name) } - private fun findCgValueByModel(modelToFind: UtModel, modelToValueMap: Map, CgValue>): CgValue? = - // Here we really need to compare models by reference. - // Standard equals is not appropriate because two models from different execution may have same `id`. - // Equals on `ModelId` is not appropriate because injected items from different execution have same `executionId`. - modelToValueMap - .filter { models -> models.key.any { it === modelToFind } } - .entries - .singleOrNull() - ?.value + private fun findCgValueByModel(model: UtModel, setOfModels: Set): CgValue? { + val key = setOfModels.find { it == model.wrap() } + return valueByUtModelWrapper[key] + } } \ No newline at end of file diff --git a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt index 970c951cfb..392fbe900f 100644 --- a/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt +++ b/utbot-framework/src/main/kotlin/org/utbot/framework/codegen/tree/CgVariableConstructor.kt @@ -1,7 +1,6 @@ package org.utbot.framework.codegen.tree import org.utbot.common.isStatic -import org.utbot.framework.codegen.domain.ModelId import org.utbot.framework.codegen.domain.builtin.forName import org.utbot.framework.codegen.domain.builtin.setArrayElement import org.utbot.framework.codegen.domain.context.CgContext @@ -100,28 +99,27 @@ open class CgVariableConstructor(val context: CgContext) : * We use [valueByModelId] for [UtReferenceModel] by id to not create new variable in case state before * was not transformed. */ - open fun getOrCreateVariable(model: UtModel, name: String? = null): CgValue { + open fun getOrCreateVariable(model: UtModel, name: String? = null): CgValue = + valueByUtModelWrapper.getOrPut(model.wrap()) { + constructValueByModel(model, name) + } + + private fun constructValueByModel(model: UtModel, name: String?): CgValue { // name could be taken from existing names, or be specified manually, or be created from generator val baseName = name ?: nameGenerator.nameFrom(model.classId) - val modelId: ModelId = context.getIdByModel(model) - - return if (model is UtReferenceModel) valueByModelId.getOrPut(modelId) { - when (model) { - is UtCompositeModel -> constructComposite(model, baseName) - is UtAssembleModel -> constructAssemble(model, baseName) - is UtArrayModel -> constructArray(model, baseName) - is UtEnumConstantModel -> constructEnumConstant(model, baseName) - is UtClassRefModel -> constructClassRef(model, baseName) - is UtLambdaModel -> constructLambda(model, baseName) - } - } else valueByModel.getOrPut(model) { - when (model) { - is UtNullModel -> nullLiteral() - is UtPrimitiveModel -> CgLiteral(model.classId, model.value) - is UtReferenceModel -> error("Unexpected UtReferenceModel: ${model::class}") - is UtVoidModel -> error("Unexpected UtVoidModel: ${model::class}") - else -> error("Unexpected UtModel: ${model::class}") - } + + return when (model) { + is UtCompositeModel -> constructComposite(model, baseName) + is UtAssembleModel -> constructAssemble(model, baseName) + is UtArrayModel -> constructArray(model, baseName) + is UtEnumConstantModel -> constructEnumConstant(model, baseName) + is UtClassRefModel -> constructClassRef(model, baseName) + is UtLambdaModel -> constructLambda(model, baseName) + is UtNullModel -> nullLiteral() + is UtPrimitiveModel -> CgLiteral(model.classId, model.value) + is UtReferenceModel -> error("Unexpected UtReferenceModel: ${model::class}") + is UtVoidModel -> error("Unexpected UtVoidModel: ${model::class}") + else -> error("Unexpected UtModel: ${model::class}") } } @@ -175,8 +173,7 @@ open class CgVariableConstructor(val context: CgContext) : newVar(variableType, baseName) { utilsClassId[createInstance](model.classId.name) } } - val modelId = context.getIdByModel(model) - valueByModelId[modelId] = obj + valueByUtModelWrapper[model.wrap()] = obj require(obj.type !is BuiltinClassId) { "Unexpected BuiltinClassId ${obj.type} found while constructing from composite model" @@ -229,8 +226,7 @@ open class CgVariableConstructor(val context: CgContext) : } } - val modelId = context.getIdByModel(model) - return valueByModelId.getValue(modelId) + return valueByUtModelWrapper.getValue(model.wrap()) } private fun processInstantiationStatement( @@ -254,8 +250,7 @@ open class CgVariableConstructor(val context: CgContext) : newVar(type, model, baseName) { initExpr }.also { - val modelId = context.getIdByModel(model) - valueByModelId[modelId] = it + valueByUtModelWrapper[model.wrap()] = it } } @@ -353,8 +348,8 @@ open class CgVariableConstructor(val context: CgContext) : } val array = newVar(arrayModel.classId, baseName) { initializer } - val arrayModelId = context.getIdByModel(arrayModel) - valueByModelId[arrayModelId] = array + + valueByUtModelWrapper[arrayModel.wrap()] = array if (canInitWithValues) { return array @@ -483,7 +478,8 @@ open class CgVariableConstructor(val context: CgContext) : * Either declares a new variable or gets it from context's cache * Returns the obtained variable */ - private fun declareOrGet(model: UtModel): CgValue = valueByModel[model] ?: getOrCreateVariable(model) + private fun declareOrGet(model: UtModel): CgValue = + valueByUtModelWrapper[model.wrap()] ?: getOrCreateVariable(model) private fun basicForLoop(start: Any, until: Any, body: (i: CgExpression) -> Unit) { forLoop { diff --git a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgVariableConstructor.kt b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgVariableConstructor.kt index 9a58b32241..328ff26f0f 100644 --- a/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgVariableConstructor.kt +++ b/utbot-js/src/main/kotlin/framework/codegen/model/constructor/tree/JsCgVariableConstructor.kt @@ -9,7 +9,6 @@ import framework.api.js.util.jsDoubleClassId import framework.api.js.util.jsNumberClassId import framework.api.js.util.jsStringClassId import framework.api.js.util.jsUndefinedClassId -import org.utbot.framework.codegen.domain.ModelId import org.utbot.framework.codegen.domain.context.CgContext import org.utbot.framework.codegen.domain.models.CgAllocateArray import org.utbot.framework.codegen.domain.models.CgArrayInitializer @@ -38,22 +37,24 @@ import kotlin.collections.set class JsCgVariableConstructor(ctx: CgContext) : CgVariableConstructor(ctx) { private val nameGenerator = CgComponents.getNameGeneratorBy(context) - - override fun getOrCreateVariable(model: UtModel, name: String?): CgValue { - val modelId: ModelId = context.getIdByModel(model) - - return if (model is UtAssembleModel) valueByModelId.getOrPut(modelId) { - // TODO SEVERE: May lead to unexpected behavior in case of changes to the original method - super.getOrCreateVariable(model, name) - } else valueByModel.getOrPut(model) { - val baseName = name ?: nameGenerator.nameFrom(model.classId) + + override fun getOrCreateVariable(model: UtModel, name: String?): CgValue = + valueByUtModelWrapper.getOrPut(model.wrap()) { when (model) { + is UtAssembleModel -> { + // TODO SEVERE: May lead to unexpected behavior in case of changes to the original method + super.getOrCreateVariable(model, name) + } + is JsPrimitiveModel -> CgLiteral(model.classId, model.value) - is UtArrayModel -> constructArray(model, baseName) + is UtArrayModel -> { + val baseName = name ?: nameGenerator.nameFrom(model.classId) + constructArray(model, baseName) + } + else -> nullLiteral() } } - } private val MAX_ARRAY_INITIALIZER_SIZE = 10 @@ -131,9 +132,7 @@ class JsCgVariableConstructor(ctx: CgContext) : CgVariableConstructor(ctx) { } val array = newVar(arrayModel.classId, baseName) { initializer } - val arrayModelId = context.getIdByModel(arrayModel) - - valueByModelId[arrayModelId] = array + valueByUtModelWrapper[arrayModel.wrap()] = array if (canInitWithValues) { return array diff --git a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt index 5c74548563..265a531d97 100644 --- a/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt +++ b/utbot-python/src/main/kotlin/org/utbot/python/framework/codegen/model/constructor/tree/PythonCgVariableConstructor.kt @@ -22,7 +22,8 @@ class PythonCgVariableConstructor(cgContext: CgContext) : CgVariableConstructor( private val nameGenerator = CgComponents.getNameGeneratorBy(context) override fun getOrCreateVariable(model: UtModel, name: String?): CgValue { val baseName = name ?: nameGenerator.nameFrom(model.classId) - return valueByModel.getOrPut(model) { + + return valueByUtModelWrapper.getOrPut(model.wrap()) { when (model) { is PythonTreeModel -> { val (value, arguments) = pythonBuildObject(model.tree, baseName)