problem with vargs method

66 views
Skip to first unread message

Jochen Theodorou

unread,
Feb 7, 2012, 11:04:30 AM2/7/12
to Da Vinci Machine Project, jvm-la...@googlegroups.com
Hi all,

maybe someone can explain to me why method handles behave this way in my
case.

Bascially I have a handle for Class#getDeclaredConstructor(Class...) I
created via unreflect.

http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html#getDeclaredConstructor%28java.lang.Class...%29

this handle is then created as a AdapterMethodHandle$AsVargsCollector.
My targetType is Object(Object,Object) and the arguments to the call are
the Class, and an empty Class[]. So the next steps are asType with the
target type, adding a guard with catch, then my guards for the arguments
and last I do an invokeWithArguments.

What I get now is a ClassCastException with the message required class
java.lang.Class but encountered class [Ljava.lang.Class;

The guards are unrelated to the problem, since it happens without them
as well.

Now can anyone explain me why I get that exception? It doesn't really
make sense to me atm.

bye Jochen

--
Jochen "blackdrag" Theodorou - Groovy Project Tech Lead
blog: http://blackdragsview.blogspot.com/
german groovy discussion newsgroup: de.comp.lang.misc
For Groovy programming sources visit http://groovy-lang.org

Jochen Theodorou

unread,
Feb 7, 2012, 11:14:50 AM2/7/12
to Da Vinci Machine Project, jvm-la...@googlegroups.com
The problem can be easily reproduced using this:
> MethodType type = MethodType.methodType(Constructor.class, Class[].class);
> MethodHandle mh = MethodHandles.lookup().findVirtual(Class.class, "getDeclaredConstructor", type);
> MethodType target = MethodType.methodType(Object.class, Object.class, Object.class);
> mh = mh.asType(target);
> mh.invokeWithArguments(Class.class,new Class[0]);

can someone tell me the mistake?

bye Jochen

Jochen Theodorou

unread,
Feb 7, 2012, 11:37:12 AM2/7/12
to Da Vinci Machine Project, Jim Laskey, jvm-la...@googlegroups.com
Am 07.02.2012 17:29, schrieb Jim Laskey:
>>>> MethodType type = MethodType.methodType(Constructor.class, Class[].class);
>>>> MethodHandle mh = MethodHandles.lookup().findVirtual(Class.class, "getDeclaredConstructor", type);
>>>> MethodType target = MethodType.methodType(Object.class, Object.class, Object.class);
>>>> mh = mh.asType(target);
>>>> mh.invokeWithArguments(Class.class,new Class[0]);
>
> As soon as you mh = mh.asType(target); it is no longer vararg, so it
> is treating new Class[0] as the second argument cast to Object. If
> you are trying to type as (Object , Object[]). I think you are going
> to run into difficulties validating (Class[]) Object[]. You may have
> to add a wrapper to get what you want, but you could also try using
> asCollector.

or in other words: I should not use invokeWithArguments for this.

If I wanted to use the same target type... since that is what my call
site gives me... and I wanted to use invokeExact instead, how would I
have to change the program?

bye blackdrag

Jochen Theodorou

unread,
Feb 7, 2012, 12:17:52 PM2/7/12
to Jim Laskey, Da Vinci Machine Project, jvm-la...@googlegroups.com

That will produce a

java.lang.invoke.WrongMethodTypeException:
(Ljava/lang/Object;[Ljava/lang/Object;)V cannot be called without a
receiver argument as ([Ljava/lang/Object;)Ljava/lang/Object;

imho casting to Object and Object[] in

mh.invokeExact((Object)Class.class, (Object[])new Class[0]);

is without effect, since invokeExact is a vargs method an everything
will just be put into an Object[] anyway. We had not a compiler change
for this kind of thing, had we?

bye Jochen

Am 07.02.2012 17:52, schrieb Jim Laskey:
> Try


>
> MethodType type = MethodType.methodType(Constructor.class, Class[].class);
> MethodHandle mh = MethodHandles.lookup().findVirtual(Class.class, "getDeclaredConstructor", type);

> MethodType target = MethodType.methodType(void.class, Object.class, Object[].class);
> mh = MethodHandles.explicitCastArguments(mh, target);
> mh.invokeExact((Object)Class.class, (Object[])new Class[0]);
>
> Cheers,
>
> -- Jim

Jochen Theodorou

unread,
Feb 7, 2012, 1:38:53 PM2/7/12
to Jim Laskey, Da Vinci Machine Project, jvm-la...@googlegroups.com
Am 07.02.2012 18:29, schrieb Jim Laskey:
> Worked okay for me. So must be addressed in a later release. :-/

later than jdk7u2? oh boy. I would feel better if I could find a bug
report that shows the problem and that is resolved. Then I would at
least have something for the release notes. But I didn't find anything
that looks fitting. The only one that seems to be slightly fitting was
fixed a year ago and surely that fix is part of jdk7u2 already.

You said for


> MethodType type = MethodType.methodType(Constructor.class, Class[].class);
> MethodHandle mh = MethodHandles.lookup().findVirtual(Class.class, "getDeclaredConstructor", type);
> MethodType target = MethodType.methodType(Object.class, Object.class, Object.class);
> mh = mh.asType(target);
> mh.invokeWithArguments(Class.class,new Class[0]);

I probably have to wrap something... can you explain what you mean?

bye Jochen

Rémi Forax

unread,
Feb 7, 2012, 2:14:59 PM2/7/12
to jvm-la...@googlegroups.com
On 02/07/2012 07:38 PM, Jochen Theodorou wrote:
> Am 07.02.2012 18:29, schrieb Jim Laskey:
>> Worked okay for me. So must be addressed in a later release. :-/
>
> later than jdk7u2? oh boy. I would feel better if I could find a bug
> report that shows the problem and that is resolved. Then I would at
> least have something for the release notes. But I didn't find anything
> that looks fitting. The only one that seems to be slightly fitting was
> fixed a year ago and surely that fix is part of jdk7u2 already.
>
> You said for
>> MethodType type = MethodType.methodType(Constructor.class,
>> Class[].class);
>> MethodHandle mh = MethodHandles.lookup().findVirtual(Class.class,
>> "getDeclaredConstructor", type);
>> MethodType target = MethodType.methodType(Object.class, Object.class,
>> Object.class);
>> mh = mh.asType(target);
>> mh.invokeWithArguments(Class.class,new Class[0]);
>
> I probably have to wrap something... can you explain what you mean?
>
> bye Jochen

Jochen,
I believe you have found a bug in Eclipse (I suppose you use Eclipse).

mh.invoke() is compiled as (Object)mh.invoke() by Eclipse instead of
(void)mh.invoke(),
hence that's why it works for Jim and not for you.

Now, how varargs works.
when you create a method handle with lookup.find* or lookup.unreflect()
on a method
which is a varargs (comp�led with the ACC_VARARGS bit) then
the created method handle has its varargs collector bit set.
A way to test if the varargs collector bit is set or not is to call,
methodHandle.isVarargCollector().

A method handle with the a varargs collector bit set is able to group
arguments in an array
only if invoked with invoke() or invokeWithArguments() but not with
invokeExact which
never do any boxing or varargs magics.

Also, when asType() is called on a method handle, the varargs collector
bit set is lost,
so if you still want a varargs call you need to call
asVarargsCollector() to set the bit.

As a meta-note, invoking a method handle with invoke or invokeArguments and
the varargs collector bit set is slow. It's only intended to be use when
you want to call a lambda/closure that is defined as a varargs.
Otherwise you should use asCollector which is way faster (at least
currently with Hotspot).

cheers,
R�mi

Jochen Theodorou

unread,
Feb 7, 2012, 3:22:21 PM2/7/12
to jvm-la...@googlegroups.com
Am 07.02.2012 20:14, schrieb R�mi Forax:
[...]

> Jochen,
> I believe you have found a bug in Eclipse (I suppose you use Eclipse).
>
> mh.invoke() is compiled as (Object)mh.invoke() by Eclipse instead of
> (void)mh.invoke(),
> hence that's why it works for Jim and not for you.

No actually your long explanation gave me the idea of what is wrong

> Now, how varargs works.
> when you create a method handle with lookup.find* or lookup.unreflect()
> on a method
> which is a varargs (comp�led with the ACC_VARARGS bit) then
> the created method handle has its varargs collector bit set.
> A way to test if the varargs collector bit is set or not is to call,
> methodHandle.isVarargCollector().
>
> A method handle with the a varargs collector bit set is able to group
> arguments in an array
> only if invoked with invoke() or invokeWithArguments() but not with
> invokeExact which
> never do any boxing or varargs magics.

ok, explicitCastArguments will do probably needed casting and boxing for
me, and since Groovy does the vargs logic on its own, there seems to be
no reason to still use invokeWithArguments anymore. I only failed to
understand invokeExact till now.

What I stupidly failed to see is that invokeExact itself realizes a call
site and thus has a different target type.

so what I do now is

> MethodHandle call = handle.asSpreader(Object[].class, args.length);
> call = call.asType(MethodType.methodType(Object.class,Object[].class));

and then I can do my

call.invokeExact(args)

without problem it seems.

Reply all
Reply to author
Forward
0 new messages