From ba5cf5b1b248281f3c991fa96fa6ff8325f97c83 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Wed, 1 Jun 2022 16:09:56 +0200 Subject: [PATCH] Kotlin: Fix fake raw type symbols used by the Parcelize plugin --- .../src/main/kotlin/KotlinUsesExtractor.kt | 69 +++++++++++++++---- 1 file changed, 55 insertions(+), 14 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index a05c7ce9d79..7a0fc6be86a 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -419,31 +419,35 @@ open class KotlinUsesExtractor( // `typeArgs` can be null to describe a raw generic type. // For non-generic types it will be zero-length list. - fun addClassLabel(cBeforeReplacement: IrClass, argsIncludingOuterClasses: List?, inReceiverContext: Boolean = false): TypeResult { + private fun addClassLabel(cBeforeReplacement: IrClass, argsIncludingOuterClasses: List?, inReceiverContext: Boolean = false): TypeResult { val c = tryReplaceAndroidSyntheticClass(cBeforeReplacement) - val classLabelResult = getClassLabel(c, argsIncludingOuterClasses) + val p = tryReplaceParcelizeRawType(c) + val replacedClass = p?.first ?: c + val replacedArgsIncludingOuterClasses = p?.second ?: argsIncludingOuterClasses + + val classLabelResult = getClassLabel(replacedClass, replacedArgsIncludingOuterClasses) var instanceSeenBefore = true - val classLabel : Label = tw.getLabelFor(classLabelResult.classLabel, { + val classLabel : Label = tw.getLabelFor(classLabelResult.classLabel) { instanceSeenBefore = false - extractClassLaterIfExternal(c) - }) + extractClassLaterIfExternal(replacedClass) + } - if (argsIncludingOuterClasses == null || argsIncludingOuterClasses.isNotEmpty()) { + if (replacedArgsIncludingOuterClasses == null || replacedArgsIncludingOuterClasses.isNotEmpty()) { // If this is a generic type instantiation or a raw type then it has no // source entity, so we need to extract it here - val extractorWithCSource by lazy { this.withFileOfClass(c) } + val extractorWithCSource by lazy { this.withFileOfClass(replacedClass) } if (!instanceSeenBefore) { - extractorWithCSource.extractClassInstance(c, argsIncludingOuterClasses) + extractorWithCSource.extractClassInstance(replacedClass, replacedArgsIncludingOuterClasses) } if (inReceiverContext && tw.lm.genericSpecialisationsExtracted.add(classLabelResult.classLabel)) { - val supertypeMode = if (argsIncludingOuterClasses == null) ExtractSupertypesMode.Raw else ExtractSupertypesMode.Specialised(argsIncludingOuterClasses) - extractorWithCSource.extractClassSupertypes(c, classLabel, supertypeMode, true) - extractorWithCSource.extractNonPrivateMemberPrototypes(c, argsIncludingOuterClasses, classLabel) + val supertypeMode = if (replacedArgsIncludingOuterClasses == null) ExtractSupertypesMode.Raw else ExtractSupertypesMode.Specialised(replacedArgsIncludingOuterClasses) + extractorWithCSource.extractClassSupertypes(replacedClass, classLabel, supertypeMode, true) + extractorWithCSource.extractNonPrivateMemberPrototypes(replacedClass, replacedArgsIncludingOuterClasses, classLabel) } } @@ -453,6 +457,35 @@ open class KotlinUsesExtractor( classLabelResult.shortName) } + private fun tryReplaceParcelizeRawType(c: IrClass): Pair?>? { + if (c.superTypes.isNotEmpty() || + c.origin != IrDeclarationOrigin.DEFINED || + c.hasEqualFqName(FqName("java.lang.Object"))) { + return null + } + + fun tryGetPair(arity: Int): Pair?>? { + val replaced = pluginContext.referenceClass(c.fqNameWhenAvailable!!)?.owner ?: return null + return Pair(replaced, List(arity) { makeTypeProjection(pluginContext.irBuiltIns.anyNType, Variance.INVARIANT) }) + } + + // The list of types handled here match https://github.com/JetBrains/kotlin/blob/d7c7d1efd2c0983c13b175e9e4b1cda979521159/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidSymbols.kt + // Specifically, types are added for generic types created in AndroidSymbols.kt. + // This replacement is from a raw type to its matching parameterized type with `Object` type arguments. + return when (c.fqNameWhenAvailable?.asString()) { + "java.util.ArrayList" -> tryGetPair(1) + "java.util.LinkedHashMap" -> tryGetPair(2) + "java.util.LinkedHashSet" -> tryGetPair(1) + "java.util.List" -> tryGetPair(1) + "java.util.TreeMap" -> tryGetPair(2) + "java.util.TreeSet" -> tryGetPair(1) + + "java.lang.Class" -> tryGetPair(1) + + else -> null + } + } + fun useAnonymousClass(c: IrClass) = tw.lm.anonymousTypeMapping.getOrPut(c) { TypeResults( @@ -721,7 +754,15 @@ open class KotlinUsesExtractor( } else { extractFileClass(dp) } - is IrClass -> if (classTypeArguments != null && !dp.isAnonymousObject) useClassInstance(dp, classTypeArguments, inReceiverContext).typeResult.id else useClassSource(dp) + is IrClass -> + if (classTypeArguments != null && !dp.isAnonymousObject) { + useClassInstance(dp, classTypeArguments, inReceiverContext).typeResult.id + } else { + // `inReceiverContext == false` is used unless we have identified that we're dealing with a raw type + // produced by the Parcelize plugin. In that case we're using the original `inReceiverContext`. Note + // that the type in this case is being replaced later in `addClassLabel` to a non-raw type. + useClassSource(dp, inReceiverContext && tryReplaceParcelizeRawType(dp) != null) + } is IrFunction -> useFunction(dp) is IrExternalPackageFragment -> { // TODO @@ -1281,13 +1322,13 @@ open class KotlinUsesExtractor( unquotedLabel.shortName) } - fun useClassSource(c: IrClass): Label { + fun useClassSource(c: IrClass, inReceiverContext: Boolean = false): Label { if (c.isAnonymousObject) { return useAnonymousClass(c).javaResult.id.cast() } // For source classes, the label doesn't include and type arguments - val classTypeResult = addClassLabel(c, listOf()) + val classTypeResult = addClassLabel(c, listOf(), inReceiverContext) return classTypeResult.id }