Another note on this subject: I found that applying a reretransformation on a lambda expression's class does not provide the original class file to the retransformer but the class file that resulted from a previous retransformation. (If a lambda class is retransformed several times.) This is different to "normal" classes where the original class file is provided.
2016-01-25 20:24 GMT+01:00 Rafael Winterhalter <rafael....@gmail.com>: > 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 >>>