Skip to content

Fix lambdas in concrete #1389

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 2 commits into from
Nov 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -570,12 +570,31 @@ class UtLambdaModel(
val capturedValues: MutableList<UtModel> = mutableListOf(),
) : UtReferenceModel(id, samType) {

val isFake: Boolean = lambdaName == fakeName

val lambdaMethodId: MethodId
get() = declaringClass.jClass
.declaredMethods
.singleOrNull { it.name == lambdaName }
?.executableId // synthetic lambda methods should not have overloads, so we always expect there to be only one method with the given name
?: error("More than one method with name $lambdaName found in class: ${declaringClass.canonicalName}")
get() {
if (isFake) {
val targetMethod = samType.jClass.declaredMethods.single()
return object : MethodId(
declaringClass,
fakeName,
targetMethod.returnType.id,
targetMethod.parameterTypes.map { it.id }
) {
override val modifiers: Int = ModifierFactory.invoke {
public = true
static = true
final = true
}
}
}
return declaringClass.jClass
.declaredMethods
.singleOrNull { it.name == lambdaName }
?.executableId // synthetic lambda methods should not have overloads, so we always expect there to be only one method with the given name
?: error("More than one method with name $lambdaName found in class: ${declaringClass.canonicalName}")
}

override fun toString(): String = "Anonymous function $lambdaName implementing functional interface $declaringClass"

Expand All @@ -591,6 +610,18 @@ class UtLambdaModel(
}

override fun hashCode(): Int = id

companion object {
private const val fakeName = "<FAKE>"

/**
* Create a non-existent lambda with fake method.
*
* That's temporary solution for building lambdas from concrete values.
*/
fun createFake(id: Int, samType: ClassId, declaringClass: ClassId) =
UtLambdaModel(id, samType, declaringClass, fakeName)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import org.utbot.framework.util.valueToClassId
import java.lang.reflect.Modifier
import java.util.IdentityHashMap
import java.util.stream.BaseStream
import org.utbot.framework.plugin.api.util.utContext

/**
* Represents common interface for model constructors.
Expand Down Expand Up @@ -79,6 +80,21 @@ class UtModelConstructor(
return objectToModelCache[value]?.let { (it as? UtReferenceModel)?.id } ?: computeUnusedIdAndUpdate()
}

private val proxyLambdaSubstring = "$\$Lambda$"

private fun isProxyLambda(value: Any?): Boolean {
if (value == null) {
return false
}
return proxyLambdaSubstring in value::class.java.name
}

private fun constructFakeLambda(value: Any, classId: ClassId): UtLambdaModel {
val baseClassName = value::class.java.name.substringBefore(proxyLambdaSubstring)
val baseClass = utContext.classLoader.loadClass(baseClassName).id
return UtLambdaModel.createFake(handleId(value), classId, baseClass)
}

/**
* Constructs a UtModel from a concrete [value] with a specific [classId]. The result can be a [UtAssembleModel]
* as well.
Expand All @@ -91,6 +107,9 @@ class UtModelConstructor(
return model
}
}
if (isProxyLambda(value)) {
return constructFakeLambda(value!!, classId)
}
return when (value) {
null -> UtNullModel(classId)
is Unit -> UtVoidModel
Expand Down