Hi John, ummm ... this seems to imply I can remove the findSuperConstructor() proposal from the Indy JEP. Incidentally, it's on my list for this week - and less work is always good. ;-) Even if, in this case, it leads to disappointment. I agree with you in that opening up the MH API like this will introduce several trapdoors and additional complication.
Please let me know ... Best, Michael > Am 29.08.2015 um 03:40 schrieb John Rose <john.r.r...@oracle.com>: > > The invokespecial-super-init dance is the thing MH's can't quite do, the > "super" call every constructor (except Object.<init>). > > It very hard to secure this pattern; just ask anybody who has worked on > (de-)serialization security. > > But, we can look at it from a more limited point of view which might improve > your use case, Jochen. > > A method handle is supposed to be a fully competent replacement for hardwired > bytecodes, and it is, except for invokespecial-super from a constructor. The > reason this is hard is that there is no way to constrain such a method > handle, once constructed, to operate inside a constructor. And superclasses > have a right to expect that you are running their constructor as a unique, > non-repeatable part of creating a subclass object. (By "have a right" I > really mean "it would be wrong to do the unexpected" by which I also mean > "attack surfaces are likely to open up if we do this.) > > So, is there a way to package up a method handle so that it can only be used > as as unique, non-repeatable part of creating a subclass object? Yes, it > can: Wire in an unconditional "new instance" operation, and immediately run > the "invokespecial super" on the new thing. > > Now the problem reduces to: Your class (just like its super) has a right to > expect that constructor code will be run on every newly-created instance > (after the super constructor), before the new object is made available to > other code. Can we package up the previous new-invokespecial-super method > handle so it can only be used in this way? Well, no, since every constructor > *also* has a hardwired call to invokespecial; we are back to the pre-existing > new-invokespecial type of MH. > > There are several possible ways out, but the problem is delicate. The > purpose of constructors is to statically mark code that must be executed > before any (normally published) reference to an object is reachable by > non-class code. If there were a way to statically mark code as > "post-super-init" ("<postsuperinit>"?), we could make an agreement with a > class that such a method would serve as the equivalent of a constructor, but > it would be the caller's responsibility to allocate the new instance *and* > call the super init. Allowing bytecode to call this stuff would require a > bunch of new verifier rules, in a place where the verifier is already hard to > understand. Perhaps a method handle could be allowed to operate where normal > bytecode cannot, but you see the problem: Method handles are designed to > give a dynamic alternative to things you can already do in bytecode. > > The "post-super-init" convention can be a private convention within a class, > in the special case of Groovy, since Groovy is responsible for generating the > whole class, and can trust itself to invoke all necessary initialization code > on each new instance. So if you had an new-invokespecial-super MH in a > private context within a Groovy-generated class, you could use it to create a > "mostly blank" instance, and then fill it in before sharing it with anybody > else. Such an invokespecial-super MH could be adequately protected from > other users by requiring that "Lookup.findSpecialConstructor" can only work > on full-powered lookups, regardless of the accessibility of the super > constructor. > > There are two further problems with this, though. First, constructors have a > unique ability and obligation to initialize blank final variables (the > non-static ones). So the Lookup.findSpecialConstructor MH has to take an > argument, not just for its super-constructor, but also for *each* final > variable in the *current* class. (Note that Lookup.findSetter will *not* > allow finals to be set, because it cannot prove that the caller is somehow > "inside" a constructor, and, even if inside it, is trustably acting on behalf > of it.) There are other ways to go, but you can see this problem too: The > new-invokespecial operator has to take responsibility for working with the > caller to fill in the blank finals. > > The second further problem is even more delicate. The JVM enforces rules of > calling <init> even (sometimes) against the wishes of people who generate > class files. We don't fully understand the practical effects of relaxing > these rules. Proofs of assertions (such as type correctness and security) > require strong premises, and the rigid rules about <init> help provide such > premises. An example of a proof-failure would be somebody looking at a > class, ensuring that all instances are secure based on the execution of > <init> methods, but then fail to notice that the class *also* runs some > instances through an alternate path, using new-invokespecial-super, which > invalidates the proof by failing to run some crucial setup code. > > With all that said, there is still wiggle room. For example, one *possible* > solution that might help Groovy, while being restrictive enough to avoid the > problems above, would be to split <init> methods and sew them together again > with method handles. > > Suppose there were a reliable way to "split" an <init> method into two parts: > Everything up to the invokespecial-super-<init> call, and everything > afterwards. (Perhaps it must be preceded *only* by load-from-local opcodes.) > Call such <init> methods "splittable". Not all will be splittable. Then we > could consider allowing a class to replace one of its splittable constructors > by a new hybrid consisting of a differently-selected super-constructor, > followed by the tail of the splittable constructor. (Note that this neatly > handles blank finals.) It would not be valid for any party other than the > sub-class itself to perform such a split, but it might, arguably, be > reasonable for a class to do such a thing. > > There are always many defects with such schemes. In this case, there is no > robust way to detect that a splittable constructor has in fact been split. > (I keep wanting to invent new bytecodes or verifier rules here!) Any rule > for splittability is going to be a little hacky, hence hard to understand and > use correctly. Specific constructors might be "coupled" strongly to matching > super-constructors, in such a way that a mix-and-match will cause surprises, > even to the author of the subclass. (Having stuff happen by invisible magic > gets old, as soon as you realize you have to vouch for the behavior of code > which you can really only see in source form.) Finally (as noted above) MHs > are quite robustly understanable from the principle that they are "just > another way" to do what bytecodes have already done; violating this principle > pushes uncertainties into equivalence proofs about MHs and bytecodes. > > In the end, I think Groovy may be better off using its ugly <init> bytecode > sequence, where every subclass constructor calls (via a switch) every > superclass constructor. > > I hope this helps, although it's kind of disappointing. We ran into same > dangerous dance, in the Valhalla bytecode interpreter, and had to fake it > from random bits of the MH runtime. > > — John > > On Feb 26, 2015, at 2:27 AM, Jochen Theodorou <blackd...@gmx.org> wrote: >> >> Am 26.02.2015 01:02, schrieb Charles Oliver Nutter: >>> After talking with folks at the Jfokus VM Summit, it seems like >>> there's a number of nice-to-have and a few need-to-have features we'd >>> like to see get into java.lang.invoke. Vladimir suggested I start a >>> thread on these features. >> >> my biggest request: allow the call of a super constructor (like >> super(foo,bar)) using MethodHandles an have it understood by the JVM like a >> normal super constructor call... same for this(...) >> >> Because what we currently do is annoying and a major pita, plus it bloats >> the bytecode we have to produce. And let us better not talk about speed or >> the that small verifier change that made our hack unusable in several java >> update versions for 7 and 8. >> >> This has been denied in the past because of security reasons... And given >> that we need dynamic argument types to determine the constructor to be >> called, and since that we have to do a call from the runtime in the uncached >> case, I fully understand why this is not done... just... it would be nice to >> have a solution that does not require us doing basically a big switch table >> with several invokespecial calls >> >> bye Jochen >> >> -- >> Jochen "blackdrag" Theodorou - Groovy Project Tech Lead >> blog: http://blackdragsview.blogspot.com/ >> german groovy discussion newsgroup: de.comp.lang.misc >> For Groovy programming sources visit http://groovy-lang.org >> >> _______________________________________________ >> mlvm-dev mailing list >> mlvm-dev@openjdk.java.net >> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev > > _______________________________________________ > mlvm-dev mailing list > mlvm-dev@openjdk.java.net > http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev _______________________________________________ mlvm-dev mailing list mlvm-dev@openjdk.java.net http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev