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,31 @@ 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.ArrayTypeSignature
59
+ import sun.reflect.generics.tree.BottomSignature
60
+ import sun.reflect.generics.tree.ClassTypeSignature
61
+ import sun.reflect.generics.tree.SimpleClassTypeSignature
62
+ import sun.reflect.generics.tree.TypeArgument
63
+ import sun.reflect.generics.tree.TypeTree
64
+ import sun.reflect.generics.tree.TypeVariableSignature
65
+ import sun.reflect.generics.tree.Wildcard
51
66
import java.io.File
67
+ import java.lang.reflect.Constructor
68
+ import java.lang.reflect.GenericSignatureFormatError
69
+ import java.lang.reflect.Method
52
70
import java.lang.reflect.Modifier
53
71
import kotlin.contracts.ExperimentalContracts
54
72
import kotlin.contracts.contract
55
73
import kotlin.jvm.internal.CallableReference
56
74
import kotlin.reflect.KCallable
57
75
import kotlin.reflect.KClass
58
76
import kotlin.reflect.KFunction
77
+ import kotlin.reflect.KType
59
78
import kotlin.reflect.full.instanceParameter
60
79
import kotlin.reflect.jvm.javaConstructor
61
80
import kotlin.reflect.jvm.javaType
81
+ import kotlin.reflect.jvm.kotlinFunction
62
82
63
83
const val SYMBOLIC_NULL_ADDR : Int = 0
64
84
@@ -264,7 +284,11 @@ data class UtError(
264
284
*/
265
285
sealed class UtModel (
266
286
open val classId : ClassId
267
- )
287
+ ) {
288
+ open fun processGenerics (type : KType ) {}
289
+
290
+ open fun fillGenericsAsObjects () {}
291
+ }
268
292
269
293
/* *
270
294
* Class representing models for values that might have an address.
@@ -402,6 +426,10 @@ data class UtCompositeModel(
402
426
val fields : MutableMap <FieldId , UtModel > = mutableMapOf(),
403
427
val mocks : MutableMap <ExecutableId , List <UtModel >> = mutableMapOf(),
404
428
) : UtReferenceModel(id, classId) {
429
+ override fun processGenerics (type : KType ) {
430
+ classId.typeParameters.fromType(type)
431
+ }
432
+
405
433
// TODO: SAT-891 - rewrite toString() method
406
434
override fun toString () = withToStringThreadLocalReentrancyGuard {
407
435
buildString {
@@ -505,6 +533,151 @@ data class UtAssembleModel(
505
533
val finalInstantiationModel
506
534
get() = instantiationChain.lastOrNull()
507
535
536
+ private fun TypeTree.cmp (other : TypeTree ): Boolean {
537
+ if (this ::class != other::class ) return false
538
+
539
+ when (this ) {
540
+ is TypeVariableSignature -> return identifier == (other as TypeVariableSignature ).identifier
541
+ is ClassTypeSignature -> {
542
+ val otherPath = (other as ClassTypeSignature ).path
543
+ return path.foldIndexed(true ) { i, prev, it ->
544
+ prev && (otherPath.getOrNull(i)?.cmp(it) ? : false )
545
+ }
546
+ }
547
+ is SimpleClassTypeSignature -> {
548
+ val otherTypeArgs = (other as SimpleClassTypeSignature ).typeArguments
549
+ return typeArguments.foldIndexed(true ) { i, prev, it ->
550
+ prev && (otherTypeArgs.getOrNull(i)?.cmp(it) ? : false )
551
+ }
552
+ }
553
+ is ArrayTypeSignature -> return componentType.cmp((other as ArrayTypeSignature ).componentType)
554
+ is BottomSignature -> return true
555
+ is Wildcard -> return true
556
+ else -> return true
557
+ }
558
+ }
559
+
560
+ override fun processGenerics (type : KType ) {
561
+ classId.typeParameters.fromType(type)
562
+
563
+ // TODO might cause problems with type params when program synthesis comes
564
+ // assume that last statement is constructor call
565
+ instantiationChain.lastOrNull()?.let inst@ { lastStatement ->
566
+ (lastStatement as ? UtExecutableCallModel )?.let {
567
+ when (val executable = it.executable) {
568
+ is ConstructorId -> executable.classId.typeParameters.copyFromClassId(classId)
569
+ is MethodId -> executable.returnType.typeParameters.copyFromClassId(classId)
570
+ }
571
+
572
+ try {
573
+ val function = when (val executable = it.executable.executable) {
574
+ is Constructor <* > -> executable.kotlinFunction
575
+ is Method -> executable.kotlinFunction
576
+ else -> unreachableBranch(" this executable does not exist $executable " )
577
+ }
578
+
579
+ it.params.mapIndexed { i, param ->
580
+ function?.parameters?.getOrNull(i)?.type?.let { it1 -> param.processGenerics(it1) }
581
+ }
582
+ } catch (e: Error ) {
583
+ // KotlinReflectionInternalError can't be imported, but it is assumed here
584
+ // it can be thrown here because, i.e., Int(Int) constructor does not exist in Kotlin
585
+ }
586
+
587
+ workaround(WorkaroundReason .COLLECTION_CONSTRUCTOR_FROM_COLLECTION ) {
588
+ val propagateFromReturnTypeToParameter = { id: Int ->
589
+ ((it.params[id] as ? UtAssembleModel )?.instantiationChain?.get(0 ) as ? UtExecutableCallModel )
590
+ ?.executable?.classId?.typeParameters?.copyFromClassId(classId)
591
+ }
592
+
593
+ when (val executable = it.executable.executable) {
594
+ is Constructor <* > -> {
595
+ // Can't parse signature here, since constructors return void
596
+ // This part only works for cases like Collection<T>(collection: Collection<T>)
597
+ if (it.executable is ConstructorId ) {
598
+ if (it.executable.classId.isSubtypeOf(Collection ::class .id)) {
599
+ if (it.executable.parameters.size == 1 && it.executable.parameters[0 ].isSubtypeOf(Collection ::class .id)) {
600
+ propagateFromReturnTypeToParameter(0 )
601
+ }
602
+ }
603
+ }
604
+ }
605
+ is Method -> {
606
+ try {
607
+ val f = Method ::class .java.getDeclaredField(" signature" )
608
+ f.isAccessible = true
609
+ val signature = f.get(executable) as ? String ? : return @inst
610
+ val parsedSignature = SignatureParser .make().parseMethodSig(signature)
611
+
612
+ // check if parameter types are equal to return types
613
+ // e.g. <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;)Ljava/util/List<TT;>;
614
+ parsedSignature.parameterTypes.forEachIndexed { paramId, param ->
615
+ if (param::class != parsedSignature.returnType::class ) return @forEachIndexed
616
+
617
+ param as ? TypeArgument ? : error(" Only TypeArgument is expected" )
618
+ parsedSignature as ? TypeArgument ? : error(" Only TypeArgument is expected" )
619
+ if (param.cmp(parsedSignature)) {
620
+ propagateFromReturnTypeToParameter(paramId)
621
+ }
622
+ }
623
+ } catch (e: GenericSignatureFormatError ) {
624
+ // TODO log
625
+ }
626
+ }
627
+ else -> unreachableBranch(" this executable does not exist $executable " )
628
+ }
629
+ }
630
+ }
631
+ }
632
+
633
+ for (model in modificationsChain) {
634
+ if (model is UtExecutableCallModel ) {
635
+ model.params.mapIndexed { i, param ->
636
+ type.arguments.getOrNull(i)?.type?.let { it1 -> param.processGenerics(it1) }
637
+ }
638
+ }
639
+ }
640
+ }
641
+
642
+ override fun fillGenericsAsObjects () {
643
+ // TODO might cause problems with type params when program synthesis comes
644
+ // assume that last statement is constructor call
645
+ instantiationChain.lastOrNull()?.let { lastStatement ->
646
+ (lastStatement as ? UtExecutableCallModel )?.let {
647
+ try {
648
+ val function = when (val executable = it.executable.executable) {
649
+ is Constructor <* > -> executable.kotlinFunction
650
+ is Method -> executable.kotlinFunction
651
+ else -> unreachableBranch(" this executable does not exist $executable " )
652
+ }
653
+ function?.let { f ->
654
+ classId.typeParameters.parameters = List (f.typeParameters.size) { objectClassId }
655
+ }
656
+
657
+ it.params.map { param -> param.fillGenericsAsObjects() }
658
+ } catch (e: Error ) {
659
+ // KotlinReflectionInternalError can't be imported, but it is assumed here
660
+ // it can be thrown here because, i.e., Int(Int) constructor does not exist in Kotlin
661
+ }
662
+ }
663
+ }
664
+
665
+ for (model in modificationsChain) {
666
+ if (model is UtExecutableCallModel ) {
667
+ val function = when (val executable = model.executable.executable) {
668
+ is Constructor <* > -> executable.kotlinFunction
669
+ is Method -> executable.kotlinFunction
670
+ else -> unreachableBranch(" this executable does not exist $executable " )
671
+ }
672
+ function?.let { f ->
673
+ model.executable.classId.typeParameters.parameters = List (f.typeParameters.size) { objectClassId }
674
+ }
675
+
676
+ model.params.map { it.fillGenericsAsObjects() }
677
+ }
678
+ }
679
+ }
680
+
508
681
override fun toString () = withToStringThreadLocalReentrancyGuard {
509
682
buildString {
510
683
append(" UtAssembleModel(${classId.simpleName} $modelName ) " )
@@ -760,8 +933,7 @@ open class ClassId @JvmOverloads constructor(
760
933
open val allConstructors: Sequence <ConstructorId >
761
934
get() = jClass.declaredConstructors.asSequence().map { it.executableId }
762
935
763
- open val typeParameters: TypeParameters
764
- get() = TypeParameters ()
936
+ open val typeParameters: TypeParameters = TypeParameters ()
765
937
766
938
open val outerClass: Class <* >?
767
939
get() = jClass.enclosingClass
@@ -904,6 +1076,19 @@ open class FieldId(val declaringClass: ClassId, val name: String) {
904
1076
open val type: ClassId
905
1077
get() = strategy.type
906
1078
1079
+ // required to store and update type parameters
1080
+ // TODO check if by lazy works correctly in newer Kotlin (https://stackoverflow.com/questions/47638464/kotlin-lazy-var-throwing-classcastexception-kotlin-uninitialized-value)
1081
+ // val fixedType: ClassId by lazy { type }
1082
+ private var hiddenFixedType: ClassId ? = null
1083
+
1084
+ val fixedType: ClassId
1085
+ get() {
1086
+ if (hiddenFixedType == null ) {
1087
+ hiddenFixedType = type
1088
+ }
1089
+ return hiddenFixedType!!
1090
+ }
1091
+
907
1092
override fun equals (other : Any? ): Boolean {
908
1093
if (this == = other) return true
909
1094
if (javaClass != other?.javaClass) return false
@@ -1064,9 +1249,31 @@ class BuiltinMethodId(
1064
1249
override val isPrivate : Boolean = false
1065
1250
) : MethodId(classId, name, returnType, parameters)
1066
1251
1067
- open class TypeParameters (val parameters : List <ClassId > = emptyList())
1252
+ open class TypeParameters (var parameters : List <ClassId > = emptyList()) {
1253
+ fun copyFromClassId (classId : ClassId ) {
1254
+ parameters = classId.typeParameters.parameters
1255
+ }
1256
+
1257
+ fun fromType (type : KType ) {
1258
+ if (type.arguments.isEmpty()) return
1259
+
1260
+ parameters = type.arguments.map {
1261
+ when (val clazz = it.type?.classifier) {
1262
+ is KClass <* > -> {
1263
+ val classId = clazz.id
1264
+ it.type?.let { t ->
1265
+ classId.typeParameters.fromType(t)
1266
+ } ? : error(" " )
1267
+
1268
+ classId
1269
+ }
1270
+ else -> objectClassId
1271
+ }
1272
+ }
1273
+ }
1274
+ }
1068
1275
1069
- class WildcardTypeParameter : TypeParameters (emptyList() )
1276
+ class WildcardTypeParameter : ClassId ( " org.utbot.framework.plugin.api.WildcardTypeParameter " )
1070
1277
1071
1278
interface CodeGenerationSettingItem {
1072
1279
val displayName: String
0 commit comments