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

Reply via email to