@@ -10,12 +10,14 @@ import org.jetbrains.kotlin.backend.jvm.ir.getJvmNameFromAnnotation
1010import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
1111import org.jetbrains.kotlin.builtins.StandardNames
1212import org.jetbrains.kotlin.descriptors.*
13+ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
1314import org.jetbrains.kotlin.ir.declarations.*
1415import org.jetbrains.kotlin.ir.expressions.*
1516import org.jetbrains.kotlin.ir.symbols.*
1617import org.jetbrains.kotlin.ir.types.*
1718import org.jetbrains.kotlin.ir.types.impl.*
1819import org.jetbrains.kotlin.ir.util.*
20+ import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature
1921import org.jetbrains.kotlin.load.java.JvmAbi
2022import org.jetbrains.kotlin.name.FqName
2123import org.jetbrains.kotlin.name.Name
@@ -32,6 +34,17 @@ open class KotlinUsesExtractor(
3234 val pluginContext : IrPluginContext ,
3335 val globalExtensionState : KotlinExtractorGlobalState
3436) {
37+
38+ val javaLangObject by lazy {
39+ val result = pluginContext.referenceClass(FqName (" java.lang.Object" ))?.owner
40+ result?.let { extractExternalClassLater(it) }
41+ result
42+ }
43+
44+ val javaLangObjectType by lazy {
45+ javaLangObject?.typeWith()
46+ }
47+
3548 fun usePackage (pkg : String ): Label <out DbPackage > {
3649 return extractPackage(pkg)
3750 }
@@ -790,6 +803,52 @@ open class KotlinUsesExtractor(
790803 else -> null
791804 }
792805
806+ val javaUtilCollection by lazy {
807+ val result = pluginContext.referenceClass(FqName (" java.util.Collection" ))?.owner
808+ result?.let { extractExternalClassLater(it) }
809+ result
810+ }
811+
812+ val wildcardCollectionType by lazy {
813+ javaUtilCollection?.let {
814+ it.symbol.typeWithArguments(listOf (IrStarProjectionImpl ))
815+ }
816+ }
817+
818+ private fun makeCovariant (t : IrTypeArgument ) =
819+ t.typeOrNull?.let { makeTypeProjection(it, Variance .OUT_VARIANCE ) } ? : t
820+
821+ private fun makeArgumentsCovariant (t : IrType ) = (t as ? IrSimpleType )?.let {
822+ t.toBuilder().also { b -> b.arguments = b.arguments.map(this ::makeCovariant) }.buildSimpleType()
823+ } ? : t
824+
825+ fun eraseCollectionsMethodParameterType (t : IrType , collectionsMethodName : String , paramIdx : Int ) =
826+ when (collectionsMethodName) {
827+ " contains" , " remove" , " containsKey" , " containsValue" , " get" , " indexOf" , " lastIndexOf" -> javaLangObjectType
828+ " getOrDefault" -> if (paramIdx == 0 ) javaLangObjectType else null
829+ " containsAll" , " removeAll" , " retainAll" -> wildcardCollectionType
830+ // Kotlin defines these like addAll(Collection<E>); Java uses addAll(Collection<? extends E>)
831+ " putAll" , " addAll" -> makeArgumentsCovariant(t)
832+ else -> null
833+ } ? : t
834+
835+ private fun overridesFunctionDefinedOn (f : IrFunction , packageName : String , className : String ) =
836+ (f as ? IrSimpleFunction )?.let {
837+ it.overriddenSymbols.any { overridden ->
838+ overridden.owner.parentClassOrNull?.let { defnClass ->
839+ defnClass.name.asString() == className &&
840+ defnClass.packageFqName?.asString() == packageName
841+ } ? : false
842+ }
843+ } ? : false
844+
845+ @OptIn(ObsoleteDescriptorBasedAPI ::class )
846+ fun overridesCollectionsMethodWithAlteredParameterTypes (f : IrFunction ) =
847+ BuiltinMethodsWithSpecialGenericSignature .getOverriddenBuiltinFunctionWithErasedValueParametersInJava(f.descriptor) != null ||
848+ (f.name.asString() == " putAll" && overridesFunctionDefinedOn(f, " kotlin.collections" , " MutableMap" )) ||
849+ (f.name.asString() == " addAll" && overridesFunctionDefinedOn(f, " kotlin.collections" , " MutableCollection" )) ||
850+ (f.name.asString() == " addAll" && overridesFunctionDefinedOn(f, " kotlin.collections" , " MutableList" ))
851+
793852 /*
794853 * This is the normal getFunctionLabel function to use. If you want
795854 * to refer to the function in its source class then
@@ -811,8 +870,19 @@ open class KotlinUsesExtractor(
811870 * `java.lang.Throwable`, which isn't what we want. So we have to
812871 * allow it to be passed in.
813872 */
873+ @OptIn(ObsoleteDescriptorBasedAPI ::class )
814874 fun getFunctionLabel (f : IrFunction , maybeParentId : Label <out DbElement >? , classTypeArgsIncludingOuterClasses : List <IrTypeArgument >? ) =
815- getFunctionLabel(f.parent, maybeParentId, getFunctionShortName(f).nameInDB, f.valueParameters, f.returnType, f.extensionReceiverParameter, getFunctionTypeParameters(f), classTypeArgsIncludingOuterClasses)
875+ getFunctionLabel(
876+ f.parent,
877+ maybeParentId,
878+ getFunctionShortName(f).nameInDB,
879+ f.valueParameters,
880+ f.returnType,
881+ f.extensionReceiverParameter,
882+ getFunctionTypeParameters(f),
883+ classTypeArgsIncludingOuterClasses,
884+ overridesCollectionsMethodWithAlteredParameterTypes(f)
885+ )
816886
817887 /*
818888 * This function actually generates the label for a function.
@@ -838,6 +908,9 @@ open class KotlinUsesExtractor(
838908 functionTypeParameters : List <IrTypeParameter >,
839909 // The type arguments of enclosing classes of the function.
840910 classTypeArgsIncludingOuterClasses : List <IrTypeArgument >? ,
911+ // If true, this method implements a Java Collections interface (Collection, Map or List) and may need
912+ // parameter erasure to match the way this class will appear to an external consumer of the .class file.
913+ overridesCollectionsMethod : Boolean ,
841914 // The prefix used in the label. "callable", unless a property label is created, then it's "property".
842915 prefix : String = "callable"
843916 ): String {
@@ -856,14 +929,19 @@ open class KotlinUsesExtractor(
856929 enclosingClass?.let { notNullClass -> makeTypeGenericSubstitutionMap(notNullClass, notNullArgs) }
857930 }
858931 }
859- val getIdForFunctionLabel = { it: IrValueParameter ->
860- // Mimic the Java extractor's behaviour: functions with type parameters are named for their erased types;
932+ val getIdForFunctionLabel = { it: IndexedValue <IrValueParameter > ->
933+ // Kotlin rewrites certain Java collections types adding additional generic constraints-- for example,
934+ // Collection.remove(Object) because Collection.remove(Collection::E) in the Kotlin universe.
935+ // If this has happened, erase the type again to get the correct Java signature.
936+ val maybeAmendedForCollections = if (overridesCollectionsMethod) eraseCollectionsMethodParameterType(it.value.type, name, it.index) else it.value.type
937+ // Now substitute any class type parameters in:
938+ val maybeSubbed = maybeAmendedForCollections.substituteTypeAndArguments(substitutionMap, TypeContext .OTHER , pluginContext)
939+ // Finally, mimic the Java extractor's behaviour by naming functions with type parameters for their erased types;
861940 // those without type parameters are named for the generic type.
862- val maybeSubbed = it.type.substituteTypeAndArguments(substitutionMap, TypeContext .OTHER , pluginContext)
863941 val maybeErased = if (functionTypeParameters.isEmpty()) maybeSubbed else erase(maybeSubbed)
864942 " {${useType(maybeErased).javaResult.id} }"
865943 }
866- val paramTypeIds = allParams.joinToString(separator = " ," , transform = getIdForFunctionLabel)
944+ val paramTypeIds = allParams.withIndex(). joinToString(separator = " ," , transform = getIdForFunctionLabel)
867945 val labelReturnType =
868946 if (name == " <init>" )
869947 pluginContext.irBuiltIns.unitType
@@ -1325,7 +1403,7 @@ open class KotlinUsesExtractor(
13251403 val returnType = getter?.returnType ? : setter?.valueParameters?.singleOrNull()?.type ? : pluginContext.irBuiltIns.unitType
13261404 val typeParams = getFunctionTypeParameters(func)
13271405
1328- getFunctionLabel(p.parent, parentId, p.name.asString(), listOf (), returnType, ext, typeParams, classTypeArgsIncludingOuterClasses, " property" )
1406+ getFunctionLabel(p.parent, parentId, p.name.asString(), listOf (), returnType, ext, typeParams, classTypeArgsIncludingOuterClasses, overridesCollectionsMethod = false , prefix = " property" )
13291407 }
13301408 }
13311409
0 commit comments