Transforming lambdas in Java 8

前端 未结 2 948
轻奢々
轻奢々 2020-12-15 07:45

Java 8 appears to generate classes to represent lambda expressions. For instance, the code:

  Runnable r = app::doStuff;

Manifests, roughly

相关标签:
2条回答
  • 2020-12-15 08:07

    Bug submission was accepted by folks at Oracle, and is being tracked as JDK-8145964. This isn't exactly a solution, but appears to be a real runtime issue.

    0 讨论(0)
  • 2020-12-15 08:12

    To me, this seems like a bug in the JVM. The system class loader attempts to locate the transformed class by its name. However, lambda expressions are loaded via anonymous class loading where the following condition:

    clazz.getClassLoader()
         .loadClass(clazz.getName().substring(0, clazz.getName().indexOf('/')))
    

    yields a ClassNotFoundException resulting in the NoClassDefError. The class is not considered a real class and such anonyoumous classes are for example not passed to a ClassFileTransformer outside of a retransform.

    All in all, the instrumentation API feels a bit buggy to me when dealing with anonymous classes. Similarly, LambdaForms are passed to ClassFileTransformers but with all arguments but the classFileBuffer set to null what breaks the transformer class's contract.

    For your example, the problem seems to be that you return null; the problem goes away when returning the classFileBuffer what is a no-op. This is however not what the ClassFileTransformer suggests, where returning null is the recommended way of doing this:

    a well-formed class file buffer (the result of the transform), or null if no transform is performed.

    To me, this seems like a bug in HotSpot. You should report this issue to the OpenJDK.

    All in all, it is perfectly possible to instrument anonymously loaded classes as I demonstrate in my code manipulation library Byte Buddy. It requires some unfortunate tweaks compared to normal instrumentation but the runtime supports it. Here is an example that successfully runs as a unit test within the library:

    Callable<String> lambda = () -> "foo";
    
    Instrumentation instrumentation = ByteBuddyAgent.install();
    ClassReloadingStrategy classReloadingStrategy = ClassReloadingStrategy.of(instrumentation)
        .preregistered(lambda.getClass());
    ClassFileLocator classFileLocator = ClassFileLocator.AgentBased.of(instrumentation, 
         lambda.getClass());
    
    assertThat(lambda.call(), is("foo"));
    
    new ByteBuddy()
      .redefine(lambda.getClass(), classFileLocator)
      .method(named("call"))
      .intercept(FixedValue.value("bar"))
      .make()
      .load(lambda.getClass().getClassLoader(), classReloadingStrategy);
    
    assertThat(lambda.call(), is("bar"));
    
    0 讨论(0)
提交回复
热议问题