Kotlin IR: Unlocking Incredible Possibilities for Code Manipulation

I’m currently working on a pet project: a Kotlin assertion library designed to handle deep assertions over any object type. The goal is to write code that looks like this:

Kotlin
confirmThat { (1..3).toList() } deepMatches { listOf(1, 2, 3) }

To make this work, the library needs to convert arbitrary code into a structured matcher tree. Under the hood, the goal is to transform that simple list check into something like this:

Kotlin
confirmThat { (1..3).toList() } deepMatches {
    ListMatcher(
        ValueMatcher(1),
        ValueMatcher(2),
        ValueMatcher(3)
    )
}

To pull this off, I weighed two sophisticated technical paths:

Option 1: Runtime Bytecode Manipulation

The first option is to modify the compiled bytecode while the application is running using tools like Byte Buddy.

  • The Pro: It’s a standard way to handle introspection on the JVM without needing a custom compiler setup.
  • The Con: It has very limited capabilities because Kotlin-specific details — like null-safety metadata — are often erased or transformed once the code is compiled. Many things are just impossible — e.g. you cannot convert primitives (int) into objects (Int), which might make it impossible to replace int with IntMatcher.

Option 2: Kotlin Intermediate Representation (IR)

The more “hardcore” approach is hijacking the Kotlin compilation process itself. By using a Kotlin Compiler Plugin, I can intercept the IR (Intermediate Representation). This happens after the code is parsed but before it undergoes any “lowering” steps (converting high-level constructs into simpler ones) or gets turned into bytecode.

  • The Pro: This allows me to see the code in its purest form. I can generate highly efficient, type-safe matchers that are baked directly into the program.
  • The Con: It’s significantly more complex to implement since it requires working deep within the compiler’s internal mechanics.

My Take

I decided to go the “hardcore” route. I’ve been experimenting with Kotlin IR, and it works perfectly! It handles the primitive-to-object mapping and null-safety metadata with ease.

Stay tuned—I’ll be sharing more on how I actually implemented the IR transformer in my next post.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *