Hi Vladmir, hello Remi,

what bothers me about instrumenting a lambda expression's target
method is the difficulty of locating the method that contains the code
and setting it into context. This is a lot of work to do since one
needs to first locate any invokedynamic call sites and interpret the
connection via the LambdaMetafactory. This is difficult to instrument
without greater efforts and does not feel like a clean solution.

Many real life instrumentations are based on the assumption of being
able to instrument any class of a given subtype or other any other
class property. For example, one would want to instrument any class
that implements InputStream to discover a resource leak. If the
interface is however functional and implemented as a lambda
expression, a tool that manipulating all class files of this type
stops working. At the same time, end users do not really understand
the difference of their former anonymous class that is now expressed a
lambda expression and perceive the beaviour as a regression.

I have myself implemented a custom solution for my code generation
library that instruments the LambdaMetafactory to apply class file
transformers to these classes manually. This does however show two
disadvantages:

1. javac creates the target method of a lambda expression as a private
method. The only way to implement a lambda expression legally in byte
code is by loading the generated implementation anonymously what
requires non-public API. While it is possible to call
Unsafe::defineAnonymousClass from the instrumented LambdaMetafactory,
it is again required to use non-standardized APIs which might fail on
differing implementations of the JVM.

2. At the same time, there is no standardized way to receive all
ClassFileTransformers that are currently registered on the VM. this
also requires calls into internal APIs that are not necessarily
supported on other platforms.

I fully understand the hesitation to support this from a technical
point of view but in reality, people are already dependant on this
feature and disallowing the instrumentation of lambda classes will
only inspire work-arrounds that reduce the stability of software using
this APIs that does currently work without problems for non-lambda
classes.

Best regards, Rafael

PS: While implementing my solution, I found that the LambdaMetafactory
of the Open JDK creates private, final methods for the serialization
bits. The methods should however not be final as they are already
private.


2016-01-23 13:55 GMT+01:00 Remi Forax <fo...@univ-mlv.fr>:
> I agree with Vladimir,
>
> You should not be able to transform/redefine a VM anonymous class because the 
> transformer will certainly mess up with the order of the constant pool 
> entries.
>
> Slightly off-topic, about ASM, when you create a ClassWriter [1], you can 
> pass a ClassReader of an existing class, in that case ASM copy the constant 
> pool from the class reader to the class writer so the constant pool is 
> preserved.
>
> Rémi
>
> [1] 
> http://asm.ow2.org/asm50/javadoc/user/org/objectweb/asm/ClassWriter.html#ClassWriter%28org.objectweb.asm.ClassReader,%20int%29
>
> ----- Mail original -----
>> De: "Vladimir Ivanov" <vladimir.x.iva...@oracle.com>
>> À: "Rafael Winterhalter" <rafael....@gmail.com>
>> Cc: "Coleen Phillimore" <coleen.phillim...@oracle.com>, 
>> core-libs-dev@openjdk.java.net, "serguei.spit...@oracle.com
>> Spitsyn" <serguei.spit...@oracle.com>, "Daniel Daugherty" 
>> <daniel.daughe...@oracle.com>
>> Envoyé: Vendredi 22 Janvier 2016 18:47:31
>> Objet: Re: ClassFileTransformer does not apply to anonymous classes
>>
>> 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
>>

Reply via email to