7

I have a class with a companion object which implements a factory interface.

class GoalInspectorData(
    ...
) {

    companion object : DataClassFactory<GoalInspectorData> {

        override fun fromV8Object(v8Object: V8Object): GoalInspectorData {
            ...
        }
    }
}

I have some code which examines this class at runtime using reflection to see if the class provides a factory method. It does this by checking to see if the class has a companion object (companionObjectInstance) and, if so, if that companion object implements the factory interface.

internal inline fun <reified T> convert(obj: Any): T {

    val companionObject = T::class.companionObjectInstance

    @Suppress("UNCHECKED_CAST")
    return when {
        T::class in builtInClasses -> obj as T
        companionObject as? DataClassFactory<T> != null -> companionObject.fromV8Object(obj as V8Object)
        else -> throw IllegalArgumentException("No converter for type ${T::class}")
    }
}

This all works fine in a debug build.

It fails in a release build with R8 enabled (minifyEnabled true in build.gradle). It fails because companionObjectInstance returns null.

I'm using the don't optimize Proguard config:

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

and in my own proguard-rules.pro I've added just about every -keep rule I can imagine in an attempt to retain this companion object, and added @Keep annotations to everything, but nothing works. R8 is determined to strip it out.

For example:

-keep class my.package.** {
    *;
}
-keep interface my.package.** {
    *;
}

-if class **$Companion extends **
-keep class <2>
-if class **$Companion implements **
-keep class <2>

Are there any other -keep rules or configuration options which would instruct R8 to retain this companion object?

Graham Borland
  • 60,055
  • 21
  • 138
  • 179

1 Answers1

7

First of all, the keep rule

-keep class my.package.** {
    *;
}

should be sufficient to keep all the classes - including companion classes in your program. You should not need the -dontoptimize flag, so using the configuration proguard-android-optimize.txt should be fine.

However, as you use Kotlin reflection you probably also need to keep the annotation class kotlin.Metadata and runtime visible annotations with these rules:

-keep @interface kotlin.Metadata {
  *;
}
-keepattributes RuntimeVisibleAnnotations

If that does still not work could you please file an R8 issue? If you can include a simple reproduction that would be great.

sgjesse
  • 3,793
  • 14
  • 17