On Sep 10, 2008, at 3:46 AM, Jochen Theodorou wrote: > John Rose schrieb: >> On Sep 9, 2008, at 3:33 PM, Jochen Theodorou wrote: >> >>> On further thought I have some questions here... is it possible to >>> have >>> custom MethodHandles? Custom in the sense of extending >>> MethodHandle... >> >> The answer is surprisingly simple: Use a bound method handle. That >> is, use MethodHandles.insertArgument to give a plain method handle a >> "hidden" receiver argument. > > the insert I have is: > >> ...
Yes, this is the one that works for creating bound method handles. A bound method handle can perform any action you want, using the incoming arguments plus the bound-in receiver. In particular, it can apply conversions to one or more arguments, and then re-apply the changed arguments to another method handle. (That last should be a tail-call; it will be some day.) > on the operand stack I have the GString, which needs to become a > String > by calling its toString() method, which can be done by for example > MyRuntime.coerceToString(Object o). And I want to call Foo#foo > (String). > What this method looks to me like is that if I want to insert a > constant > argument to the method call, then it is fine, because I know the value > and the value won't change. But my GString is probably no compile time > constant. The constant you insert is a smart adapter object which you have created to perform the conversion and then invoke the ultimate target method (as another method handle). Sequence of events: caller calls indexOf(gs1, gs2) # two GStrings call site links to my2argConverter.convert, a bound method handle BMH calls my2argConverter.invoke(gs1, gs2) Groovy2ArgConverter.convert(gs1, gs2) does something correct to its arguments Groovy2ArgConverter.convert(gs1, gs2) then calls this.target, a DMH of String.indexOf String.indexOf(s1, s2) is called (s1 being the receiver) Here, Groovy2ArgConverter is a hypothetical adapter-building class which takes two arguments, performs conversions, and then calls a target MH. The data in this class could be three MH's, one to convert each of the arguments, and which is the ultimate receiver: class Groovy2ArgConverter { MethodHandle arg1conv, arg2conv; // called to convert the incoming arguments MethodHandle target; // called on the converted arguments Object invoke(Object a1, Object a2) { return target.invoke (arg1conv.invoke(a1), arg2conv.invoke(a2)); } } (Problem with sample code: Javac does not support the MH.invoke operation. This might have to be generated directly to bytecodes.) The arg1conv guys are sketched here as method handles, but there is no reason they must be: I'd consider using an interface-based argument conversion API here also. > hmm... I think saying closures here is openeing a can of worms that we > should keep closed for now. I see MethodHanldes as function pointer > and > if they have some closure like functionality or not is not really > important. I see that this insert can be used to insert constants, > which > allows a kind of currying. But I don't want to curry for each call > anew... No, you curry when you link the call site. When linking the call site, you do the following steps: discover the ultimate call target (say, String.indexOf(String)) notice that the caller and callee signatures disagree figure out a way to convert each argument create a customized "my2argConverter" which embodies those conversions and that ultimate target make a BMH for its invoke method install that BMH as the call site's target This example assumes a statically linked (non-dispatching) call. If there are multiple possible targets, the dispatch logic can be built into the adapter also, either along with argument conversion or (factored out) as another layer of MH adaptation, embodying the type decision tree, with or w/o caching: class Groovy2ArgPIC { Map<Class, MethodHandle> targets; MethodHandle lookupTarget(Class a1c, Class a2c) { ... } Object invoke(Object a1, Object a2) { return lookupTarget (a1.getClass(), a2.getClass()).invoke(a1, a2); } } > To illustrate the problem: > > static Object coerceToStringAndCall(Object receiver, Object argument, > MethodHandle mh) { > String string = argument.toString(); > return mh.call(receiver,string); > } > > Then I can reference this by a MethodHandle and insert the > MethodHandle > I got from my method selection... but this has several problems... > first > I have one such method for only one argument, but the transformation > might be needed for one out of n arguments at different positions. > Meaning this won't do in general. That alone won't do in general; you'll have to write or generate one such class for each syndrome of argument adaptation. See above an example which handles all possible conversion scenarios for two reference arguments; you'd need other ones for 1,3,4... argument calls. If we can build flyby adapters that reify the argument lists into a standard form, we can build an omnibus converter which handles all kinds of argument lists. But even if we don't get those, we can cope by creating N classes (where N<256 is the length of the longest argument list in the system). Method handles reduce, but do not eliminate, the need for generating adapter classes. Without method handles, we have to generate one class per target method (or small group thereof). With method handles, we still might have to generate one adapter class per distinct method signature, or (worst case) per pair of signatures that are connected by a call. I'm trying to do realistic and simple things in the JVM to fold down the number of these adapters, but it is a work in progress. > And second... is this ok for HotSpot? > This method is most probably used from many places with different > MethodHandles... so this is kind of megamorphic, or not? Not megamorphic in the traditional sense, since MHs do not exhibit a distinct class per target. But, yes, megamorphic in the sense that the adapter code will be reused for many target methods. Therefore, the adapter code needs to be inlineable. Remember that an invokedynamic call site has compile-time knowledge of the (dynamically linked) target MH, so it can be inlined, within the usual sorts of limits. If the target MH is a BMH to Groovy2ArgConverter.invoke, and the compiler can see a specific instance bound to that method (in the BMH), then the compiler can probably (work in progress!) pick up the ultimate target also, and bypass the megamorphic hazard. > At last inlining whatever mh referes must be undone very soon... > only if > coerceToStringAndCall is inlined there is a chance that the > function mh > points to can be inlined as well. Which it will be, if the BMH is bound to an invokedynamic call site, and if coerceToStringAndCall looks profitable to inline. > And there is a third too... the return > value. For example if I have > > int foo(String x) {return x.size()} > def y=1 > int x = foo("$y") > > then foo would return an int and I would like to avoid boxing if > possible. but that means mh can't return Object, that means also > coerceToStringAndCall can't return Object. It sure isn't as > problematic > as the GString before, since there is only a limited set of > primitives, > but it increases the number of such stupid functions. Yes indeed it does. You still need those stupid functions. The challenge I see is to design the adapter API (in MethodHandles) so as (a) to push most of the stupid functions down into the shared code in java.dyn., and (b) factor the JVM-specific implementation of that code so that the JVM (HotSpot or any other JVM) can provide low-level polymorphism to reduce the number of stupid functions in the implementation. Example of low-level polymorphism: The prototype JVM I posted on mlvm has a JVM-level adapter called "checkcast" which works equally well for all argument signatures. You supply it with two paramters: An argument number (which had better refer to a reference parameter) and a desired class... plus of course the adapter's ultimate target MH. The adapter accepts the incoming arguments, checks the indicated argument by performing a checkcast, and then delegates the (unchanged) argument list to the target. If you want to check two arguments, you stack two of these adapters. The purpose is to wire together methods and call sites which differ slightly about reference types (perhaps there are erased types in play, so the argument is typed as Object, but known to be some narrower T which the target method expects). ...And so on. >> From there you are back to the usual game of creating lots of little >> adapter classes. > > what do I need these adapter classes for? In Groovy 1.5.x we don't > have > them. In Groovy 1.6 we create classes at runtime, they are used for > the > call site caching. But the goal must be to not to need these call site > classes created at runtime. I mean if I need them anyway, why do I > bother with MethodHandles? They might not be right for Groovy, after all. (But I hope they will be.) The goal is to move much, not all, of the need to generate adapter code out of the language runtime and down into the shared code, where the JVM will be able to understand and optimize it better. >> Right. The way forward for that is tricky, because the question is >> how to divide the labor between language runtime and JVM (and their >> respective implementors). Current design has minimal impact on the >> JVM, but it pushes adapter generation back onto the language >> implementor. > > letting the implementor write the adapter is ok for me, but it > should be > possible to write the adapter code as methods, not as classes and > without having to make calls on the method handle. BMHs can target either static or non-static methods, so it's up to you how to factor the adapter code. >> The JVM will use lots of stack frames for the foreseeable future. >> >> Consider the case of return-value coercion: You need a stack frame >> of some sort to mark where the thread is going to process the return >> value, between the time the callee returns it and the caller receives >> it (under a different type). A simple example: boxing int to >> Integer. So you can do lots of *argument* coercions without pushing >> new stack frames, but return coercions inherently require them. Not >> even tailcall can help there. > > if you are allowed to have argument transforming "things", that can be > chained together with the a normal method handle, then those could do > pre- and post-processing for the MethodHandle... for example: > > goal call a method given by MethodHandle mh, which requires a > String and > returns an int, with a GString and box the int after. > > arguments on stack: > Object receiver > GString gs > > code created by chaining method handles: > > //flyby? > replace(1,convertGStringToString(gs)) //stack: Object, String > > x=invoke mh //stack: int > > //ricochet? > x=invoke boxInt(x) //stack: Integer > > boxXY and convertXToY can be simple methods, taking one argument and > returning one value, which is used to replace an arugment on the > stack, > or to replace a return value. If I could use MethoHandles like this, > then I would not need to create any class or adapter at runtime, only > MethodHandles. Also this has a good chance of not being changed This is a very good example, with conversions on an argument and the return value. Yes, given those more powerful adapters, it would be possible to wire everything up with MHs. Under the covers, it's between the JVM and rt.jar whether or how much of that requires generating funny extra classes, as is the case today with reflection. Regarding Class.forName: >> I think we need a different solution for Class.forName than >> abolishing stack frames. There needs to be a way for trusted code to >> run Class.forName on behalf of another class, or else for a class to >> create a capability (method handle?) that will run Class.forName for >> it, and hand it to the semi-trusted code that needs it. > > sounds complicated The security part is slightly complicated. Without security, the API is trivial: Class forName(String name, Class context); The reason the context argument is fished out of the control stack is simply to prevent malware from forging it. (E.g., Class.forName ("java.lang.PackagePrivateClass", Object.class).) As a first cut, if your code is privileged, you need something like: Class sun.misc.Unsafe.lookupClassFromClass(String name, Class context) (Cf. Unsafe.defineClass, which provides a similarly unrestricted version of class loading.) But that would work only with JVMs of the HotSpot family. (And doesn't exist today.) There are probably other tactics that would work, too, but none occur to me off the top of my head. -- John --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "JVM Languages" group. To post to this group, send email to jvm-languages@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/jvm-languages?hl=en -~----------~----~----~----~------~----~------~--~---