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