Skip to content

Incompatibility between MethodDelegation signature and intercepted method in ByteBuddy #1859

@HXavier21

Description

@HXavier21

Description:

I am learning ByteBuddy and encountered an issue while trying to use MethodDelegation to intercept a method. Below is my full demo:

import net.bytebuddy.ByteBuddy
import net.bytebuddy.agent.ByteBuddyAgent
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy
import net.bytebuddy.implementation.MethodDelegation
import net.bytebuddy.implementation.bind.annotation.RuntimeType
import net.bytebuddy.implementation.bind.annotation.SuperCall
import net.bytebuddy.matcher.ElementMatchers
import java.util.concurrent.Callable

class InnerRequest {
    fun request(message: String) {
        println(message)
    }
}

class CustomRequest {
    fun request() {
        val message = "secret String"
        InnerRequest().request(message)
    }
}

class RequestInterceptor {
    companion object {
        @JvmStatic
        fun intercept(@RuntimeType message: String, @SuperCall zpuer: Callable<Any>) {
            println("Get secret message: $message")
            zpuer.call()
        }
    }
}

fun main() {
    ByteBuddyAgent.install()
    ByteBuddy()
        .redefine(InnerRequest::class.java)
        .method(ElementMatchers.named("request"))
        .intercept(MethodDelegation.to(RequestInterceptor::class.java))
        .make()
        .load(
            InnerRequest::class.java.classLoader,
            ClassReloadingStrategy.fromInstalledAgent()
        )

    CustomRequest().request()
}

When I run this code, I get the following error:

Exception in thread "main" java.lang.IllegalArgumentException: None of [public static final void RequestInterceptor.intercept(java.lang.String,java.util.concurrent.Callable)] allows for delegation from public final void InnerRequest.request(java.lang.String)

I understand that this error occurs because the interceptor method's signature does not match the intercepted method's signature.
To address this, I tried using Advice instead of MethodDelegation, and it worked perfectly. Here is the working version with Advice:

class RequestAdvice {
    companion object {
        @JvmStatic
        @Advice.OnMethodEnter
        fun enter(@Advice.Argument(0) message: String) {
            println("Get secret message: $message")
        }
    }
}

fun main() {
    ByteBuddyAgent.install()

    ByteBuddy()
        .redefine(InnerRequest::class.java)
        .visit(
            Advice.to(RequestAdvice::class.java)
                .on(ElementMatchers.named("request"))
        )
        .make()
        .load(
            InnerRequest::class.java.classLoader,
            ClassReloadingStrategy.fromInstalledAgent()
        )
    CustomRequest().request()
}

This approach works as expected, and the output is:

Get secret message: secret String
secret String

However, I want to achieve the same functionality using MethodDelegation instead of Advice. How can I modify my interceptor and MethodDelegation logic to make it work?


Additional Notes:

  1. I understand that the MethodDelegation signature must match the intercepted method's signature (including parameters, return type, and static/non-static nature).
  2. I want to keep the ability to access the method argument (message) and optionally call the original method (like @SuperCall does).

Any help would be greatly appreciated.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions