Skip to content

Supported assemble models for streams in concrete execution #1229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 28, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Got rid of varargs processing stuff
  • Loading branch information
Damtev committed Oct 28, 2022
commit 5b7ffc70f2b97a090a57e38dcee4664889e77213
43 changes: 5 additions & 38 deletions utbot-core/src/main/kotlin/org/utbot/common/KClassUtil.kt
Original file line number Diff line number Diff line change
@@ -1,55 +1,22 @@
package org.utbot.common

import java.lang.reflect.Array
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.Method
import kotlin.reflect.KClass

val Class<*>.nameOfPackage: String get() = `package`?.name?:""

/**
* Invokes [this] method of passed [obj] instance (null for static methods) with the passed [args] arguments.
* NOTE: vararg parameters must be passed as an array of the corresponding type.
*/
fun Method.invokeCatching(obj: Any?, args: List<Any?>) = try {
val invocation = if (isVarArgs) {
// In java only last parameter could be vararg
val firstNonVarargParametersCount = parameterCount - 1

val varargArray = constructVarargParameterArray(firstNonVarargParametersCount, args)

if (firstNonVarargParametersCount == 0) {
// Only vararg parameter, just pass only it as an array
invoke(obj, varargArray)
} else {
// Pass first non vararg parameters as vararg, and the vararg parameter as an array
val firstNonVarargParameters = args.take(firstNonVarargParametersCount)

invoke(obj, *firstNonVarargParameters.toTypedArray(), varargArray)
}
} else {
invoke(obj, *args.toTypedArray())
}
val invocation = invoke(obj, *args.toTypedArray())

Result.success(invocation)
} catch (e: InvocationTargetException) {
Result.failure<Nothing>(e.targetException)
}

// https://stackoverflow.com/a/59857242
private fun Method.constructVarargParameterArray(firstNonVarargParametersCount: Int, args: List<Any?>): Any {
val varargCount = args.size - firstNonVarargParametersCount
val varargElements = args.drop(firstNonVarargParametersCount)

val varargElementType = parameterTypes.last().componentType
requireNotNull(varargElementType) {
"Vararg parameter of method $this was expected to be array but ${parameterTypes.last()} found"
}

val varargArray = Array.newInstance(parameterTypes.last().componentType, varargCount)

varargElements.forEachIndexed { index, value ->
Array.set(varargArray, index, value)
}

return varargArray
}

val KClass<*>.allNestedClasses: List<KClass<*>>
get() = listOf(this) + nestedClasses.flatMap { it.allNestedClasses }
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ import org.utbot.framework.plugin.api.util.isAbstract
import org.utbot.framework.plugin.api.util.isArray
import org.utbot.framework.plugin.api.util.isStatic
import org.utbot.framework.plugin.api.util.isSubtypeOf
import org.utbot.framework.plugin.api.util.isVarArgs
import org.utbot.framework.plugin.api.util.jClass
import org.utbot.framework.plugin.api.util.method
import org.utbot.framework.plugin.api.util.objectArrayClassId
Expand Down Expand Up @@ -487,17 +486,8 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA
executable: ExecutableId,
args: List<CgExpression>,
ambiguousOverloads: List<ExecutableId>
): List<CgExpression> {
val parametersCount = executable.parameters.size

val (nonVarargParameters, varargParameters) = if (executable.isVarArgs) {
// Vararg is always the last parameter
args.take(parametersCount - 1) to args.drop(parametersCount - 1)
} else {
args to emptyList()
}

val castedNonVarargs = nonVarargParameters.withIndex().map { (i ,arg) ->
): List<CgExpression> =
args.withIndex().map { (i ,arg) ->
val targetType = executable.parameters[i]

// always cast nulls
Expand All @@ -514,33 +504,6 @@ internal class CgCallableAccessManagerImpl(val context: CgContext) : CgCallableA
if (ancestors.isNotEmpty()) typeCast(targetType, arg) else arg
}

if (varargParameters.isEmpty()) {
return castedNonVarargs
}

// Vararg exist, create an array for them without inner casts

val varargClassId = executable.parameters.last()
val varargElementClassId = varargClassId.elementClassId
?: error("Vararg parameter of $executable has to be an array but ${executable.parameters.last()} found")

// Use a simple array initializer here since this situation is pretty rare
val initializer = arrayInitializer(
arrayType = varargClassId,
elementType = varargElementClassId,
values = varargParameters
)

val arrayForVarargs = variableConstructor.newVar(
baseType = varargElementClassId,
baseName = "varargParameters"
) {
initializer
}

return castedNonVarargs + arrayForVarargs
}

/**
* Receives a list of [CgExpression].
* Transforms it into a list of [CgExpression] where:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,38 @@ package org.utbot.framework.concrete

import org.utbot.framework.plugin.api.ClassId
import org.utbot.framework.plugin.api.MethodId
import org.utbot.framework.plugin.api.UtArrayModel
import org.utbot.framework.plugin.api.UtAssembleModel
import org.utbot.framework.plugin.api.UtExecutableCallModel
import org.utbot.framework.plugin.api.UtModel
import org.utbot.framework.plugin.api.UtNullModel
import org.utbot.framework.plugin.api.UtPrimitiveModel
import org.utbot.framework.plugin.api.UtStatementModel
import org.utbot.framework.plugin.api.util.defaultValueModel
import org.utbot.framework.plugin.api.util.doubleArrayClassId
import org.utbot.framework.plugin.api.util.doubleStreamClassId
import org.utbot.framework.plugin.api.util.intArrayClassId
import org.utbot.framework.plugin.api.util.intStreamClassId
import org.utbot.framework.plugin.api.util.isPrimitiveWrapper
import org.utbot.framework.plugin.api.util.longArrayClassId
import org.utbot.framework.plugin.api.util.longStreamClassId
import org.utbot.framework.plugin.api.util.methodId
import org.utbot.framework.plugin.api.util.objectArrayClassId
import org.utbot.framework.plugin.api.util.streamClassId
import org.utbot.framework.util.modelIdCounter
import org.utbot.framework.util.valueToClassId

/**
* Max number of elements in any concrete stream.
*/
private const val STREAM_ELEMENTS_LIMIT: Int = 1_000_000

internal abstract class AbstractStreamConstructor(private val streamClassId: ClassId, elementsClassId: ClassId) : UtAssembleModelConstructorBase() {
internal abstract class AbstractStreamConstructor(private val streamClassId: ClassId, private val elementsClassId: ClassId) : UtAssembleModelConstructorBase() {
private val singleElementClassId: ClassId = elementsClassId.elementClassId
?: error("Stream $streamClassId elements have to be an array but $elementsClassId found")

private val elementDefaultValueModel: UtModel = singleElementClassId.defaultValueModel()

override fun provideInstantiationCall(
internalConstructor: UtModelConstructorInterface,
value: Any,
Expand All @@ -46,10 +58,18 @@ internal abstract class AbstractStreamConstructor(private val streamClassId: Cla
)
}

val varargModelsArray = UtArrayModel(
id = modelIdCounter.incrementAndGet(),
classId = elementsClassId,
length = models.size,
constModel = elementDefaultValueModel,
stores = models.mapIndexed { i, model -> i to model.wrapperModelToPrimitiveModel() }.toMap(mutableMapOf())
)

return UtExecutableCallModel(
instance = null,
executable = ofMethodId,
params = models
params = listOf(varargModelsArray)
)
}

Expand All @@ -71,6 +91,30 @@ internal abstract class AbstractStreamConstructor(private val streamClassId: Cla
returnType = this.streamClassId,
arguments = arrayOf(elementsClassId) // vararg
)

/**
* Transforms [this] to [UtPrimitiveModel] if it is an [UtAssembleModel] for the corresponding wrapper
* (primitive int and wrapper Integer, etc.), and throws an error otherwise.
*/
private fun UtModel.wrapperModelToPrimitiveModel(): UtModel {
if (!classId.isPrimitiveWrapper) {
// We do not need to transform classes other than primitive wrappers
return this
}

require(this !is UtNullModel) {
"Unexpected null value in wrapper for primitive stream ${this@AbstractStreamConstructor}"
}

require(this is UtAssembleModel) {
"Unexpected not wrapper assemble model $this for value in wrapper " +
"for primitive stream ${this@AbstractStreamConstructor.streamClassId}"
}

return (instantiationCall.params.firstOrNull() as? UtPrimitiveModel)
?: error("No primitive value parameter for wrapper constructor $instantiationCall in model $this " +
"in wrapper for primitive stream ${this@AbstractStreamConstructor.streamClassId}")
}
}

internal class BaseStreamConstructor : AbstractStreamConstructor(streamClassId, objectArrayClassId)
Expand Down