Is invokedynamic dynamic enough?

Iulian Dragos jaguarul at gmail.com
Thu Dec 18 12:06:35 PST 2008


Now that I got your attention, prepare for an email about how Scala
can't use invoke dynamic.

First let me introduce some background. Scala has structural subtyping
for refinement types. Simply put, one can define a type that has a set
of methods, which is a supertype of all types that contain all these
methods (with the right signature). Such types need not, of course,
extend any particular interface. For example:

type Closable =Object {
  def close: Unit
}

def closingTime(f: Closable) {
  f.close
}

is the type of all things that have a 'close' method that returns
nothing. A method expecting a Closable would accept a
java.io.InputStream, even though InputStream does not extend Closable.

The problem is that calls to 'close' cannot be 'resolved' by the
compiler: it cannot emit an invoke instruction, since it does not know
in which class this method should be looked up. The current
implementation uses reflection. However, the type system guarantees
that each call succeeds. We'd like to do better, and make these calls
as efficient as possible. After all, the compiler knows they exist in
the receiver, just that the receiver class may change at each
invocation.

Invoke dynamic seemed the answer. Instead of looking up such methods
through reflection, we could emit an invoke dynamic, and use method
handles to directly invoke the right method. This should be much
faster: no argument boxing/unboxing and no superfluous visibility
checks. Unfortunately, the requirement for installing a target in a
call site is that the type of the target method (the defining class is
considered its first parameter, so it is part of the method type) is
exactly the same as the static type of the call site! The static type
of the call site is the sequence of types on the stack. As
/statically/ inferred by the verifier.

To see why this won't work, think about what should the parameter type
of 'closingTime' be. It has to be a type that is a super type of all
other types, in the JVM sense. It has to be Object. This means the
call site has the type Object. Suppose we had a method handle to
InputStream.close: we would not be able to use it, because its static
type would be void(InputStream) instead of void(Object). I assume the
same rules apply to MethodHandle.invoke, irrespective of invoke
dynamic.

If the static types of both the method handle, and the call site have
to be equal, method handles are only usable in cases when even the
compiler has enough knowledge to generate a direct 'invoke', on the
receiver type (ok, a method handle does not care about the method
name). What is their main use case, then?

To fix it, we could think of a cast of the receiver, but casts are not
dynamic enough either: they need a static argument (an index in the
ConstantPool), so they can't change at runtime (which makes sense from
the verifier POV) .

There is one way out, according to the spec:
MethodHandles.convertArguments. This method generates another method
whose arguments are converted to/from the given method type, through
casts and conversions. Unfortunately, this is not yet implemented, so
it remains to be seen if such method handles are faster than
reflective calls.

I don't have any good suggestions (yet), but I think the current
design could be improved to be more.. dynamic. And I hope I explained
the problem well enough that others will sympathize with my views.

Cheers,
iulian

-- 
« Je déteste la montagne, ça cache le paysage »
Alphonse Allais



More information about the mlvm-dev mailing list