ClassFileTransformer does not apply to anonymous classes

Vladimir Ivanov vladimir.x.ivanov at oracle.com
Fri Jan 22 17:47:31 UTC 2016


Rafael,

First of all, I'd like to agree on the terminology. There's some 
confusion there. Anonymous term is ambiguous in Java. There are 
anonymous classes on language level and there's 
Unsafe.defineAnonymousClass() which produce anonymous-in-VM-sense 
classes. I prefer to call them VM anonymous classes to avoid confusion.

I assume that whenever you use anonymous you assume "VM anonymous".

> thank you for your response. While I completely understand your view
> from a VM implementor's point of view, as a practicioner I would
> recommend against it. Not being able to instrument lambda expressions
> puts a severe limitation onto using the instrumentation API
> alltogether. For example, a monitoring application that promises to
> record all invocations of a method of a certain interface that only
> works 95% of the time is not 5% less usefull but might no longer be
> useful at all. People have build large applications based on the
> assumption that all user application code can be instrumented and such
> a regression would hurt them. For example, until today, over 30 people
> that use my code generation framework reached out to me and reported
> the reported behavior as a bug in my library and people are only
> starting to migrate their applications to Java 8. The outcome is
> simply not intuitive as using a lambda expression over an anonyous
> inner class should not change an application's behavior.
Can you elaborate on that point, please?

I don't see any problems with instrumenting user code. Javac represents 
lambda expression body as a private static method of the enclosing 
class, which can be instrumented. VM anonymous classes are used only as 
a mechanism to glue functional interfaces and lambda expressions 
together at runtime.

For example:
    static void f(Runnable r) { r.run(); }
    f(() -> {});

Test::f (7 bytes)
   @ 1   Test$$Lambda$1/791452441::run (4 bytes)   inline (hot)
     @ 0   Test::lambda$main$0 (1 bytes)   inline (hot)

Why do you need to instrument Test$$Lambda$1/... and not 
Test::lambda$main$0?

> The currently used workaround that people use is to instrument the
> LambdaMetaFactory class itself to apply the transformer manually when
> it is being created. This solution is spreading as a standard approach
> and I am sure that forbidding a redefinition alltogether would only
> inspire to other workarrounds for being able to migrate running code
> to the next Java version.
It doesn't sound like a workaround, but as a solution for a different 
problem.

If there's a need to gather profile for every lambda expression 
instantiation site (indy call), then redefining bootstrap method is the 
right way to go IMO. It allows to intercept and customize indy call site 
binding.

What does sound as a workaround is an attempt to instrument classes 
produced by LambdaMetaFactory. Right now, it generates a fresh VM 
anonymous class on every request, but it is an implementation detail and 
not a requirement.

For example, LambdaMetaFactory can reuse wrappers on per-functional 
interface basis. It would provide significant footprint savings for 
lambda expression-rich code bases (usually, there are much more 
instantiations than functional interface flavors).

> Furthermore, I do not think that not being able to patch constant pool
> indices in the generated code introduces any problems in practice.
> Most real-world instrumentation only appends new constant pool entries
> without interfering with the existant pool. Rather, I would like to
> see an exception being raised if one attempts to change the original
> constant pool without me being able to consider this from a technical
> perspective. I think this would serve to be sufficient for most
> people.
If you care only about lambda expressions, then constant pool patching 
shouldn't be a problem (AFAIR it isn't used for lambda expressions). But 
in a more generic case (even for java.lang.invoke purposes), the 
coupling between class file and CP patches is very tight and can be 
easily broken with benign class file transformations. Considering ASM 
library, I'm not sure that even simple instrumentations preserve 
original constant pool structure.

Probably, VM can do some structural checks on CP to forbid any 
modifications except appends, but there are still aliasing problems - 
sharing doesn't work (even for string constants), so new code shouldn't 
use original CP entries.

I'd prefer to avoid such increase in complexity in VM implementation.

> I really hope that there is a way to allow for patching anonymously
> loaded classes even without exposing the constant pool patches.
 From VM implementation perspective I don't see any problems with 
allowing class file redefinition for VM anonymous classes. What I want 
to do is to discourage from doing so. Again - it is very fragile and 
there's little we can do to fix that.

> So far, thank you for looking into this. I am sure this a more complex
> matter than what I am able to comprehend. I appreciate that you are
> taking the time to consider my opinion!

Best regards,
Vladimir Ivanov



More information about the core-libs-dev mailing list