New proposal for #ReflectiveAccessToNonExportedTypes: Open modules & open packages

Andrew Dinn adinn at redhat.com
Fri Nov 18 16:43:46 UTC 2016


On 12/11/16 03:23, John Rose wrote:
> On Nov 11, 2016, at 7:09 AM, forax at univ-mlv.fr
> <mailto:forax at univ-mlv.fr> wrote:
>> MH.invokeWithArguments takes an array of arguments but because it is
>> specified as a varargs you may think that it works like Method.invoke,
>> but it is a trap,
>> it takes the receiver and the arguments altogether into the same array.
> 
> Thanks, Remi.  That should fix the problem.

I'm not sure Remi's answer exactly addresses my question exactly as he
explained what happens when there is a receiver for the call and my
problem case is a static call. Anyway, I think I have got the picture
and now know what is wrong.

> We thought a little bit about adding more overloadings to
> invokeWithArguments,
> such as one that works like Method.invoke (one prepended argument).
> The general case would be making invokeWithArguments be
> signature-polymorphic,
> with an on-the-fly asSpreader transform on the way through.
> 
> But, such extra generality would simplify only a few use cases, and on the
> other hand it would probably create plenty of confusion whenever the target
> method is *also* a varargs method.

My problem was that the String[] argument I was passing to Arrays.asList
got wrapped in an enclosing Object[] array. That's because the method
has a varargs signature and when you call the method handle returned by
findStatic or findVirtual the arguments are expected to be provided
unwrapped in the invoke call. Whereas a method handle for a method with
a trailing Object[] argument (naturally) expects the argument to contain
the relevant Objects wrapped in an Object[].

This is where the disparity with reflective invocation occurs where the
varargs arguments must be provided pre-wrapped to the reflective invoke
call.

So, (excusing the expository-but-fake array literals used to show
supplied arguments) where I reflectively call

  method.invoke(["first", "second", third"])

the corresponding handle call would be expected to be

  handle.invokeWithArguments("first", "second", "third")

Similary, with a varargs instance method (let's call it
Foo.foo(Object...) where the reflective call would be

  method.invoke(foo, ["first", "second", third"])

the corresponding handle call would be either

  handle.invokeWithArguments(foo, "first", "second", "third")

or

  handle.bindTo(foo).invokeWithArguments("first", "second", "third")

(I think that was what Remi was getting at :-)

Of course, since I package the arguments in generci code and then invoke
the method in code specific to JDK8[-] or JDK9[+] I am stuck with an
Object for the receiver (or null for a static call) and an Object[] for
the arguments. Hence, I cannot actually supply the arguments one by one
in the method handle call.

The fix I found is to detect the case where a method is varargs and in
that case use method asfixedArity() to convert the method handle
retrieved by findStatic or findVirtual to one which accepts the
arguments pre-wrapped.

So, where I currently have the following reflective code:

  Object receiver = ...
  Object[] args = ...

  Method method = ...
  boolean isStatic = ...

  if (isStatic) {
    method.invoke(null, args);
  } else {
    method.invoke(receiver, args);
  }

I simply need to replace the latter 5 lines as

  . . .
  MethodType methodType =
    MethodType.methodType(method.getReturnType(),
                          method.getParameterTypes());
  MethodHandle handle;

  if (isStatic) {
    handle = theLookup.findStatic(method.getDeclaringClass(),
                                  method.getName(),
                                  methodType);
  } else {
    handle = theLookup.findVirtual(method.getDeclaringClass(),
                                   method.getName(),
                                   methodType);
  }

  if (method.isVarArgs()) {
    handle = handle.asfixedArity();
  }

  if (isStatic) {
    handle.invokeWithArguments(args);
  } else {
    handle.bindTo(receiver).invokeWithArguments(args);
  }

This appears to have resolved my problem.

regards,


Andrew Dinn
-----------
Senior Principal Software Engineer
Red Hat UK Ltd
Registered in England and Wales under Company Registration No. 03798903
Directors: Michael Cunningham, Michael ("Mike") O'Neill, Eric Shander


More information about the jigsaw-dev mailing list