Byte-buddy: generate classes with cyclic types

Stephan Brandauer :

I'm trying to generate classes with a cyclic class dependency, similar to this question: Byte Buddy - Handling cyclic references in generated classes

As a minimal example, the kind of classes I want to generate have dependencies like this:

//class A depends on class B, and vice-versa
final class A { B theB; }
final class B { A theA; }

The accepted answer in the link above did not provide enough information for me to get this to work. This is what I tried:

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.jar.asm.Opcodes;

public class ByteBuddyHello {

    public static void main(String[] args) {
        try {
            final ByteBuddy bb = new ByteBuddy();
            final TypeDescription.Latent typeDescrA = new TypeDescription.Latent("A", 0, null, null);
            final TypeDescription.Latent typeDescrB = new TypeDescription.Latent("B", 0, null, null);
            final DynamicType.Unloaded<Object> madeA = bb
                    .subclass(Object.class)
                    .name("A")
                    .defineField("theB", typeDescrB, Opcodes.ACC_PUBLIC)
                    .make(); // exception thrown here!
            final DynamicType.Unloaded<Object> madeB = bb.subclass(Object.class)
                    .name("B")
                    .defineField("theA", typeDescrA, Opcodes.ACC_PUBLIC)
                    .make();

            Object a = madeA
                    .include(madeB)
                    .load(ByteBuddyHello.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
                    .getLoaded().newInstance();
            System.out.println(a.toString());
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

When I run this, I get Exception in thread "main" java.lang.IllegalStateException: Cannot resolve declared type of a latent type description: class B in the marked line.

The answer to the referenced question above says to "Make sure that you do not load a type before defining the latent type properly", and I'm guessing that this might be my issue. I don't know how to define a latent type though :-(

Edit: made classes A and B above final (as this would be the ideal solution)

Edit: Add stack trace

Exception in thread "main" java.lang.IllegalStateException: Cannot resolve declared type of a latent type description: class B
    at net.bytebuddy.description.type.TypeDescription$Latent.getDeclaringType(TypeDescription.java:7613)
    at net.bytebuddy.description.type.TypeDescription$AbstractBase.getSegmentCount(TypeDescription.java:6833)
    at net.bytebuddy.implementation.attribute.AnnotationAppender$ForTypeAnnotations.onNonGenericType(AnnotationAppender.java:617)
    at net.bytebuddy.implementation.attribute.AnnotationAppender$ForTypeAnnotations.onNonGenericType(AnnotationAppender.java:333)
    at net.bytebuddy.description.type.TypeDescription$Generic$OfNonGenericType.accept(TypeDescription.java:3364)
    at net.bytebuddy.implementation.attribute.FieldAttributeAppender$ForInstrumentedField.apply(FieldAttributeAppender.java:122)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$FieldPool$Record$ForExplicitField.apply(TypeWriter.java:270)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:4156)
    at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1633)
    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:2559)
    at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:2661)
    at my.package.playground.ByteBuddyHello.main(ByteBuddyHello.java:20)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Rafael Winterhalter :

This is a bug in Byte Buddy; the resolver for type annotations needs to know the depth of any type description and therefore resolves the type path of any type description. For latent types, this depth is always 0 but the default implementation applies a more complex solution.

This will be fixed in the next release. In the meantime, subclass the latent type description and override the method to return 0.

I decided not to change the TypeDescription.Latent type but rather make the InstrumentedType.Default implementation more accessible. Use the latter type which allows you to define features of the cyclic type which will be visible to any user. This way, you can for example specify existing fields and methods if you want to define Implementations that validate against this feature.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=440581&siteId=1