javabyte-buddy

ByteBuddy throws java.lang.IllegalStateException - Cannot locate field named name for class


I am trying to explore and understand the bytebuddy library to create advice for one of the method. But I am encountering an exception upfront as below which seems a bit weird as given below:

Exception in thread "main" java.lang.IllegalStateException: Cannot locate field named name for class org.purbarun.proxy.MyService$ByteBuddy$8XL9Iy3U
    at net.bytebuddy.asm.Advice$OffsetMapping$ForField$Unresolved.resolve(Advice.java:2534)
    at net.bytebuddy.asm.Advice$OffsetMapping$ForField.resolve(Advice.java:2459)
    at net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodEnter.doApply(Advice.java:9979)
    at net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodEnter$WithDiscardedEnterType.doApply(Advice.java:10081)
    at net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$ForMethodEnter.apply(Advice.java:9937)
    at net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$AdviceMethodInliner.visitMethod(Advice.java:9620)
    at net.bytebuddy.jar.asm.ClassReader.readMethod(ClassReader.java:1354)
    at net.bytebuddy.jar.asm.ClassReader.accept(ClassReader.java:745)
    at net.bytebuddy.utility.AsmClassReader$ForAsm.accept(AsmClassReader.java:225)
    at net.bytebuddy.asm.Advice$Dispatcher$Inlining$Resolved$AdviceMethodInliner.apply(Advice.java:9613)
    at net.bytebuddy.asm.Advice$AdviceVisitor.onAfterExceptionTable(Advice.java:11919)
    at net.bytebuddy.utility.visitor.ExceptionTableSensitiveMethodVisitor.considerEndOfExceptionTable(ExceptionTableSensitiveMethodVisitor.java:49)
    at net.bytebuddy.utility.visitor.ExceptionTableSensitiveMethodVisitor.visitVarInsn(ExceptionTableSensitiveMethodVisitor.java:113)
    at net.bytebuddy.implementation.bytecode.member.MethodVariableAccess$OffsetLoading.apply(MethodVariableAccess.java:354)
    at net.bytebuddy.implementation.bytecode.StackManipulation$Compound.apply(StackManipulation.java:243)
    at net.bytebuddy.implementation.SuperMethodCall$Appender.apply(SuperMethodCall.java:139)
    at net.bytebuddy.asm.Advice$Appender$EmulatingMethodVisitor.resolve(Advice.java:12525)
    at net.bytebuddy.asm.Advice$Appender.apply(Advice.java:12478)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:732)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:717)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:624)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:6072)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2246)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4085)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3769)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:4021)
    at org.purbarun.proxy.MainRunner.main(MainRunner.java:14)

My code as given below should be very simple and straightforward as I am quite new to this:

public class MyService {
    public void myMethod(String args) {
        System.out.println(args);
    }
}

public class MyServiceInterceptor {
    @OnMethodEnter
    static void enter(@This Object thisObject,
            @Origin String origin,
            @Origin("#t #m") String detaildOrigin,
            @AllArguments Object[] ary,
            @FieldValue(value = "name", readOnly = false) String nameField){
        System.out.println("test");
    }
}

public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException, SecurityException, InstantiationException, IllegalArgumentException {
        Class<? extends MyService> type=new ByteBuddy()
          .subclass(MyService.class)
          .method(ElementMatchers.isMethod()).intercept(Advice.to(MyServiceInterceptor.class))
          .make()
          .load(MyService.class.getClassLoader())
          .getLoaded();
        
        String returnVal = (String)type.getDeclaredMethod("myMethod", String.class).invoke(type.getDeclaredConstructor().newInstance(), "Method");
        System.out.println("return value: " + returnVal);
    }

Not sure if I am doing kind of silly mistakes which I am not able to get.

Can anyone from the community help me out to figure out the error.


Solution

  • If you don't have a name field in MyService simply remove the @FieldValue parameter from your advice:

    @OnMethodEnter
    static void enter(
            @This Object thisObject,
            @Origin String origin,
            @Origin("#t #m") String detaildOrigin,
            @AllArguments Object[] ary
            // Remove: @FieldValue(value = "name", readOnly = false) String nameField
    ) {
        System.out.println("test");
    }
    

    If you do plan to access a field from the original class via @FieldValue, ensure that the field exists in the class:

    public class MyService {
        public String name; // Must exist to be accessible with @FieldValue
        public void myMethod(String args) {
            System.out.println(args);
        }
    }