Moshi: ProGuard configuration for Kotlin 1.2

Created on 30 Nov 2017  路  17Comments  路  Source: square/moshi

We upgraded to Kotlin 1.2 but got warnings at compile time and crashes at runtime. This is a ProGuard config change makes the app working again.

-dontwarn kotlin.reflect.jvm.internal.**
-keep class kotlin.reflect.jvm.internal.** { *; }

Hesitant to submit a PR just yet because I didn't spend too much time to see if this is the minimum config to get it working with kotlin 1.2. The crashes were reported very late night so I just quickly made a hotfix.

Posted here in case someone else has this issue. Or someone is more familiar with the changes can shed some light on this. I might find some time tomorrow digging deeper into this.

The warnings were:

Warning: kotlin.reflect.jvm.internal.KClassImpl: can't find referenced class kotlin.reflect.jvm.internal.KClassImpl$kotlin.reflect.jvm.internal.KClassImpl$Data
Warning: kotlin.reflect.jvm.internal.KClassImpl: can't find referenced class kotlin.reflect.jvm.internal.KClassImpl$kotlin.reflect.jvm.internal.KClassImpl$Data
Warning: kotlin.reflect.jvm.internal.KClassImpl$data$1: can't find referenced class kotlin.reflect.jvm.internal.KClassImpl$kotlin.reflect.jvm.internal.KClassImpl$Data
Warning: kotlin.reflect.jvm.internal.KClassImpl$data$1: can't find referenced class kotlin.reflect.jvm.internal.KClassImpl$kotlin.reflect.jvm.internal.KClassImpl$Data
Warning: kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap: can't find referenced class kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap$kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap$Entry
Warning: kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap: can't find referenced class kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap$kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap$EntrySet
Warning: kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap$Entry: can't find referenced class kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap$kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap$Entry
Warning: kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap$Entry: can't find referenced class kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap$kotlin.reflect.jvm.internal.impl.protobuf.SmallSortedMap$Entry

And the stacktrace for the crashes:

Fatal Exception: java.lang.NoClassDefFoundError: kotlin.reflect.jvm.internal.impl.builtins.b
       at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.createBuiltInsModule(KotlinBuiltIns.java:150)
       at kotlin.reflect.jvm.internal.impl.platform.JvmBuiltIns.(JvmBuiltIns.kt)
       at kotlin.reflect.jvm.internal.impl.platform.JvmBuiltIns.(JvmBuiltIns.kt)
       at kotlin.reflect.jvm.internal.impl.load.kotlin.reflect.RuntimeModuleData$Companion.create(RuntimeModuleData.kt:54)
       at kotlin.reflect.jvm.internal.ModuleByClassLoaderKt.getOrCreateModule(moduleByClassLoader.kt:58)
       at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data$moduleData$2.invoke(KDeclarationContainerImpl.kt:35)
       at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data$moduleData$2.invoke(KDeclarationContainerImpl.kt:32)
       at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
       at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
       at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data.getModuleData(KDeclarationContainerImpl.kt)
       at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:46)
       at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:43)
       at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
       at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
       at kotlin.reflect.jvm.internal.KClassImpl$Data.getDescriptor(KClassImpl.kt)
       at kotlin.reflect.jvm.internal.KClassImpl.getDescriptor(KClassImpl.kt:172)
       at kotlin.reflect.jvm.internal.KClassImpl.getConstructorDescriptors(KClassImpl.kt:186)
       at kotlin.reflect.jvm.internal.KClassImpl$Data$constructors$2.invoke(KClassImpl.kt:90)
       at kotlin.reflect.jvm.internal.KClassImpl$Data$constructors$2.invoke(KClassImpl.kt:43)
       at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
       at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
       at kotlin.reflect.jvm.internal.KClassImpl$Data.getConstructors(KClassImpl.kt)
       at kotlin.reflect.jvm.internal.KClassImpl.getConstructors(KClassImpl.kt:222)
       at kotlin.reflect.full.KClasses.getPrimaryConstructor(KClasses.kt:40)
       at com.squareup.moshi.KotlinJsonAdapterFactory.create(KotlinJsonAdapter.kt:160)
       at com.squareup.moshi.Moshi.adapter(Moshi.java:100)
       at retrofit2.converter.moshi.MoshiConverterFactory.responseBodyConverter(MoshiConverterFactory.java:91)
       at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:330)
       at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:313)
       at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:736)
       at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:169)
       at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
       at retrofit2.Retrofit$1.invoke(Retrofit.java:147)

and,

Caused by java.util.NoSuchElementException: Collection is empty.
       at kotlin.collections.CollectionsKt___CollectionsKt.first(_Collections.kt:166)
       at kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader$Companion.(BuiltInsLoader.kt)
       at kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader.(Unknown Source)
       at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.createBuiltInsModule(KotlinBuiltIns.java:150)
       at kotlin.reflect.jvm.internal.impl.platform.JvmBuiltIns.(JvmBuiltIns.kt)
       at kotlin.reflect.jvm.internal.impl.platform.JvmBuiltIns.(JvmBuiltIns.kt)
       at kotlin.reflect.jvm.internal.impl.load.kotlin.reflect.RuntimeModuleData$Companion.create(RuntimeModuleData.kt:54)
       at kotlin.reflect.jvm.internal.ModuleByClassLoaderKt.getOrCreateModule(moduleByClassLoader.kt:58)
       at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data$moduleData$2.invoke(KDeclarationContainerImpl.kt:35)
       at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data$moduleData$2.invoke(KDeclarationContainerImpl.kt:32)
       at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
       at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
       at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data.getModuleData(Unknown Source:7)
       at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:46)
       at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:43)
       at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
       at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
       at kotlin.reflect.jvm.internal.KClassImpl$Data.getDescriptor(Unknown Source:7)
       at kotlin.reflect.jvm.internal.KClassImpl.getDescriptor(KClassImpl.kt:172)
       at kotlin.reflect.jvm.internal.KClassImpl.getConstructorDescriptors(KClassImpl.kt:186)
       at kotlin.reflect.jvm.internal.KClassImpl$Data$constructors$2.invoke(KClassImpl.kt:90)
       at kotlin.reflect.jvm.internal.KClassImpl$Data$constructors$2.invoke(KClassImpl.kt:43)
       at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93)
       at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32)
       at kotlin.reflect.jvm.internal.KClassImpl$Data.getConstructors(Unknown Source:7)
       at kotlin.reflect.jvm.internal.KClassImpl.getConstructors(KClassImpl.kt:222)
       at kotlin.reflect.full.KClasses.getPrimaryConstructor(KClasses.kt:40)
       at com.squareup.moshi.KotlinJsonAdapterFactory.create(KotlinJsonAdapter.kt:160)
       at com.squareup.moshi.Moshi.adapter(Moshi.java:100)
       at com.squareup.moshi.Moshi.adapter(Moshi.java:58)
       at com.squareup.moshi.CollectionJsonAdapter.newArrayListAdapter(CollectionJsonAdapter.java:52)
       at com.squareup.moshi.CollectionJsonAdapter$1.create(CollectionJsonAdapter.java:36)
       at com.squareup.moshi.Moshi.adapter(Moshi.java:100)
       at com.squareup.moshi.Moshi.adapter(Moshi.java:58)
       at com.tinder.api.model.common.AutoValue_ApiMatch$MoshiJsonAdapter.(AutoValue_ApiMatch.java)
       at com.tinder.api.model.common.ApiMatch.jsonAdapter(ApiMatch.java:19)
       at com.tinder.api.moshi.AutoValueMoshi_TinderMoshiAdapterFactory.create(AutoValueMoshi_TinderMoshiAdapterFactory.java:220)
       at com.squareup.moshi.Moshi.adapter(Moshi.java:100)
       at com.squareup.moshi.Moshi.adapter(Moshi.java:58)
       at com.squareup.moshi.CollectionJsonAdapter.newArrayListAdapter(CollectionJsonAdapter.java:52)
       at com.squareup.moshi.CollectionJsonAdapter$1.create(CollectionJsonAdapter.java:36)
       at com.squareup.moshi.Moshi.adapter(Moshi.java:100)
       at com.squareup.moshi.Moshi.adapter(Moshi.java:58)
       at com.tinder.api.model.updates.AutoValue_Updates$MoshiJsonAdapter.(AutoValue_Updates.java)
       at com.tinder.api.model.updates.Updates.jsonAdapter(Updates.java:20)
       at com.tinder.api.moshi.AutoValueMoshi_TinderMoshiAdapterFactory.create(AutoValueMoshi_TinderMoshiAdapterFactory.java:170)
       at com.squareup.moshi.Moshi.adapter(Moshi.java:100)
       at retrofit2.converter.moshi.MoshiConverterFactory.responseBodyConverter(MoshiConverterFactory.java:91)
       at retrofit2.Retrofit.nextResponseBodyConverter(Retrofit.java:330)
       at retrofit2.Retrofit.responseBodyConverter(Retrofit.java:313)
       at retrofit2.ServiceMethod$Builder.createResponseConverter(ServiceMethod.java:736)
       at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:169)
       at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:170)
       at retrofit2.Retrofit$1.invoke(Retrofit.java:147)
       at java.lang.reflect.Proxy.invoke(Proxy.java:913)
ProGuard needs info

Most helpful comment

Finally got some time looking a little deeper into this.

The ProGuard warning actually wasn't coming from the usage of kotlin reflect in moshi kotlin, but rather kotlin reflect 1.2. We explicitly added kotlin reflect 1.2 because of the Runtime JAR files in the classpath should have the same version warning. Though, the kotlin reflect is only used in moshi kotlin, so we can just ignore the warning?

Now the NoClassDefFoundError crashes may just be API changes in 1.2. And the following NoSuchElementException could just be the cascading effect of the NoClassDefFoundError? Played around with different ProGuard configurations and it looks like -keep public class kotlin.reflect.jvm.internal.impl.builtins.* { public *; } is the minimum config to get the app working.

So maybe a PR to update README on ProGuard configuration with kotlin reflect 1.2?

All 17 comments

Finally got some time looking a little deeper into this.

The ProGuard warning actually wasn't coming from the usage of kotlin reflect in moshi kotlin, but rather kotlin reflect 1.2. We explicitly added kotlin reflect 1.2 because of the Runtime JAR files in the classpath should have the same version warning. Though, the kotlin reflect is only used in moshi kotlin, so we can just ignore the warning?

Now the NoClassDefFoundError crashes may just be API changes in 1.2. And the following NoSuchElementException could just be the cascading effect of the NoClassDefFoundError? Played around with different ProGuard configurations and it looks like -keep public class kotlin.reflect.jvm.internal.impl.builtins.* { public *; } is the minimum config to get the app working.

So maybe a PR to update README on ProGuard configuration with kotlin reflect 1.2?

exactly same problem here. I just ignore the warning because if i explicitly add 1.2, it breaks my release build with those warnings.

FWIW - I got a similar trace from running the lint driver in android tools from the CLI. Posted to lint-dev, maybe Tor can shed more light - https://groups.google.com/forum/#!topic/lint-dev/70j4pQ8uMuA

Same crash here. Does anybody have any idea why explicitly adding kotlin-reflect as a dependency causes this crash?

Same issue, have any update info about it?

Same issue here. Watching on this.

i don't have an update on this proguard issue per se. but we are in the process of migrating reflection based moshi kotlin to the codegen. haven't had any horror proguard stories keep me awake at night.

The minimum config is

-keep class kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoaderImpl

which will keep only the no-arg constructor of the service defined in META-INF/services/kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader. Anything more is keeping too much.

I might missed something, but there is no kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoaderImpl
full name is kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl

and still, this results in various errors of missing FieldOverridabilityCondition, etc

I had the same problem with Kotlin1.3, possible solution is to add this rule.
-keep interface kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader

This seems to work (kotlin 1.2.71)

-keep interface kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader
-keep class kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl

This work for me (kotlin 1.3.10)

-keep interface kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader
-keep class kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl

I might missed something, but there is no kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoaderImpl
full name is kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl

and still, this results in various errors of missing FieldOverridabilityCondition, etc

same error

-ignorewarnings?

@androidovshchik no because your code will crash in the runtime :)

As of latest kotlin-stdlib (right now 1.3.41 but still uses kotlin-reflect 1.2.71) _I think_ the minimum configuration should change to the following:

-keep class kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader
-keep class kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl
-keep class kotlin.reflect.jvm.internal.impl.load.java.FieldOverridabilityCondition
-keep class kotlin.reflect.jvm.internal.impl.load.java.ErasedOverridabilityCondition
-keep class kotlin.reflect.jvm.internal.impl.load.java.JavaIncompatibilityRulesOverridabilityCondition

I still don't understand why the first rule (with the explicit path) works but not this one:
-keep class kotlin.reflect.jvm.internal.impl.builtins. BuiltInsLoaderImpl
which is in the META-INF/services 馃

And as in #880 this:~

-keep class kotlin.Metadata

I think this is something that should be handled in kotlin-reflect's artifact directly, as manually having to figure out which internal APIs of it to keep are just going to potentially break again between kotlin versions (regardless of Moshi version). Would welcome a link to a youtrack issue for Kotlin's issue tracker here.

After talking with Jetbrains on the kotlin-lang slack, I've opened a PR to add embedded proguard rules to kotlin-reflect here: https://github.com/JetBrains/kotlin/pull/2893

Please feel free to chime in with any suggestions!

Was this page helpful?
0 / 5 - 0 ratings