Roel Meeuws
Roel Meeuws

Reputation: 43

Can bytebuddy intercept a method call and make it call a private method of a second object?

Given a class A with a public method overrideMe(B that, C arg) and a second class B with a private method callMe(C arg). Is it possible to generate a subclass with bytebuddy that will invoke the private method callMe(C arg) from the implemented overrideMe(B that, C arg)? This basically would mimic the JVM reflection GeneratedMethodAccessor which does exactly that.

Currently, I am trying to use a MethodCall like this:

Method callMeHandler = B.class.getDeclaredMethod("callMe");
callMeHandler.setAccessible(true);
MethodCall methodCall = MethodCall.invoke(callMeHandler).onArgument(0);
if(callMeHandler.getParameterCount() == 1)
    methodCall = methodCall.withArgument(1);

Composable coercedMethodCall = methodCall
           .withAssigner(ReferenceTypeAwareAssigner.INSTANCE, Typing.DYNAMIC);

String className = callMeHandler.getDeclaringClass().getName()
       + "$_generatedAccessor$" + callMeHandler.getName()
       + "$" + accessorCounter.getAndIncrement();

new ByteBuddy().subclass(GeneratableHandlerInvocation.class)
               .name(className)
               .method(ElementMatchers.named("invoke"))
               .intercept(Advice.to(GeneratableHandlerInvocation.class)
                                .wrap(coercedMethodCall))
               .make()
               .load(callMeHandler.getDeclaringClass().getClassLoader())
               .getLoaded()
               .newInstance();

, but it complains with

java.lang.IllegalStateException: Cannot invoke private void com.mycompany.secret.B.callMe() virtually
    at net.bytebuddy.implementation.MethodCall$MethodInvoker$ForVirtualInvocation$WithImplicitType.invoke(MethodCall.java:2124)
    at net.bytebuddy.implementation.MethodCall$Appender.apply(MethodCall.java:2400)
    at net.bytebuddy.asm.Advice$Appender$EmulatingMethodVisitor.resolve(Advice.java:7156)
    at net.bytebuddy.asm.Advice$Appender.apply(Advice.java:7109)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:614)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:603)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:521)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:4102)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1612)
    at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:174)
    at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:155)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:2560)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2662)
    at com.mycompany.secret.Main.defineTheSpecialClass(Main.java:190)

Upvotes: 1

Views: 1050

Answers (1)

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44067

No, this is impossible. The JVM's byte code validator forbids it. Therefore, Byte Buddy does not allow you to create such a method call in the first place.

If you want to change a class and allow such an invocation, look into Byte Buddy's agent API and the Advice class which allows you to alter existing code.

Upvotes: 1

Related Questions