problem with vargs method
Volker Simonis
volker.simonis at gmail.com
Thu Feb 9 03:45:16 PST 2012
Jim's example works on 7u2 for me as well.
The cast is not without effect at all. It is crucial to have it!
That is because of the so called "Signature polymorphism" of the
'invokeExact()' and plain 'invoke()' methods (read the API
documentation for more details:
http://docs.oracle.com/javase/7/docs/api/index.html?java/lang/invoke/MethodHandles.html).
In short, 'invokeExact()' and 'invoke()' are declared as vararg
methods as you correctly noticed. But that's just to make them compile
and "operate with any of a wide range of call signatures and return
types". However at runtime the method is called with the "symbolic
type descriptor derived from the actual argument and return types, not
from the method declaration". And in the case of "invokeExect()" the
exact symbolic type descriptor has to match the signature of the
calle, otherwise you'll get a WrongMethodType exception.
I've slightly extended Jim's example to exactly match your desired target type:
==========================================================
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
public class Varargs {
public static void main(String[] args) {
try {
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 = MethodHandles.explicitCastArguments(mh, target);
Object constructor = mh.invokeExact((Object)Class.class, (Object)null);
System.out.println(constructor);
MethodType target1 = MethodType.methodType(Object.class,
Object.class, Object[].class);
mh = MethodHandles.explicitCastArguments(mh, target1);
constructor = mh.invokeExact((Object)Class.class, (Object[])new Class[0]);
System.out.println(constructor);
}
catch(Throwable e) {
System.out.println(e);
}
}
}
==========================================================
As mentioned before, it runs just fine with jdk7u2:
==========================================================
java -showversion -cp ~/Java/invokedynamic/ Varargs
java version "1.7.0_02"
Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
Java HotSpot(TM) 64-Bit Server VM (build 22.0-b10, mixed mode)
private java.lang.Class()
private java.lang.Class()
==========================================================
One general advice is to use the -XX:+TraceMethodHandles paramter to
trace the VM is handling MethodHandles. Unfortunately, this parameter
is only available in debug builds, but fortunately it's not too hard
to compile a debug version of the VM yourself. Yyou can than just
replace the original libjvm.so in the JDK directory with the one build
by you.
I would also recommend to use a new Hotspot version from
http://hg.openjdk.java.net/hsx/hotspot-main/hotspot for this purpose
which already has the change "7120468: SPARC/x86: use frame::describe
to enhance trace_method_handle". This makes the additional information
which you get with -XX:+TraceMethodHandles and -XX:+Verbose
-XX:+WizardMode a lot more readable.
Here's how that output looks for the following code snippet:
MethodType target = MethodType.methodType(Object.class,
Object.class, Object.class);
mh = MethodHandles.explicitCastArguments(mh, target);
Object constructor = mh.invokeExact((Object)Class.class, (Object)null);
System.out.println(constructor);
I've shoretend it nevertheless, because it is really verbose.
Nevertheless you can see how the adapter chain is walked which casts
the arguments to the required types and finally calls the actual
target of the method handle:
MH invokeExact
MethodHandle:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; : #(
java.lang.Object a1,
java.lang.Object a2
) => {
--- next target: java.lang.invoke.AdapterMethodHandle
mark(hash 0,age 0) - klass: 'java/lang/invoke/AdapterMethodHandle'
- ---- fields (total size 6 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 a
'java/lang/invoke/AdapterMethodHandle' (ecdcaa40 ecdb1108)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (ecdb1108 0)
- private final 'vmargslot' 'I' @32 0
- private final 'argument' 'Ljava/lang/Object;' @36 a
'java/lang/Class' = 'java/lang/Class'[] (ecd63828 cc200)
- private final 'conversion' 'I' @40 836096 (cc200)
convert object v3 = checkcast(a2, [Ljava.lang.Class;);
--- next target: java.lang.invoke.AdapterMethodHandle
mark(hash 0,age 0) - klass: 'java/lang/invoke/AdapterMethodHandle'
- ---- fields (total size 6 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 a
'java/lang/invoke/AdapterMethodHandle$AsVarargsCollector' (ecdb1420
ecdca9c8)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdca9c8 1)
- private final 'vmargslot' 'I' @32 1
- private final 'argument' 'Ljava/lang/Object;' @36 a
'java/lang/Class' = 'java/lang/Class' (ecd60b70 cc200)
- private final 'conversion' 'I' @40 836096 (cc200)
convert object v4 = checkcast(a1, java.lang.Class);
--- next target: java.lang.invoke.AdapterMethodHandle$AsVarargsCollector
mark(hash 0,age 0) - klass:
'java/lang/invoke/AdapterMethodHandle$AsVarargsCollector'
- ---- fields (total size 7 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 a
'java/lang/invoke/DirectMethodHandle' (ecdb1388 ecdb10d0)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdb10d0 2)
- private final 'vmargslot' 'I' @32 2
- private final 'argument' 'Ljava/lang/Object;' @36 NULL (0 0)
- private final 'conversion' 'I' @40 0
- final 'target' 'Ljava/lang/invoke/MethodHandle;' @44 a
'java/lang/invoke/DirectMethodHandle' (ecdb1388 ecd63828)
- final 'arrayType' 'Ljava/lang/Class;' @48 a 'java/lang/Class' =
'java/lang/Class'[] (ecd63828 ecdca930)
- 'cache' 'Ljava/lang/invoke/MethodHandle;' @52 a
'java/lang/invoke/AdapterMethodHandle' (ecdca930 1)
--- next target: java.lang.invoke.DirectMethodHandle
mark(hash 0,age 0) - klass: 'java/lang/invoke/DirectMethodHandle'
- ---- fields (total size 5 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 {method}
'getDeclaredConstructor'
'([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;' in
'java/lang/Class'[2,2] (c16124f0 ecdb10d0)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdb10d0 fffffffe)
- private final 'vmindex' 'I' @32 -2 (fffffffe)
return invokespecial
getDeclaredConstructor([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;(v4,
v3)
}
MH adapter_check_cast
MethodHandle:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; : #(
java.lang.Object a1,
java.lang.Object a2
) => {
--- next target: java.lang.invoke.AdapterMethodHandle
mark(hash 0,age 0) - klass: 'java/lang/invoke/AdapterMethodHandle'
- ---- fields (total size 6 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 a
'java/lang/invoke/AdapterMethodHandle' (ecdcaa40 ecdb1108)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; (ecdb1108 0)
- private final 'vmargslot' 'I' @32 0
- private final 'argument' 'Ljava/lang/Object;' @36 a
'java/lang/Class' = 'java/lang/Class'[] (ecd63828 cc200)
- private final 'conversion' 'I' @40 836096 (cc200)
convert object v3 = checkcast(a2, [Ljava.lang.Class;);
--- next target: java.lang.invoke.AdapterMethodHandle
mark(hash 0,age 0) - klass: 'java/lang/invoke/AdapterMethodHandle'
- ---- fields (total size 6 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 a
'java/lang/invoke/AdapterMethodHandle$AsVarargsCollector' (ecdb1420
ecdca9c8)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdca9c8 1)
- private final 'vmargslot' 'I' @32 1
- private final 'argument' 'Ljava/lang/Object;' @36 a
'java/lang/Class' = 'java/lang/Class' (ecd60b70 cc200)
- private final 'conversion' 'I' @40 836096 (cc200)
convert object v4 = checkcast(a1, java.lang.Class);
--- next target: java.lang.invoke.AdapterMethodHandle$AsVarargsCollector
mark(hash 0,age 0) - klass:
'java/lang/invoke/AdapterMethodHandle$AsVarargsCollector'
- ---- fields (total size 7 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 a
'java/lang/invoke/DirectMethodHandle' (ecdb1388 ecdb10d0)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdb10d0 2)
- private final 'vmargslot' 'I' @32 2
- private final 'argument' 'Ljava/lang/Object;' @36 NULL (0 0)
- private final 'conversion' 'I' @40 0
- final 'target' 'Ljava/lang/invoke/MethodHandle;' @44 a
'java/lang/invoke/DirectMethodHandle' (ecdb1388 ecd63828)
- final 'arrayType' 'Ljava/lang/Class;' @48 a 'java/lang/Class' =
'java/lang/Class'[] (ecd63828 ecdca930)
- 'cache' 'Ljava/lang/invoke/MethodHandle;' @52 a
'java/lang/invoke/AdapterMethodHandle' (ecdca930 1)
--- next target: java.lang.invoke.DirectMethodHandle
mark(hash 0,age 0) - klass: 'java/lang/invoke/DirectMethodHandle'
- ---- fields (total size 5 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 {method}
'getDeclaredConstructor'
'([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;' in
'java/lang/Class'[2,2] (c16124f0 ecdb10d0)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdb10d0 fffffffe)
- private final 'vmindex' 'I' @32 -2 (fffffffe)
return invokespecial
getDeclaredConstructor([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;(v4,
v3)
}
MH adapter_check_cast
MethodHandle:(Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
: #(
java.lang.Object a1,
[Ljava.lang.Class; a2
) => {
--- next target: java.lang.invoke.AdapterMethodHandle
mark(hash 0,age 0) - klass: 'java/lang/invoke/AdapterMethodHandle'
- ---- fields (total size 6 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 a
'java/lang/invoke/AdapterMethodHandle$AsVarargsCollector' (ecdb1420
ecdca9c8)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdca9c8 1)
- private final 'vmargslot' 'I' @32 1
- private final 'argument' 'Ljava/lang/Object;' @36 a
'java/lang/Class' = 'java/lang/Class' (ecd60b70 cc200)
- private final 'conversion' 'I' @40 836096 (cc200)
convert object v3 = checkcast(a1, java.lang.Class);
--- next target: java.lang.invoke.AdapterMethodHandle$AsVarargsCollector
mark(hash 0,age 0) - klass:
'java/lang/invoke/AdapterMethodHandle$AsVarargsCollector'
- ---- fields (total size 7 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 a
'java/lang/invoke/DirectMethodHandle' (ecdb1388 ecdb10d0)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdb10d0 2)
- private final 'vmargslot' 'I' @32 2
- private final 'argument' 'Ljava/lang/Object;' @36 NULL (0 0)
- private final 'conversion' 'I' @40 0
- final 'target' 'Ljava/lang/invoke/MethodHandle;' @44 a
'java/lang/invoke/DirectMethodHandle' (ecdb1388 ecd63828)
- final 'arrayType' 'Ljava/lang/Class;' @48 a 'java/lang/Class' =
'java/lang/Class'[] (ecd63828 ecdca930)
- 'cache' 'Ljava/lang/invoke/MethodHandle;' @52 a
'java/lang/invoke/AdapterMethodHandle' (ecdca930 1)
--- next target: java.lang.invoke.DirectMethodHandle
mark(hash 0,age 0) - klass: 'java/lang/invoke/DirectMethodHandle'
- ---- fields (total size 5 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 {method}
'getDeclaredConstructor'
'([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;' in
'java/lang/Class'[2,2] (c16124f0 ecdb10d0)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdb10d0 fffffffe)
- private final 'vmindex' 'I' @32 -2 (fffffffe)
return invokespecial
getDeclaredConstructor([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;(v3,
a2)
}
MH adapter_retype_only
MethodHandle:(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
: #(
java.lang.Class a1,
[Ljava.lang.Class; a2
) => {
--- next target: java.lang.invoke.AdapterMethodHandle$AsVarargsCollector
mark(hash 0,age 0) - klass:
'java/lang/invoke/AdapterMethodHandle$AsVarargsCollector'
- ---- fields (total size 7 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 a
'java/lang/invoke/DirectMethodHandle' (ecdb1388 ecdb10d0)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdb10d0 2)
- private final 'vmargslot' 'I' @32 2
- private final 'argument' 'Ljava/lang/Object;' @36 NULL (0 0)
- private final 'conversion' 'I' @40 0
- final 'target' 'Ljava/lang/invoke/MethodHandle;' @44 a
'java/lang/invoke/DirectMethodHandle' (ecdb1388 ecd63828)
- final 'arrayType' 'Ljava/lang/Class;' @48 a 'java/lang/Class' =
'java/lang/Class'[] (ecd63828 ecdca930)
- 'cache' 'Ljava/lang/invoke/MethodHandle;' @52 a
'java/lang/invoke/AdapterMethodHandle' (ecdca930 1)
--- next target: java.lang.invoke.DirectMethodHandle
mark(hash 0,age 0) - klass: 'java/lang/invoke/DirectMethodHandle'
- ---- fields (total size 5 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 {method}
'getDeclaredConstructor'
'([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;' in
'java/lang/Class'[2,2] (c16124f0 ecdb10d0)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdb10d0 fffffffe)
- private final 'vmindex' 'I' @32 -2 (fffffffe)
return invokespecial
getDeclaredConstructor([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;(a1,
a2)
}
MH invokespecial
MethodHandle:(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
: #(
java.lang.Class a1,
[Ljava.lang.Class; a2
) => {
--- next target: java.lang.invoke.DirectMethodHandle
mark(hash 0,age 0) - klass: 'java/lang/invoke/DirectMethodHandle'
- ---- fields (total size 5 words):
- private 'vmentry' 'B' @12 0
- 'vmtarget' 'Ljava/lang/Object;' @24 {method}
'getDeclaredConstructor'
'([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;' in
'java/lang/Class'[2,2] (c16124f0 ecdb10d0)
- private 'type' 'Ljava/lang/invoke/MethodType;' @28 a
'java/lang/invoke/MethodType' =
(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
(ecdb10d0 fffffffe)
- private final 'vmindex' 'I' @32 -2 (fffffffe)
return invokespecial
getDeclaredConstructor([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;(a1,
a2)
}
private java.lang.Class()
On Tue, Feb 7, 2012 at 6:17 PM, Jochen Theodorou <blackdrag at gmx.org> wrote:
>
> 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
>>
>> On 2012-02-07, at 12:37 PM, Jochen Theodorou wrote:
>>
>>> 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 "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 "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
>
> _______________________________________________
> mlvm-dev mailing list
> mlvm-dev at openjdk.java.net
> http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev
More information about the mlvm-dev
mailing list