On May 25, 2010, at 2:12 PM, Charles Oliver Nutter wrote: > On Mon, May 24, 2010 at 8:22 PM, John Rose <john.r.r...@oracle.com> wrote: >> Right. Here are the degrees of freedom I see at this point: >> - whether to accept SAM types which are not interfaces (default: yes) >> - whether to allow a query API for recovering a method handle from a proxy >> (default: yes) >> - whether to allow multiple methods to be associated with multiple MHs >> (default: no) > > So you'd wire up a single handle that is dispatched through for all > incoming cases? Similar to how we (and presumably Groovy, when there > are no type hints) have a single JRuby "DynamicMethod" object for all > overloads of a given method name, and it makes a selection based on > actual types at runtime?
No, it would be some sort of marked tuple of method handles. The most concrete representation would be a pair of arrays: String[] and MethodHandle[]. Or it could be an interface CallResolver which, when queried with Class, String, and MethodType, gives a MethodHandle to call. You get best optimization when the CallResolver logic can be folded away for constant inputs. Forcing all calls to go through a single MH brings unrelated flows of control together, hurting optimization. >> Also, the query API in point 2 is harder to get right if you accept the >> other points. > > I don't have a particular need for the query API, though I haven't > thought through the possibilities yet. For the most part, all I ever > need is the implementation of some interface or abstract class, and > after that it can "just" be a class to me. The query API would allow the wrapper to be unwrapped directly. This is useful if you are ping-ponging the same value between modules that expect a MH or expect a wrapper. You can unwrap by binding one of the wrapper's methods to the wrapper, but it is likely that implementations would produce an ever-lengthening chain of MH indirections, one link for each wrap or unwrap step. That seems like a performance hazard to me. It is likely people will want to decorate method handles with many additional APIs. (That's what JMH was for.) If we have a standard interface AsMethodHandle or MethodHandleBox, then these decorated method handles can act much like subclassed objects of MH, since any MH-consuming API will work with a decorated MH, simply by calling MethodHandleBox.asMethodHandle. The explicit conversion call is clunkier than an implicit conversion to a supertype, but the effect is the same. >> Here's chained multi-phase version: >> class InstanceBuilder<T,R> { >> InstanceBuilder(List<Class<? extends T>> supers, List<String> names, >> List<MethodType> types); >> InstanceBuilder bindReceiver(R receiver); >> InstanceBuilder bindMethod(String name, MethodHandle method); >> T newInstance(); >> } >> >> And so on... > > Yeah, these seem like the right general direction. I'd probably my > money behind the multi-phase, multi-call version. Probably the most credible alternative to an InstanceBuilder API is a CallResolver API (see above). I don't know how to choose between them... > To add another use case to the Scala Function interfaces (which have > multiple abstract methods): JRuby's own DynamicMethod has a number of > call paths of different arities; if it were possible to implement all > of them with handles we'd never have to generate another "invoker" > again. Well, you can do an adapter class right now. Is the lack of inlining stopping you? >> Wrapping method handles adds layers of indirection and boxing hazards as >> Remi points out. > > To be sure...I'd love to move back into the realm of having no users > and JRuby being in its early research days, so I could commit to being > Java 7-only. It would make meeting performance challenges a vastly > simpler proposition. Unfortunately that's pretty hard to justify right > now, when we're busy trying to get paying customers (who stubbornly > refuse to use trunk builds of MLVM...imagine the nerve!). :) Let's keep thinking about commoning up the 6 and 7 source lines. It seems to me that we ought to be able to do better. >> Another possibility is to ask for an implicit conversion from selected >> application types X to MethodHandle. That in effect invents a new type >> relation: delegation. So it's no small matter. It's hard to keep that >> genie confined to a small bottle. > > Being able to generate subclasses would definitely be the more > straightforward option here; we don't really care about the actual > type of *any* of our generated DynamicMethod subtypes...we just need a > way to stitch calls from A to B through a known interface without > defeating inlining. If you had more control over inlining, would you be able to make more architectural adjustments to co-adapt the source lines? Maybe it's time for the long-awaited @Inline directive. > >>> Perhaps this also helps Java 7 closures work nicer by making it >>> possible to have a generic Closure or Function superclass completely >>> independent of java.dyn? Or by making it possible to implement >>> (faster) Java reflection with fewer generic frames under the covers >>> via a single abstract supertype populated by indy? >> >> The best way I know to accelerate Java reflective invocation is this: >> java.lang.reflect.Method foo = ...; >> try { z = MethodHandles.unreflect(foo).invokeGeneric(a, b, c, ...); } >> catch { ... } >> >> For this to be faster, it assumes there is method handle caching on >> unreflect, which I haven't done. But could be done. >> >> For example, you could preserve source compatibility by making a wrapper >> method to hide MethodHandles. > > This would be similar to us lazily generating a direct handle object > for each reflected method, which we currently support but don't turn > on by default. In our case, there's an explosion of user-loaded > classes to deal with, as well as the stickiness of loading custom > classes that reference classes you might want to unload later...back > to the ClassLoader hard reference issue again. > > But your trick is cleaner; we could potentially have a separate branch > that goes out to indy code if it's around, and that indy code would > use this logic. The ideal case would be that both indy and non-indy > dispatch through the same interface...and then we're back to needing a > way to implement an interface entirely with method handles again :) If MHs.unreflect(foo) in the example above is your own API, then you can change the return value (in different builds) to get source compatibility: class RubyInvocables { static java.dyn.MethodHandle getInvocable(RubyMethod m) { ... } } // use: RubyInvocables.getInvocable(foo).invokeGeneric(a, b, c, ...); class RubyInvocables { static RubyInvocableInterface getInvocable(RubyMethod m) { ... } } interface RubyInvocableInterface { Object invokeGeneric(); Object invokeGeneric(Object a); Object invokeGeneric(Object a, Object b); Object invokeGeneric(Object a, Object b, Object c); Object invokeGeneric(Object a, Object b, Object c, Object... ds); } // use: RubyInvocables.getInvocable(foo).invokeGeneric(a, b, c, ...); -- John _______________________________________________ mlvm-dev mailing list mlvm-dev@openjdk.java.net http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev