Maybe this is a more graphic example of a problem that end-users currently face: http://stackoverflow.com/questions/33912026/intercepting-calls-to-java-8-lambda-expressions-using-byte-buddy
2016-01-26 10:02 GMT+01:00 <fo...@univ-mlv.fr>: > Hello, > > ----- Mail original ----- >> De: "Rafael Winterhalter" <rafael....@gmail.com> >> À: "Remi Forax" <fo...@univ-mlv.fr> >> Cc: "Vladimir Ivanov" <vladimir.x.iva...@oracle.com>, "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é: Lundi 25 Janvier 2016 20:24:01 >> Objet: Re: ClassFileTransformer does not apply to anonymous classes >> >> 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. > > The lambda body is analyzed because the body is translated to a static method > (which can be non static BTW) which is part of the class that uses the lambda. > The lambda proxy is a runtime artifact and the way it is currently > implemented will likely to change in the future, using a vm anonymous class > is a workaround of the fact that there is currently no way to load a > function/method without a class. You should not try to analyze the lambda > proxy, it doesn't contain codes but it's a straw glue between the interface > and the body of the lambda. > >> >> 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. > > The invokedynamic corresponding to a lambda creation contains a link to the > private method, it's i believe all you need, but i'm not sure because what > you're doing exactly is fuzy for me. > >> >> 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. > > again, there is no such thing as a lambda class, trying to see a lambda as an > anonymous class will never fully work. > a lambda is a lambda, you have the link between the functional interface and > the lambda body encoded into the invokedynamic which is used to create the > lambda, you should not try to get more information, because as i said, it may > change. > >> >> 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. > > yes, private is enough but it's not a big deal. > >> > > regards, > Rémi > >> >> 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 >> >> >>