Search code examples
javabyte-buddy

add behavior to method with ByteBuddy


Given Sample.someMethod(...) and InvocationHandler decoration, I want to create a dynamic subclass that overrides someMethod with

someMethod(...) {
  decoration.invoke(...);
  super.someMethod(...);
}

My code looks like this:

    Class<? extends Sample> dynamicSubclass = new ByteBuddy()
        .subclass(Sample.class)
        .method(ElementMatchers.named("someMethod"))
            .intercept(new Implementation.Compound(
                InvocationHandlerAdapter.of(decoration),
                SuperMethodCall.INSTANCE))
        .make()
        .load(Sample.class.getClassLoader())  // line 42
        .getLoaded();

...and it results in the following exception:

java.lang.VerifyError: Expecting a stack map frame
Exception Details:
  Location:
    pl/morgwai/sample/pojo/ByteBuddySample$Sample$ByteBuddy$PXUw8Sz7.someMethod(I)Ljava/lang/String; @27: aload_0
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: b200 0a2a b200 0e04 bd00 1059 031b b800
    0000010: 1653 b900 1c04 00c0 001e b02a 1bb7 0020
    0000020: b0                                     

    at java.base/java.lang.Class.getDeclaredFields0(Native Method)
    at java.base/java.lang.Class.privateGetDeclaredFields(Class.java:3061)
    at java.base/java.lang.Class.getDeclaredField(Class.java:2409)
    at net.bytebuddy.implementation.LoadedTypeInitializer$ForStaticField.onLoad(LoadedTypeInitializer.java:122)
    at net.bytebuddy.implementation.LoadedTypeInitializer$Compound.onLoad(LoadedTypeInitializer.java:192)
    at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:102)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6292)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6281)
    at pl.morgwai.sample.pojo.ByteBuddySample.main(ByteBuddySample.java:42)

What am I doing wrong and how to fix it?
(I'm using byte-buddy 1.10.22 and openjdk-11)

Thanks!

UPDATE:

I'm able to achieve

someMethod(...) {
  decoration.invoke(...);
}

and

someMethod(...) {
  super.someMethod(...);
}

by doing .intercept(InvocationHandlerAdapter.of(decoration)) and .intercept(SuperMethodCall.INSTANCE) respectively, so it seems, I'm not using Implementation.Compound properly...

UPDATE-2:

I'm also able to achieve

someMethod(...) {
  super.someMethod(...);
  decoration.invoke(...);
}

by

.intercept(SuperMethodCall.INSTANCE.andThen(InvocationHandlerAdapter.of(decoration)))

but I can't do the other way around to achieve the order I need as InvocationHandlerAdapter does not implement Implementation.Composable...


Solution

  • As kindly explained by Rafael Winterhalter it's an unintended omission and in the next release InvocationHandlerAdapter will be implementing Composable (commit already pushed) and then the simplest way will be to do

    .intercept(InvocationHandlerAdapter.of(decoration).andThen(SuperMethodCall.INSTANCE))