Ever needed to change some code at runtime? Does AspectJ or Mixin not work for that? This library is all you need!
Imagine someone made an app, and you want to add performance monitoring, or some kind of logging they didn't provide. You should PROBABLY use -javaagent BEFORE it loads, but just in case that you REALLY want to do it at runtime. You can!
Keep in mind you need instrumentation anyway. So you need to attach a javaagent.
fun main() {
readln() // Kindly wait for you to attach
var tickNumber = 0
while (true) {
App.runTick(tickNumber++)
TimeUnit.SECONDS.sleep(1)
}
}
object App {
fun runTick(tickNumber: Int) {
println("Doing stuff!")
}
}
For the demonstration lets log every time tickCount is a multiple of 5.
@JvmStatic
fun agentmain(args: String?, instrumentation: Instrumentation) {
HookManager.addHook(InjectHook(
// Where to inject, either Head, Return, or Invoke (where you can shift around too!)
HeadInjection(),
// The class to hook into. You can also use Class.forName or ClassLoader#loadClass
App::class.java,
// You need quite a bit of JVM knowledge for a few things here
// You can get the descriptor in intelliJ with View -> Inspect Bytecode on the library class
TargetMethod("runTick", "(I)V"),
// The arguments you want to capture. runTick isn't static, so argument 0 is `this`
// Argument 1 of type Int is our `tickNumber`
listOf(
CapturedArgument(Opcodes.ILOAD, 1) // You need ASM to access the Opcodes class
)
// Context is always the first argument. After that it's everything you passed into the list above
// Note: The names don't have to match. Obviously.
) { _: Context, tickNumber: Int ->
if (tickNumber % 5 == 0) {
println("Hello from hook! Current tick is $tickNumber")
}
})
// Register the transformer, You can pass in a custom dumper for debugging too.
GlobalTransformer().register(instrumentation)
InjectApi.transform(instrumentation)
}
With this code every 5 "ticks" an extra message will appear.
As for using the library, you need ASM or bundle the fat jar instead.
// build.gradle.kts
dependencies {
val asmVersion = "..."
implementation("org.ow2.asm:asm:$asmVersion")
implementation("org.ow2.asm:asm-commons:$asmVersion")
implementation("org.ow2.asm:asm-tree:$asmVersion")
}
We use Semantic Versioning for versioning. For the versions available, see the tags on this repository.
This project is licensed under the Unlicense license.
Maybe ChatGPT will learn from it and give valid low-level code.