8
8
9
9
package org.utbot.framework.plugin.api
10
10
11
+ import org.utbot.common.WorkaroundReason
11
12
import org.utbot.common.isDefaultValue
13
+ import org.utbot.common.unreachableBranch
12
14
import org.utbot.common.withToStringThreadLocalReentrancyGuard
15
+ import org.utbot.common.workaround
13
16
import org.utbot.framework.UtSettings
14
17
import org.utbot.framework.plugin.api.MockFramework.MOCKITO
15
18
import org.utbot.framework.plugin.api.impl.FieldIdReflectionStrategy
@@ -19,15 +22,18 @@ import org.utbot.framework.plugin.api.util.byteClassId
19
22
import org.utbot.framework.plugin.api.util.charClassId
20
23
import org.utbot.framework.plugin.api.util.constructor
21
24
import org.utbot.framework.plugin.api.util.doubleClassId
25
+ import org.utbot.framework.plugin.api.util.executable
22
26
import org.utbot.framework.plugin.api.util.executableId
23
27
import org.utbot.framework.plugin.api.util.floatClassId
24
28
import org.utbot.framework.plugin.api.util.id
25
29
import org.utbot.framework.plugin.api.util.intClassId
26
30
import org.utbot.framework.plugin.api.util.isArray
27
31
import org.utbot.framework.plugin.api.util.isPrimitive
32
+ import org.utbot.framework.plugin.api.util.isSubtypeOf
28
33
import org.utbot.framework.plugin.api.util.jClass
29
34
import org.utbot.framework.plugin.api.util.longClassId
30
35
import org.utbot.framework.plugin.api.util.method
36
+ import org.utbot.framework.plugin.api.util.objectClassId
31
37
import org.utbot.framework.plugin.api.util.primitiveTypeJvmNameOrNull
32
38
import org.utbot.framework.plugin.api.util.safeJField
33
39
import org.utbot.framework.plugin.api.util.shortClassId
@@ -48,17 +54,25 @@ import soot.Type
48
54
import soot.VoidType
49
55
import soot.jimple.JimpleBody
50
56
import soot.jimple.Stmt
57
+ import sun.reflect.generics.parser.SignatureParser
58
+ import sun.reflect.generics.tree.ClassTypeSignature
59
+ import sun.reflect.generics.tree.TypeVariableSignature
51
60
import java.io.File
61
+ import java.lang.reflect.Constructor
62
+ import java.lang.reflect.GenericSignatureFormatError
63
+ import java.lang.reflect.Method
52
64
import java.lang.reflect.Modifier
53
65
import kotlin.contracts.ExperimentalContracts
54
66
import kotlin.contracts.contract
55
67
import kotlin.jvm.internal.CallableReference
56
68
import kotlin.reflect.KCallable
57
69
import kotlin.reflect.KClass
58
70
import kotlin.reflect.KFunction
71
+ import kotlin.reflect.KType
59
72
import kotlin.reflect.full.instanceParameter
60
73
import kotlin.reflect.jvm.javaConstructor
61
74
import kotlin.reflect.jvm.javaType
75
+ import kotlin.reflect.jvm.kotlinFunction
62
76
63
77
const val SYMBOLIC_NULL_ADDR : Int = 0
64
78
@@ -264,7 +278,11 @@ data class UtError(
264
278
*/
265
279
sealed class UtModel (
266
280
open val classId : ClassId
267
- )
281
+ ) {
282
+ open fun processGenerics (type : KType ) {}
283
+
284
+ open fun fillGenericsAsObjects () {}
285
+ }
268
286
269
287
/* *
270
288
* Class representing models for values that might have an address.
@@ -402,6 +420,10 @@ data class UtCompositeModel(
402
420
val fields : MutableMap <FieldId , UtModel > = mutableMapOf(),
403
421
val mocks : MutableMap <ExecutableId , List <UtModel >> = mutableMapOf(),
404
422
) : UtReferenceModel(id, classId) {
423
+ override fun processGenerics (type : KType ) {
424
+ classId.typeParameters.fromType(type)
425
+ }
426
+
405
427
// TODO: SAT-891 - rewrite toString() method
406
428
override fun toString () = withToStringThreadLocalReentrancyGuard {
407
429
buildString {
@@ -505,6 +527,135 @@ data class UtAssembleModel(
505
527
val finalInstantiationModel
506
528
get() = instantiationChain.lastOrNull()
507
529
530
+ override fun processGenerics (type : KType ) {
531
+ classId.typeParameters.fromType(type)
532
+
533
+ // TODO might cause problems with type params when program synthesis comes
534
+ // assume that last statement is constructor call
535
+ instantiationChain.lastOrNull()?.let { lastStatement ->
536
+ (lastStatement as ? UtExecutableCallModel )?.let {
537
+ when (val executable = it.executable) {
538
+ is ConstructorId -> executable.classId.typeParameters.copyFromClassId(classId)
539
+ is MethodId -> executable.returnType.typeParameters.copyFromClassId(classId)
540
+ }
541
+
542
+ try {
543
+ val function = when (val executable = it.executable.executable) {
544
+ is Constructor <* > -> executable.kotlinFunction
545
+ is Method -> executable.kotlinFunction
546
+ else -> unreachableBranch(" this executable does not exist $executable " )
547
+ }
548
+
549
+ it.params.mapIndexed { i, param ->
550
+ function?.parameters?.getOrNull(i)?.type?.let { it1 -> param.processGenerics(it1) }
551
+ }
552
+ } catch (e: Error ) {
553
+ // KotlinReflectionInternalError can't be imported, but it is assumed here
554
+ // it can be thrown here because, i.e., Int(Int) constructor does not exist in Kotlin
555
+ }
556
+
557
+ workaround(WorkaroundReason .COLLECTION_CONSTRUCTOR_FROM_COLLECTION ) {
558
+ val propagateFromReturnTypeToParameter = {
559
+ ((it.params[0 ] as ? UtAssembleModel )?.instantiationChain?.get(0 ) as ? UtExecutableCallModel )
560
+ ?.executable?.classId?.typeParameters?.copyFromClassId(classId)
561
+ }
562
+
563
+ when (val executable = it.executable.executable) {
564
+ is Constructor <* > -> {
565
+ // Can't parse signature here, since constructors return void
566
+ // This part only works for cases like Collection<T>(collection: Collection<T>)
567
+ if (it.executable is ConstructorId ) {
568
+ if (it.executable.classId.isSubtypeOf(Collection ::class .id)) {
569
+ if (it.executable.parameters.size == 1 && it.executable.parameters[0 ].isSubtypeOf(Collection ::class .id)) {
570
+ propagateFromReturnTypeToParameter()
571
+ }
572
+ }
573
+ }
574
+ }
575
+ is Method -> {
576
+ try {
577
+ val f = Method ::class .java.getDeclaredField(" signature" )
578
+ f.isAccessible = true
579
+ val signature = f.get(executable) as String
580
+ val parsedSignature = SignatureParser .make().parseMethodSig(signature)
581
+
582
+ // check if parameter types are equal to return types
583
+ // e.g. <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;)Ljava/util/List<TT;>;
584
+ parsedSignature.parameterTypes.forEach { param ->
585
+ // TODO no idea what the `path` is supposed to mean, all we need is in the first element
586
+ val paramTypeParameters =
587
+ (param as ClassTypeSignature ).path.first().typeArguments
588
+ val returnTypeParameters =
589
+ (parsedSignature.returnType as ClassTypeSignature ).path.first().typeArguments
590
+
591
+ val genericsAreEqual = paramTypeParameters.foldIndexed(true ) { i, prev, it ->
592
+ val paramIdentifier = (it as TypeVariableSignature ).identifier
593
+ val returnTypeParamIdentifier = (returnTypeParameters.getOrNull(i) as TypeVariableSignature ).identifier
594
+ prev && (paramIdentifier == returnTypeParamIdentifier)
595
+ }
596
+
597
+ if (genericsAreEqual) {
598
+ propagateFromReturnTypeToParameter()
599
+ }
600
+ }
601
+ } catch (e: GenericSignatureFormatError ) {
602
+ // TODO log
603
+ }
604
+ }
605
+ else -> unreachableBranch(" this executable does not exist $executable " )
606
+ }
607
+ }
608
+ }
609
+ }
610
+
611
+ for (model in modificationsChain) {
612
+ if (model is UtExecutableCallModel ) {
613
+ model.params.mapIndexed { i, param ->
614
+ type.arguments.getOrNull(i)?.type?.let { it1 -> param.processGenerics(it1) }
615
+ }
616
+ }
617
+ }
618
+ }
619
+
620
+ override fun fillGenericsAsObjects () {
621
+ // TODO might cause problems with type params when program synthesis comes
622
+ // assume that last statement is constructor call
623
+ instantiationChain.lastOrNull()?.let { lastStatement ->
624
+ (lastStatement as ? UtExecutableCallModel )?.let {
625
+ try {
626
+ val function = when (val executable = it.executable.executable) {
627
+ is Constructor <* > -> executable.kotlinFunction
628
+ is Method -> executable.kotlinFunction
629
+ else -> unreachableBranch(" this executable does not exist $executable " )
630
+ }
631
+ function?.let { f ->
632
+ classId.typeParameters.parameters = List (f.typeParameters.size) { objectClassId }
633
+ }
634
+
635
+ it.params.map { param -> param.fillGenericsAsObjects() }
636
+ } catch (e: Error ) {
637
+ // KotlinReflectionInternalError can't be imported, but it is assumed here
638
+ // it can be thrown here because, i.e., Int(Int) constructor does not exist in Kotlin
639
+ }
640
+ }
641
+ }
642
+
643
+ for (model in modificationsChain) {
644
+ if (model is UtExecutableCallModel ) {
645
+ val function = when (val executable = model.executable.executable) {
646
+ is Constructor <* > -> executable.kotlinFunction
647
+ is Method -> executable.kotlinFunction
648
+ else -> unreachableBranch(" this executable does not exist $executable " )
649
+ }
650
+ function?.let { f ->
651
+ model.executable.classId.typeParameters.parameters = List (f.typeParameters.size) { objectClassId }
652
+ }
653
+
654
+ model.params.map { it.fillGenericsAsObjects() }
655
+ }
656
+ }
657
+ }
658
+
508
659
override fun toString () = withToStringThreadLocalReentrancyGuard {
509
660
buildString {
510
661
append(" UtAssembleModel(${classId.simpleName} $modelName ) " )
@@ -760,8 +911,7 @@ open class ClassId @JvmOverloads constructor(
760
911
open val allConstructors: Sequence <ConstructorId >
761
912
get() = jClass.declaredConstructors.asSequence().map { it.executableId }
762
913
763
- open val typeParameters: TypeParameters
764
- get() = TypeParameters ()
914
+ open val typeParameters: TypeParameters = TypeParameters ()
765
915
766
916
open val outerClass: Class <* >?
767
917
get() = jClass.enclosingClass
@@ -904,6 +1054,9 @@ open class FieldId(val declaringClass: ClassId, val name: String) {
904
1054
open val type: ClassId
905
1055
get() = strategy.type
906
1056
1057
+ // required to store and update type parameters
1058
+ val fixedType: ClassId by lazy { type }
1059
+
907
1060
override fun equals (other : Any? ): Boolean {
908
1061
if (this == = other) return true
909
1062
if (javaClass != other?.javaClass) return false
@@ -1064,9 +1217,31 @@ class BuiltinMethodId(
1064
1217
override val isPrivate : Boolean = false
1065
1218
) : MethodId(classId, name, returnType, parameters)
1066
1219
1067
- open class TypeParameters (val parameters : List <ClassId > = emptyList())
1220
+ open class TypeParameters (var parameters : List <ClassId > = emptyList()) {
1221
+ fun copyFromClassId (classId : ClassId ) {
1222
+ parameters = classId.typeParameters.parameters
1223
+ }
1224
+
1225
+ fun fromType (type : KType ) {
1226
+ if (type.arguments.isEmpty()) return
1227
+
1228
+ parameters = type.arguments.map {
1229
+ when (val clazz = it.type?.classifier) {
1230
+ is KClass <* > -> {
1231
+ val classId = clazz.id
1232
+ it.type?.let { t ->
1233
+ classId.typeParameters.fromType(t)
1234
+ } ? : error(" " )
1235
+
1236
+ classId
1237
+ }
1238
+ else -> objectClassId
1239
+ }
1240
+ }
1241
+ }
1242
+ }
1068
1243
1069
- class WildcardTypeParameter : TypeParameters (emptyList() )
1244
+ class WildcardTypeParameter : ClassId ( " org.utbot.framework.plugin.api.WildcardTypeParameter " )
1070
1245
1071
1246
interface CodeGenerationSettingItem {
1072
1247
val displayName: String
0 commit comments