Skip to content

Commit 7e05e96

Browse files
committed
Avoid creating array for varargs that are only used once
1 parent b30aea3 commit 7e05e96

File tree

5 files changed

+69
-2
lines changed

5 files changed

+69
-2
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,5 +119,29 @@ class UtModelDeepMapper private constructor(
119119
*/
120120
fun collectAllModels(block: (collector: UtModelDeepMapper) -> Unit): Set<UtModel> =
121121
UtModelDeepMapper(UtModelNoopMapper).also(block).allInputtedModels
122+
123+
/**
124+
* Creates identity deep mapper, runs [block] on it, and returns the map showing how many times each
125+
* model is used (i.e. deeply counts usages of all models reachable from models passed to `counter`).
126+
*
127+
* @param ignoreAssembleOrigin if this flag is set to `true`, usages that occur
128+
* inside of [UtAssembleModel.origin] (and origin sub-models) are ignored.
129+
*/
130+
fun countUsages(
131+
ignoreAssembleOrigin: Boolean,
132+
block: (counter: UtModelDeepMapper) -> Unit
133+
) : Map<UtModel, Int> {
134+
val usageCounts = mutableMapOf<UtModel, Int>()
135+
block(UtModelDeepMapper(object : UtModelMapper {
136+
override fun <T : UtModel> map(model: T, clazz: Class<T>): T {
137+
usageCounts[model] = (usageCounts[model] ?: 0) + 1
138+
return when {
139+
ignoreAssembleOrigin && model is UtAssembleModel -> clazz.cast(model.copy(origin = null))
140+
else -> model
141+
}
142+
}
143+
}))
144+
return usageCounts
145+
}
122146
}
123147
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import org.utbot.framework.plugin.api.UtDirectGetFieldModel
55
import org.utbot.framework.plugin.api.UtDirectSetFieldModel
66
import org.utbot.framework.plugin.api.UtExecutableCallModel
77
import org.utbot.framework.plugin.api.UtExecution
8+
import org.utbot.framework.plugin.api.UtExecutionFailure
9+
import org.utbot.framework.plugin.api.UtExecutionResult
10+
import org.utbot.framework.plugin.api.UtExecutionSuccess
811
import org.utbot.framework.plugin.api.UtInstrumentation
912
import org.utbot.framework.plugin.api.UtModel
1013
import org.utbot.framework.plugin.api.UtNewInstanceInstrumentation
@@ -59,6 +62,17 @@ fun UtInstrumentation.mapModels(mapper: UtModelMapper) = when (this) {
5962
is UtStaticMethodInstrumentation -> copy(values = values.mapModels(mapper))
6063
}
6164

65+
fun UtExecutionResult.mapModels(mapper: UtModelMapper): UtExecutionResult = when (this) {
66+
is UtExecutionSuccess -> copy(model = model.map(mapper))
67+
is UtExecutionFailure -> this
68+
}
69+
6270
fun UtExecution.mapStateBeforeModels(mapper: UtModelMapper) = copy(
6371
stateBefore = stateBefore.mapModels(mapper)
6472
)
73+
74+
fun UtExecution.mapAllModels(mapper: UtModelMapper) = copy(
75+
stateBefore = stateBefore.mapModels(mapper),
76+
stateAfter = stateAfter.mapModels(mapper),
77+
result = result.mapModels(mapper),
78+
)

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@ interface CgContextOwner {
248248
*/
249249
val relevantFieldManagers: MutableList<CgAbstractClassFieldManager>
250250

251+
var modelToUsageCountInMethod: Map<UtModel, Int>
252+
251253
fun block(init: () -> Unit): Block {
252254
val prevBlock = currentBlock
253255
return try {
@@ -510,6 +512,7 @@ class CgContext(
510512
override val hangingTestsTimeout: HangingTestsTimeout = HangingTestsTimeout(),
511513
override val enableTestsTimeout: Boolean = true,
512514
override val relevantFieldManagers: MutableList<CgAbstractClassFieldManager> = mutableListOf(),
515+
override var modelToUsageCountInMethod: Map<UtModel, Int> = emptyMap(),
513516
override var containsReflectiveCall: Boolean = false,
514517
) : CgContextOwner {
515518
override lateinit var statesCache: EnvironmentFieldStateCache
@@ -666,6 +669,8 @@ class CgContext(
666669
runtimeExceptionTestsBehaviour = this.runtimeExceptionTestsBehaviour,
667670
hangingTestsTimeout = this.hangingTestsTimeout,
668671
enableTestsTimeout = this.enableTestsTimeout,
672+
relevantFieldManagers = this.relevantFieldManagers,
673+
modelToUsageCountInMethod = this.modelToUsageCountInMethod,
669674
containsReflectiveCall = this.containsReflectiveCall,
670675
)
671676
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ import org.utbot.framework.plugin.api.UtTimeoutException
117117
import org.utbot.framework.plugin.api.UtVoidModel
118118
import org.utbot.framework.plugin.api.isNotNull
119119
import org.utbot.framework.plugin.api.isNull
120+
import org.utbot.framework.plugin.api.mapper.UtModelDeepMapper.Companion.countUsages
121+
import org.utbot.framework.plugin.api.mapper.mapAllModels
120122
import org.utbot.framework.plugin.api.onFailure
121123
import org.utbot.framework.plugin.api.onSuccess
122124
import org.utbot.framework.plugin.api.util.IndentUtil.TAB
@@ -1819,6 +1821,9 @@ open class CgMethodConstructor(val context: CgContext) : CgContextOwner by conte
18191821
currentExecution = execution
18201822
determineExecutionType()
18211823
statesCache = EnvironmentFieldStateCache.emptyCacheFor(execution)
1824+
modelToUsageCountInMethod = countUsages(ignoreAssembleOrigin = true) { counter ->
1825+
execution.mapAllModels(counter)
1826+
}
18221827
return try {
18231828
block()
18241829
} finally {

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

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import org.utbot.framework.plugin.api.UtPrimitiveModel
4545
import org.utbot.framework.plugin.api.UtReferenceModel
4646
import org.utbot.framework.plugin.api.UtStatementCallModel
4747
import org.utbot.framework.plugin.api.UtVoidModel
48+
import org.utbot.framework.plugin.api.isNull
4849
import org.utbot.framework.plugin.api.util.booleanClassId
4950
import org.utbot.framework.plugin.api.util.booleanWrapperClassId
5051
import org.utbot.framework.plugin.api.util.classClassId
@@ -288,10 +289,18 @@ open class CgVariableConstructor(val context: CgContext) :
288289
when (statementModel) {
289290
is UtExecutableCallModel -> {
290291
val executable = statementModel.executable
291-
val paramNames = runCatching {
292+
var paramNames = runCatching {
292293
executable.executable.parameters.map { if (it.isNamePresent) it.name else null }
293294
}.getOrNull()
294-
val params = statementModel.params
295+
var params = statementModel.params
296+
297+
if (runCatching { executable.executable.isVarArgs }.getOrDefault(false)) {
298+
convertArrayToVarArgs(statementModel.params.last())?.let { varArgs ->
299+
paramNames = paramNames?.dropLast(1)?.plus(varArgs.map { paramNames?.last()?.removeSuffix("s") })
300+
params = params.dropLast(1) + varArgs
301+
}
302+
}
303+
295304
val caller = statementModel.instance?.let { declareOrGet(it) }
296305
val args = params.mapIndexed { i, param ->
297306
declareOrGet(param, name = paramNames?.getOrNull(i))
@@ -309,6 +318,16 @@ open class CgVariableConstructor(val context: CgContext) :
309318
}
310319
}
311320

321+
private fun convertArrayToVarArgs(array: UtModel): List<UtModel>? = when {
322+
array !is UtArrayModel -> null
323+
modelToUsageCountInMethod[array] != 1 -> null
324+
325+
// avoid ambiguity, where `null` can mean either array itself or first its element
326+
array.length == 1 && array[0].isNull() -> null
327+
328+
else -> List(array.length) { i -> array[i] }
329+
}
330+
312331
/**
313332
* If executable is getter/setter that should be syntactically replaced with field access
314333
* (e.g., getter/setter generated by Kotlin in Kotlin code), this method returns [CgStatement]

0 commit comments

Comments
 (0)