John Rose schrieb: [...] >> 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)); } > }
hmm... this way I can have my runtime single argument converter methods, true. That's better already... still it adds a stack frame and if I have to convert target return value, then no tail call will help me getting rid of that frame I guess. I guess a common technique would then be to cache such adapter to be able to reuse them... well depending on class loader issues. > 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. method handles are fine for me ;) [...] > This example assumes a statically linked (non-dispatching) call. If > there are multiple possible targets, which is the standard for Groovy, because we have our own dispatching rules, that slightly differ from Java in some cases. For example we allow foo(null) even if there are multiple foo methods with object subclassing argument. > 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); } > } hmm... if I have a MethodHandle created by unreflect, then this handle does never call another method, does it? >> 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. since we can not directly access the arguments on the stack I guess there is no way around it, yes. > 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). Object, int, long, double, float... so for 1 argument being one of these and one return value being one of these types I need 25 adapter classes. For 2 arguments it is 125. And with three arguments I break 256 easily. > 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. if all is Object based it sure is a small number, but as soon as other classes or primitives are added the number explodes easily >> 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. that would be very nice [...] >> 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. which reminds me of another question... if mh for example points to foo(String), can I invoke that with an Object (runtime type is of course a String), without doing the cast? Because this sounds as if this is not the case. I am asking, because in for example: >> 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)); } >> } if target expects two Strings, then arg1conv could consists of the original transformation and a cast dummy. Which is not really a problem of course... >>> 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. I agree >>> 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. afaik it currently won't do without code generation... currently... ;) >>> 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 argument 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. I think they would be really great, but I don't know if they are doable and how muich trouble they give to VM implementor. > 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).) but.... > Class.forName ("java.lang.PackagePrivateClass", true, > Object.class.getClassLoader()) I don't get the security argument yet I guess. But maybe this method simply works completely different? I know Class.forName(String) more or less bypasses the security checks, so the other versions does maybe not. > 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. Class#forName0 ? that one is private of course.. bye blackdrag -- Jochen "blackdrag" Theodorou The Groovy Project Tech Lead (http://groovy.codehaus.org) http://blackdragsview.blogspot.com/ http://www.g2one.com/ --~--~---------~--~----~------------~-------~--~----~ 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 -~----------~----~----~----~------~----~------~--~---