Kotlin Reflection, Shadow Jars & `minimize`

Jason Dusek
The Lyf So Short
Published in
2 min readSep 15, 2019

--

Kotlin’s reflection support uses a dynamic strategy and so interacts poorly with dependency optimizers like the shadow Gradle plugin’s minimize. To make sure it gets included in your optimized release build, you can take a shortcut and exclude all Kotlin standard library dependencies from minimization:

Snippet in Gradle’s Kotlin DSL

John Rengelman’s shadow plugin for Gradle is a utility for building all-in-one application bundles called variously uber JARs or fat JARs. The plugin handles a variety of complications that arise when a JAR contains all of its dependencies:

  • The name of dependencies of dependencies might be the same, because they are different version of the same dependency. A related problem is that the names of the dependencies are liable to conflict with names in an application that wants to include the all-in-one JAR. One way to resolve that is renaming the dependencies with obscure, machine generated names — shading them. Hence shadow plugin.
  • Another important issue is that the all-in-one JAR is too big. It might use just one class each from a few dependencies. Naively including the whole dependency could make the JAR ten times larger than it needs to be. Retaining only the necessary dependencies requires some “tree-shaking” — tracing the call tree to find all the code that is actually used and then removing all the rest — and that is what the minimize utility in the shadow plugin provides.

Here is where we get to the conflict with kotlin-reflect and other runtime support packages. The calling program doesn’t reference most of the features of kotlin-reflect that it uses, because they are accessed from behind a facade — kotlin.jvm.internal.Reflection — that has default implementations. The Kotlin standard library loads Kotlin reflection at runtime if it is available and supplies a default implementation that throws an exception on advanced features when it’s not. The dynamic loading portion is reproduced below:

Excerpt from Reflection.java

This is not the first time or the last time one is likely to see this fairly useful pattern followed by advanced Kotlin/JVM libraries. When using SLF4J (Simple Logging Facade for Java) or JSR-223 (Scripting for the Java Platform) the same issues arise. The terms facade and service provider are a clue that dependencies which work fine from the IDE are liable to be stripped away by the dependency optimizer. For Gradle’s shadow plugin, the mitigation is to configure them as exclusions in the minimize block.

--

--