简体   繁体   中英

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)

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM