Have you ever found yourself in need to include a certain string or method name on your stack traces, for debugging or metrics?

The following snippet shows how easy that can be done with the javassist library. The snippet is written in Kotlin, but can be easily converted into Java or any other JVM language.

For the example, we want to see "iLikePie" as one of our method names. Here’s how it looks:

Your stack is:
java.lang.Exception: Stack trace
        at java.lang.Thread.dumpStack(Thread.java:1336)
        at Dynamic_framesKt$main$$inlined$withFrameNamed$1.get(dynamic-frames.kt:51)
        at dynamic.iLikePie.iLikePie(iLikePie.java)
        at dynamic.iLikePie.apply(iLikePie.java)
        at Dynamic_framesKt.main(dynamic-frames.kt:57)

…​and here’s the code to implement it.

#!/usr/bin/env kscript
//DEPS org.javassist:javassist:3.24.1-GA

import javassist.ClassPool
import javassist.CtMethod
import java.util.function.Function
import java.util.function.Supplier

val classPool = ClassPool.getDefault()
val generatedClasses: MutableMap<String, Function<Supplier<Any>, Any>> = mutableMapOf()

fun dynamicFrameNamed(name: String): Function<Supplier<Any>, Any> {
    val dynamicClass = classPool.makeClass("dynamic.$name").apply {
        addInterface(classPool.get("java.util.function.Function"))

        addMethod(CtMethod.make("""
            private Object $name(java.util.function.Supplier block) {
                return block.get();
            }
        """, this))

        addMethod(CtMethod.make("""
            public Object apply(Object o) {
                java.util.function.Supplier block = (java.util.function.Supplier) o;
                return $name(block);
            }
        """, this))

    }

    return dynamicClass.toClass().newInstance() as Function<Supplier<Any>, Any>
}

inline fun withFrameNamed(name: String, crossinline block: () -> Unit) {
    generatedClasses
        .getOrPut(name) { dynamicFrameNamed(name) }
        .apply(object : java.util.function.Supplier<Any> {
            override fun get(): Any {
                block()
                return ""
            }
        })
}

fun main(args: Array<String>) {
    withFrameNamed("iLikePie") {
        println("Your stack is:")
        Thread.dumpStack()
    }
}