raphw / byte-buddy

Runtime code generation for the Java virtual machine.
https://bytebuddy.net
Apache License 2.0
6.28k stars 807 forks source link

net.bytebuddy.dynamic.scaffold.FieldLocator$ForClassHierarchy is thrown #1556

Closed dumakant closed 1 month ago

dumakant commented 1 year ago

We are using byte buddy version 1.14.4. I am trying to add fields dynamically and create getter and setter methods for those fields. Below is my code : ByteBuddy eventBeanByteBuddy = new ByteBuddy(); Builder eventBuilder = null; eventBuilder = eventBeanByteBuddy.subclass(BaseCustomEventDimension.class); eventBuilder.defineField(dimension.getTableName(), Integer.class, 1); // Define a getter method for the property eventBuilder = eventBuilder .defineMethod("get" + capitalize(dimension.getTableName()), Integer.class, 1) .intercept(FieldAccessor.ofField(dimension.getTableName()));

    // Define a setter method for the property
    eventBuilder = eventBuilder
        .defineMethod("set" + capitalize(dimension.getTableName()), void.class, 1)
        .withParameter(Integer.class)
        .intercept(FieldAccessor.ofField(dimension.getTableName()));

    final Object dimBean = eventBuilder.make()
                                        .load(Thread.currentThread().getContextClassLoader())
                                        .getLoaded().newInstance();

This code is throwing java.lang.IllegalStateException: Cannot resolve field for public java.lang.String com.plumtree.analytics.core.persist.BaseCustomEventDimension$ByteBuddy$yob7wDbB.getUSERID() using net.bytebuddy.dynamic.scaffold.FieldLocator$ForClassHierarchy@fc6fd349 at net.bytebuddy.implementation.FieldAccessor$FieldLocation$Relative$Prepared.resolve(FieldAccessor.java:311) at net.bytebuddy.implementation.FieldAccessor$ForImplicitProperty$Appender.apply(FieldAccessor.java:860) at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:730) at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:715) at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:622) at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:6043) at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2224) at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4050) at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3734) at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:3986)

I tried debugging, but could not find out the cause of this issue. Please help in resolving this issue.

raphw commented 1 year ago

Byte Buddy is immutable: You have to assign the defineField method back to the variable.

dumakant commented 1 year ago

Hi Rafael, Can you please explain again? I did not understand what exactly should be done? This is my code ByteBuddy eventBeanByteBuddy = new ByteBuddy(); Class beanClass = BaseCustomEventDimension.class; eventBuilder = eventBeanByteBuddy.subclass(BaseCustomEventDimension.class);

    eventBuilder.defineField(dimension.getTableName(), Integer.class, 1);

    // Define a getter method for the property
    eventBuilder = eventBuilder
        .defineMethod("get" + capitalize(dimension.getTableName()), Integer.class, 1)
        .intercept(FieldAccessor.ofField(dimension.getTableName()));

    // Define a setter method for the property
    eventBuilder = eventBuilder
        .defineMethod("set" + capitalize(dimension.getTableName()), void.class, 1)
        .withParameter(Integer.class)
        .intercept(FieldAccessor.ofField(dimension.getTableName()));

    final Object dimBean = eventBuilder.make()
                                        .load(Thread.currentThread().getContextClassLoader())
                                        .getLoaded().newInstance();
raphw commented 1 year ago
eventBuilder = eventBuilder.defineField(dimension.getTableName(), Integer.class, 1);

The first assignment is missing.

dumakant commented 1 year ago

After doing the assignment of the field as mentioned above, I have resolved the above exception. But now facing another issue as mentioned below when using defineField(). Also when I tried using defineProperty() which generates getter and setter automatically, even that code is giving below exception. Am I missing some configuration?

java.lang.IllegalStateException: Cannot resolve field for public java.lang.Integer com.plumtree.analytics.core.persist.BaseCustomEventDimension$ByteBuddy$bnEGAZIf.getASDIM_USERS() using net.bytebuddy.dynamic.scaffold.FieldLocator$ForClassHierarchy@9110d968 at net.bytebuddy.implementation.FieldAccessor$FieldLocation$Relative$Prepared.resolve(FieldAccessor.java:311) at net.bytebuddy.implementation.FieldAccessor$ForImplicitProperty$Appender.apply(FieldAccessor.java:860) at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:730) at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:715) at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:622) at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:6043) at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2224) at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$UsingTypeWriter.make(DynamicType.java:4050) at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3734) at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:3986)

raphw commented 1 year ago

This works without a problem:

    DynamicType.Builder<Object> eventBuilder = new ByteBuddy().subclass(Object.class);

    eventBuilder = eventBuilder.defineField("foo", Integer.class, 1);

    // Define a getter method for the property
    eventBuilder = eventBuilder
            .defineMethod("get" + "Foo", Integer.class, 1)
            .intercept(FieldAccessor.ofField("foo"));

    // Define a setter method for the property
    eventBuilder = eventBuilder
            .defineMethod("set" + "Foo", void.class, 1)
            .withParameter(Integer.class)
            .intercept(FieldAccessor.ofField("foo"));

    final Object dimBean = eventBuilder.make()
            .load(Thread.currentThread().getContextClassLoader())
            .getLoaded().newInstance();

Note that there is a defineProperty method.

dumakant commented 1 year ago

defineProperty and defineField are trivial programmes and they work fine when I write a small standalone programme. But does not work in our code base as expected. The class which I am trying to subclass say for Eg: Foo.class has following hierarchy Foo extends A A extends B B is an abstract class.

None of these classes have any constructor, they have just few member variables and accessor methods for the same. I am suspecting, these newly introduced fields and methods are not loaded properly at runtime. I tried playing around with ClassLoadingStrategy also, but no luck

dumakant commented 1 year ago

I found out the cause for this issue.

Issue #1 When I declare 2 fields, then this problem is occuring.

      builder =  builder.defineField("ADMIN_USER", Integer.class, 1);

    // Define a getter method for the property
    builder = builder
        .defineMethod("get" + capitalize("ADMIN_USER"), Integer.class, 1)
        .intercept(FieldAccessor.ofField("ADMIN_USER"));

    // Define a setter method for the property
    builder = builder
        .defineMethod("set" + capitalize("ADMIN_USER"), void.class, 1)
        .withParameter(Integer.class)
        .intercept(FieldAccessor.ofField("ADMIN_USER"));*/        

 builder =  builder.defineField("USERID", String.class, 1);

 // Define a getter method for the property
 builder = builder
   .defineMethod("get" + capitalize("USERID"), propertyType, 1)
   .intercept(FieldAccessor.ofField("USERID"));

 // Define a setter method for the property
 builder = builder
   .defineMethod("set" + capitalize("USERID"), void.class, 1)
   .withParameter(propertyType)
   .intercept(FieldAccessor.ofField("USERID"));

Issue #2 Since the filed names are dynamically generated, one of the field name was 1698909321000 This resulted in following error java.lang.IllegalStateException: Illegal field name for private java.lang.Integer com.plumtree.analytics.core.persist.BaseCustomEventDimension$ByteBuddy$MiKGhIzU.1698909321000

I got passed these issues by using defineProperty and renamed the above field