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