3

So I ran into

Exception in thread "Thread-0" java.lang.IllegalArgumentException: Unknown type: null
    at net.bytebuddy.description.type.TypeDefinition$Sort.describe(TypeDefinition.java:213)
    at net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType$ForLoadedType$ParameterArgumentTypeList.get(TypeDescription.java:4595)
    at net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType$ForLoadedType$ParameterArgumentTypeList.get(TypeDescription.java:4569)
    at java.util.AbstractList$Itr.next(AbstractList.java:358)
    at net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor.onParameterizedType(TypeDescription.java:1556)
    at net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor$ForDetachment.onParameterizedType(TypeDescription.java:1709)
    at net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType.accept(TypeDescription.java:4407)
    at net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor.onParameterizedType(TypeDescription.java:1557)
    at net.bytebuddy.description.type.TypeDescription$Generic$Visitor$Substitutor$ForDetachment.onParameterizedType(TypeDescription.java:1709)
    at net.bytebuddy.description.type.TypeDescription$Generic$OfParameterizedType.accept(TypeDescription.java:4407)
    at net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection.accept(TypeDescription.java:5308)
    at net.bytebuddy.description.field.FieldDescription$AbstractBase.asToken(FieldDescription.java:143)
    at net.bytebuddy.description.field.FieldDescription$AbstractBase.asToken(FieldDescription.java:87)
    at net.bytebuddy.description.field.FieldList$AbstractBase.asTokenList(FieldList.java:47)
    at net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default$1.represent(InstrumentedType.java:222)
    at net.bytebuddy.ByteBuddy.redefine(ByteBuddy.java:698)
    at net.bytebuddy.ByteBuddy.redefine(ByteBuddy.java:676)
    at parc.Foo.redefineClass(Foo.java:137)

When trying to redefine a class that is already loaded with the bytecode the JVM loaded.

The code was in a preliminary step already transformed by a soot-framework and we suspect that some of the signature attributes might have become outdated or gone missing in that processs and that ByteBuddy simply insists on the correctness of the info it doesn't have.

Strictly speaking, ByteBuddy doesn't need that information, either. (Obviously, seeing how the signature attribute is optional and how the class is loaded and run by the JVM just fine.) So a quick way to check would be to tell byteBuddy to simply not care and see if that changes anything.

Is there a way to configure ByteBuddy in such a way?

(ByteBuddy version is 1.7.9)

(Project requires Java 7)

(class reloading is done

private void redefineClass(String classname, byte[] bytecode) {
    ClassFileLocator cfl = ClassFileLocator.Simple.of(classname,bytecode);

    Class clazz;
    try{
        clazz = Class.forName(classname);
    }catch(ClassNotFoundException e){
        throw new RuntimeException(e);
    }

    Debug._print("REDEFINING %s",clazz.getName());

    new ByteBuddy()
            .redefine(clazz,cfl)
            .make()
            .load(clazz.getClassLoader(),ByteBuddyConfig.reloadingStrategy)
            ;
}

with

public class ByteBuddyConfig {

    static final ClassReloadingStrategy reloadingStrategy;
    static {
        try {
            reloadingStrategy = new ClassReloadingStrategy(
                    (Instrumentation) ClassLoader.getSystemClassLoader()
                            .loadClass("net.bytebuddy.agent.Installer")
                            .getMethod("getInstrumentation")
                            .invoke(null),
                    ClassReloadingStrategy.Strategy.RETRANSFORMATION);
        }catch(ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e){
            throw new RuntimeException(e);
        }
    }
}

thanks to @kutschkern from how to debug an internal error? )

User1291
  • 7,664
  • 8
  • 51
  • 108

2 Answers2

3

I suppose that whatever the ByteBuddy frontend is doing here, is part of the support for all the other operations you could chain to perform another transformation. As said in the answer to your other question, you can skip these operations when have the byte code already:

ClassReloadingStrategy s = ClassReloadingStrategy.fromInstalledAgent();
s.load(clazz.getClassLoader(),
    Collections.singletonMap(new TypeDescription.ForLoadedType(clazz), bytecode));

Before Java 8, you’ll need Collections.<TypeDescription,byte[]>singletonMap(…).

when the class loading strategy is based on ClassReloadingStrategy.Strategy.REDEFINITION you can also use

ClassReloadingStrategy s = ClassReloadingStrategy.fromInstalledAgent();
s.reset(ClassFileLocator.Simple.of(classname, bytecode), clazz);

as it will use the bytecode retrieved through the ClassFileLocator as base.

I suggest to stay with the standard way of acquiring the ClassReloadingStrategy implementation, as you did in your other question, I can’t recognize what you hope to gain with this more complicated reflective operation.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • The reflective option was suggested to me to try in case ``.fromInstalledAgent()`` had trouble with its being an anonymous inner class and although it didn't take care of the particular issue in the other question, I figured there was no harm in keeping it. ``.fromInstalledAgent()`` seems to work fine, here, though. – User1291 Feb 20 '18 at 11:53
  • @User1291 that made me realize that my first code was just a special case. I added the general solution that should work with every `ClassReloadingStrategy` (for those strategies using a different loader instead of Instrumentation, you would have to use the returned map). – Holger Feb 20 '18 at 12:58
  • Thank you. So what does, ``reset(...)`` do in the ``ClassReloadingStrategy.Strategy.REDEFINITION`` that it doesn't do in other strategies? (Oh, and you need to cast the ``.ForLoadedType`` to ``TypeDescription``) – User1291 Feb 20 '18 at 13:22
  • 1
    @User1291: as said, this strategy uses the class file bytes retrieved through your `ClassFileLocator` as original bytes to reset to, which is technically the same as redefining the classes to those bytes. In contrast, `RETRANSFORMATION` triggers a retransformation through the Instrumentation API where it is up to the JVM to reset to a previously stored version before calling all registered transformers. A JVM without retransformation capabilities won’t have such previous state; that’s why `REDEFINITION` relies on your `ClassFileLocator`. – Holger Feb 20 '18 at 13:28
1

You can set the net.bytebuddy.raw property to true what forces Byte Buddy into ignoring any generic type information. Know that setting this property might yield unexpected results as Byte Buddy is for example no longer able to properly resolve bridge methods and other things.

You can typically set this property when you are writing a Java agent that only transforms already existing methods, typically using Advice.

This is a strange error though, that implies a ParameterizedType that defines one of its getActualTypeArguments as null. Such errors do typically raise errors by the JVM generic signature parser.

As for the reflective operations, the yet unreleased Byte Buddy 1.7.11 will include a convenience method for setting the strategy.

Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192