On May 25, 2010, at 2:12 PM, Charles Oliver Nutter wrote:
> On Mon, May 24, 2010 at 8:22 PM, John Rose <[email protected]> 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
[email protected]
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev