Skip to content

Commit 233edf1

Browse files
committed
Fixed unnecessary creation of multiple variables for the same expressions
1 parent cde88e8 commit 233edf1

File tree

4 files changed

+97
-34
lines changed

4 files changed

+97
-34
lines changed

utbot-framework/src/main/kotlin/org/utbot/framework/codegen/model/constructor/context/CgContext.kt

+9-9
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,6 @@ import org.utbot.framework.codegen.TestFramework
1010
import org.utbot.framework.codegen.model.constructor.tree.Block
1111
import org.utbot.framework.codegen.model.constructor.util.EnvironmentFieldStateCache
1212
import org.utbot.framework.codegen.model.constructor.util.importIfNeeded
13-
import org.utbot.framework.codegen.model.tree.CgAnnotation
14-
import org.utbot.framework.codegen.model.tree.CgExecutableCall
15-
import org.utbot.framework.codegen.model.tree.CgStatement
16-
import org.utbot.framework.codegen.model.tree.CgStatementExecutableCall
17-
import org.utbot.framework.codegen.model.tree.CgTestMethod
18-
import org.utbot.framework.codegen.model.tree.CgThisInstance
19-
import org.utbot.framework.codegen.model.tree.CgValue
20-
import org.utbot.framework.codegen.model.tree.CgVariable
2113
import java.util.IdentityHashMap
2214
import kotlinx.collections.immutable.PersistentList
2315
import kotlinx.collections.immutable.PersistentMap
@@ -31,7 +23,7 @@ import org.utbot.framework.codegen.model.constructor.builtin.UtilClassFileMethod
3123
import org.utbot.framework.codegen.model.constructor.builtin.UtilMethodProvider
3224
import org.utbot.framework.codegen.model.constructor.TestClassContext
3325
import org.utbot.framework.codegen.model.constructor.TestClassModel
34-
import org.utbot.framework.codegen.model.tree.CgParameterKind
26+
import org.utbot.framework.codegen.model.tree.*
3527
import org.utbot.framework.plugin.api.BuiltinClassId
3628
import org.utbot.framework.plugin.api.ClassId
3729
import org.utbot.framework.plugin.api.CodegenLanguage
@@ -154,6 +146,11 @@ internal interface CgContextOwner {
154146
// variable names being used in the current name scope
155147
var existingVariableNames: PersistentSet<String>
156148

149+
/**
150+
* Stores the variables that were created with specific [CgExpression].
151+
*/
152+
var createdFromExpressionVariables: PersistentMap<CgExpression, CgVariable>
153+
157154
// variables of java.lang.Class type declared in the current name scope
158155
var declaredClassRefs: PersistentMap<ClassId, CgVariable>
159156

@@ -309,6 +306,7 @@ internal interface CgContextOwner {
309306

310307
fun <R> withNameScope(block: () -> R): R {
311308
val prevVariableNames = existingVariableNames
309+
val prevCreatedFromExpressionVariables = createdFromExpressionVariables
312310
val prevDeclaredClassRefs = declaredClassRefs
313311
val prevDeclaredExecutableRefs = declaredExecutableRefs
314312
val prevDeclaredFieldRefs = declaredFieldRefs
@@ -318,6 +316,7 @@ internal interface CgContextOwner {
318316
block()
319317
} finally {
320318
existingVariableNames = prevVariableNames
319+
createdFromExpressionVariables = prevCreatedFromExpressionVariables
321320
declaredClassRefs = prevDeclaredClassRefs
322321
declaredExecutableRefs = prevDeclaredExecutableRefs
323322
declaredFieldRefs = prevDeclaredFieldRefs
@@ -419,6 +418,7 @@ internal data class CgContext(
419418
override var mockFrameworkUsed: Boolean = false,
420419
override var currentBlock: PersistentList<CgStatement> = persistentListOf(),
421420
override var existingVariableNames: PersistentSet<String> = persistentSetOf(),
421+
override var createdFromExpressionVariables: PersistentMap<CgExpression, CgVariable> = persistentMapOf(),
422422
override var declaredClassRefs: PersistentMap<ClassId, CgVariable> = persistentMapOf(),
423423
override var declaredExecutableRefs: PersistentMap<ExecutableId, CgVariable> = persistentMapOf(),
424424
override var declaredFieldRefs: PersistentMap<FieldId, CgVariable> = persistentMapOf(),

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

+1
Original file line numberDiff line numberDiff line change
@@ -1639,6 +1639,7 @@ internal class CgMethodConstructor(val context: CgContext) : CgContextOwner by c
16391639
containsReflectiveCall = false
16401640
mockFrameworkManager.clearExecutionResources()
16411641
currentMethodParameters.clear()
1642+
createdFromExpressionVariables.clear()
16421643
}
16431644

16441645
/**

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

+11
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ internal class CgStatementConstructorImpl(context: CgContext) :
199199
// check if we can use base type and initializer directly
200200
// if not, fall back to using reflection
201201
val baseExpr = init()
202+
203+
// return already created variable if present
204+
createdFromExpressionVariables[baseExpr]?.let {
205+
return Either.right(it)
206+
}
207+
202208
val (type, expr) = when (baseExpr) {
203209
is CgEnumConstantAccess -> guardEnumConstantAccess(baseExpr)
204210
is CgAllocateArray -> guardArrayAllocation(baseExpr)
@@ -233,6 +239,11 @@ internal class CgStatementConstructorImpl(context: CgContext) :
233239
classRef?.let { declaredClassRefs = declaredClassRefs.put(it, declaration.variable) }
234240
updateVariableScope(declaration.variable, model)
235241

242+
// update created variables storage
243+
declaration.variable.let {
244+
createdFromExpressionVariables = createdFromExpressionVariables.put(baseExpr, it)
245+
}
246+
236247
return Either.left(declaration)
237248
}
238249

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

+76-25
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import org.utbot.framework.plugin.api.util.intClassId
3030
import org.utbot.framework.plugin.api.util.objectArrayClassId
3131
import org.utbot.framework.plugin.api.util.objectClassId
3232
import org.utbot.framework.plugin.api.util.voidClassId
33+
import java.util.Objects
3334

3435
interface CgElement {
3536
// TODO: order of cases is important here due to inheritance between some of the element types
@@ -343,7 +344,7 @@ class CgMultipleArgsAnnotation(
343344
val arguments: MutableList<CgNamedAnnotationArgument>
344345
) : CgAnnotation()
345346

346-
class CgArrayAnnotationArgument(
347+
data class CgArrayAnnotationArgument(
347348
val values: List<CgExpression>
348349
) : CgExpression {
349350
override val type: ClassId = objectArrayClassId // TODO: is this type correct?
@@ -475,7 +476,7 @@ fun convertDocToCg(stmt: DocStatement): CgDocStatement {
475476

476477
// Anonymous function (lambda)
477478

478-
class CgAnonymousFunction(
479+
data class CgAnonymousFunction(
479480
override val type: ClassId,
480481
val parameters: List<CgParameterDeclaration>,
481482
val body: List<CgStatement>
@@ -489,7 +490,7 @@ class CgReturnStatement(val expression: CgExpression) : CgStatement
489490

490491
// TODO: check nested array element access expressions e.g. a[0][1][2]
491492
// TODO in general it is not CgReferenceExpression because array element can have a primitive type
492-
class CgArrayElementAccess(val array: CgExpression, val index: CgExpression) : CgReferenceExpression {
493+
data class CgArrayElementAccess(val array: CgExpression, val index: CgExpression) : CgReferenceExpression {
493494
override val type: ClassId = array.type.elementClassId ?: objectClassId
494495
}
495496

@@ -501,17 +502,17 @@ sealed class CgComparison : CgExpression {
501502
override val type: ClassId = booleanClassId
502503
}
503504

504-
class CgLessThan(
505+
data class CgLessThan(
505506
override val left: CgExpression,
506507
override val right: CgExpression
507508
) : CgComparison()
508509

509-
class CgGreaterThan(
510+
data class CgGreaterThan(
510511
override val left: CgExpression,
511512
override val right: CgExpression
512513
) : CgComparison()
513514

514-
class CgEqualTo(
515+
data class CgEqualTo(
515516
override val left: CgExpression,
516517
override val right: CgExpression
517518
) : CgComparison()
@@ -620,7 +621,7 @@ interface CgReferenceExpression : CgExpression
620621
*
621622
* @property isSafetyCast shows if we should use "as?" instead of "as" in Kotlin code
622623
*/
623-
class CgTypeCast(
624+
data class CgTypeCast(
624625
val targetType: ClassId,
625626
val expression: CgExpression,
626627
val isSafetyCast: Boolean = false,
@@ -631,7 +632,7 @@ class CgTypeCast(
631632
/**
632633
* Represents [java.lang.Class.isInstance] method.
633634
*/
634-
class CgIsInstance(
635+
data class CgIsInstance(
635636
val classExpression: CgExpression,
636637
val value: CgExpression,
637638
): CgExpression {
@@ -645,7 +646,7 @@ interface CgValue : CgReferenceExpression
645646

646647
// This instance
647648

648-
class CgThisInstance(override val type: ClassId) : CgValue
649+
data class CgThisInstance(override val type: ClassId) : CgValue
649650

650651
// Variables
651652

@@ -683,7 +684,7 @@ open class CgVariable(
683684
* - in Java it is an equivalent of [CgVariable]
684685
* - in Kotlin the difference is in addition of "!!" to the name
685686
*/
686-
class CgNotNullAssertion(val expression: CgExpression) : CgValue {
687+
data class CgNotNullAssertion(val expression: CgExpression) : CgValue {
687688
override val type: ClassId
688689
get() = when (val expressionType = expression.type) {
689690
is BuiltinClassId -> BuiltinClassId(
@@ -740,7 +741,7 @@ sealed class CgParameterKind {
740741

741742
// Primitive and String literals
742743

743-
class CgLiteral(override val type: ClassId, val value: Any?) : CgValue {
744+
data class CgLiteral(override val type: ClassId, val value: Any?) : CgValue {
744745
override fun equals(other: Any?): Boolean {
745746
if (this === other) return true
746747
if (javaClass != other?.javaClass) return false
@@ -770,12 +771,42 @@ class CgNonStaticRunnable(
770771
type: ClassId,
771772
val referenceExpression: CgReferenceExpression,
772773
methodId: MethodId
773-
) : CgRunnable(type, methodId)
774+
) : CgRunnable(type, methodId) {
775+
override fun equals(other: Any?): Boolean {
776+
if (this === other) return true
777+
if (javaClass != other?.javaClass) return false
778+
779+
other as CgNonStaticRunnable
780+
781+
if (type != other.type) return false
782+
if (referenceExpression != other.referenceExpression) return false
783+
if (methodId != other.methodId) return false
784+
785+
return true
786+
}
787+
788+
override fun hashCode(): Int = Objects.hash(type, referenceExpression, methodId)
789+
}
774790

775791
/**
776792
* [classId] is Random is Random::nextRandomInt (static) etc
777793
*/
778-
class CgStaticRunnable(type: ClassId, val classId: ClassId, methodId: MethodId) : CgRunnable(type, methodId)
794+
class CgStaticRunnable(type: ClassId, val classId: ClassId, methodId: MethodId) : CgRunnable(type, methodId) {
795+
override fun equals(other: Any?): Boolean {
796+
if (this === other) return true
797+
if (javaClass != other?.javaClass) return false
798+
799+
other as CgStaticRunnable
800+
801+
if (type != other.type) return false
802+
if (classId != other.classId) return false
803+
if (methodId != other.methodId) return false
804+
805+
return true
806+
}
807+
808+
override fun hashCode(): Int = Objects.hash(type, classId, methodId)
809+
}
779810

780811
// Array allocation
781812

@@ -805,10 +836,10 @@ open class CgAllocateArray(type: ClassId, elementType: ClassId, val size: Int) :
805836
/**
806837
* Allocation of an array with initialization. For example: `new String[] {"a", "b", null}`.
807838
*/
808-
class CgAllocateInitializedArray(val initializer: CgArrayInitializer) :
839+
data class CgAllocateInitializedArray(val initializer: CgArrayInitializer) :
809840
CgAllocateArray(initializer.arrayType, initializer.elementType, initializer.size)
810841

811-
class CgArrayInitializer(val arrayType: ClassId, val elementType: ClassId, val values: List<CgExpression>) : CgExpression {
842+
data class CgArrayInitializer(val arrayType: ClassId, val elementType: ClassId, val values: List<CgExpression>) : CgExpression {
812843
val size: Int
813844
get() = values.size
814845

@@ -819,7 +850,7 @@ class CgArrayInitializer(val arrayType: ClassId, val elementType: ClassId, val v
819850

820851
// Spread operator (for Kotlin, empty for Java)
821852

822-
class CgSpread(override val type: ClassId, val array: CgExpression) : CgExpression
853+
data class CgSpread(override val type: ClassId, val array: CgExpression) : CgExpression
823854

824855
// Enum constant
825856

@@ -840,12 +871,12 @@ abstract class CgAbstractFieldAccess : CgReferenceExpression {
840871
get() = fieldId.type
841872
}
842873

843-
class CgFieldAccess(
874+
data class CgFieldAccess(
844875
val caller: CgExpression,
845876
override val fieldId: FieldId
846877
) : CgAbstractFieldAccess()
847878

848-
class CgStaticFieldAccess(
879+
data class CgStaticFieldAccess(
849880
override val fieldId: FieldId
850881
) : CgAbstractFieldAccess() {
851882
val declaringClass: ClassId = fieldId.declaringClass
@@ -854,7 +885,7 @@ class CgStaticFieldAccess(
854885

855886
// Conditional statements
856887

857-
class CgIfStatement(
888+
data class CgIfStatement(
858889
val condition: CgExpression,
859890
val trueBranch: List<CgStatement>,
860891
val falseBranch: List<CgStatement>? = null // false branch may be absent
@@ -880,7 +911,7 @@ class CgLogicalAnd(
880911
override val type: ClassId = booleanClassId
881912
}
882913

883-
class CgLogicalOr(
914+
data class CgLogicalOr(
884915
val left: CgExpression,
885916
val right: CgExpression
886917
) : CgExpression {
@@ -892,7 +923,7 @@ class CgLogicalOr(
892923
/**
893924
* @param variable represents an array variable
894925
*/
895-
class CgGetLength(val variable: CgVariable) : CgExpression {
926+
data class CgGetLength(val variable: CgVariable) : CgExpression {
896927
override val type: ClassId = intClassId
897928
}
898929

@@ -902,9 +933,29 @@ sealed class CgGetClass(val classId: ClassId) : CgReferenceExpression {
902933
override val type: ClassId = Class::class.id
903934
}
904935

905-
class CgGetJavaClass(classId: ClassId) : CgGetClass(classId)
936+
class CgGetJavaClass(classId: ClassId) : CgGetClass(classId) {
937+
override fun equals(other: Any?): Boolean {
938+
if (this === other) return true
939+
if (javaClass != other?.javaClass) return false
940+
return classId == (other as CgGetClass).classId
941+
}
906942

907-
class CgGetKotlinClass(classId: ClassId) : CgGetClass(classId)
943+
override fun hashCode(): Int {
944+
return javaClass.hashCode()
945+
}
946+
}
947+
948+
class CgGetKotlinClass(classId: ClassId) : CgGetClass(classId) {
949+
override fun equals(other: Any?): Boolean {
950+
if (this === other) return true
951+
if (javaClass != other?.javaClass) return false
952+
return classId == (other as CgGetClass).classId
953+
}
954+
955+
override fun hashCode(): Int {
956+
return javaClass.hashCode()
957+
}
958+
}
908959

909960
// Executable calls
910961

@@ -918,15 +969,15 @@ abstract class CgExecutableCall : CgReferenceExpression {
918969
abstract val typeParameters: TypeParameters
919970
}
920971

921-
class CgConstructorCall(
972+
data class CgConstructorCall(
922973
override val executableId: ConstructorId,
923974
override val arguments: List<CgExpression>,
924975
override val typeParameters: TypeParameters = TypeParameters()
925976
) : CgExecutableCall() {
926977
override val type: ClassId = executableId.classId
927978
}
928979

929-
class CgMethodCall(
980+
data class CgMethodCall(
930981
val caller: CgExpression?,
931982
override val executableId: MethodId,
932983
override val arguments: List<CgExpression>,

0 commit comments

Comments
 (0)