Question about MethodHandles#explicitCastArgument

39 views
Skip to first unread message

Jochen Theodorou

unread,
Nov 14, 2012, 9:16:48 AM11/14/12
to jvm-la...@googlegroups.com
Hi,

I have a call to MethodHandles#explicitCastArgument with first argument
being my
MethodHandle(Groovy3135Bug,String,float,float,float,float,float)Object
and the type I cast to being
(Groovy3135Bug,String,Byte,Short,Integer,Long,Float)Object

Ignoring the first two I should have some kind of unboxing followed by
casting for most arguments. In the javadoc to that method I read:
"""
If T0 is a reference and T1 a primitive, an unboxing conversion will be
applied at runtime, possibly followed by a Java casting conversion (JLS
5.5) on the primitive value, possibly followed by a conversion from byte
to boolean by testing the low-order bit.
"""

Aren't T0 and T1 in my case for example T0=Byte and T1=float? Unless I
mix up T0 and T1, this should be working, or not?

Well... it fails with a NPE... that can't be right, can it? If I first
cast to an all-Object signature, and then to the target, then it
works... but that can't be how it should be, or not?

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

_______________________________________________
mlvm-dev mailing list
mlvm...@openjdk.java.net
http://mail.openjdk.java.net/mailman/listinfo/mlvm-dev

John Rose

unread,
Nov 15, 2012, 6:24:08 PM11/15/12
to jvm-la...@googlegroups.com
On Nov 14, 2012, at 6:16 AM, Jochen Theodorou wrote:

I have a call to MethodHandles#explicitCastArgument with first argument being my MethodHandle(Groovy3135Bug,String,float,float,float,float,float)Object and the type I cast to being (Groovy3135Bug,String,Byte,Short,Integer,Long,Float)Object

Ignoring the first two I should have some kind of unboxing followed by casting for most arguments. In the javadoc to that method I read:
"""
If T0 is a reference and T1 a primitive, an unboxing conversion will be applied at runtime, possibly followed by a Java casting conversion (JLS 5.5) on the primitive value, possibly followed by a conversion from byte to boolean by testing the low-order bit.
"""

Aren't T0 and T1 in my case for example T0=Byte and T1=float? Unless I mix up T0 and T1, this should be working, or not?

Yes, you are reading it right.  When the call to the MH eventually happens, the actual argument of type T0=java.lang.Byte will be explicitly cast to the required argument type T1=float.

Well... it fails with a NPE... that can't be right, can it?

It can be right, but only if the actual argument is a null reference.  If it is a non-null Byte, it must unbox to a byte and then widen to a float.

Here's the relevant doc from MethodHandle#asType:
An unboxing operation may fail because the original reference is null,
causing a {@link java.lang.NullPointerException NullPointerException}.
An unboxing operation or a reference cast may also fail on a reference
to an object of the wrong type,
causing a {@link java.lang.ClassCastException ClassCastException}.

What you are seeing is a bug.  I can reproduce this in JDK 7 but not in JDK 8.  The upcoming JDK 7 update will fix this, since it is a backport from 8.

If I first cast to an all-Object signature, and then to the target, then it works... but that can't be how it should be, or not?

The difference between Object and Byte in that context is that the Object must be a wrapper object of any sort, whereas Byte is only a wrapped byte.  In the first case a CCE is possible.  In both cases an NPE is possible.  The second case is probably more efficient, since the JVM doesn't need to test for multiple wrapper types.

Thanks for the report.

— John

P.S.  Here's my test:

import java.lang.invoke.*;
import static java.lang.invoke.MethodHandles.*;
import static java.lang.invoke.MethodType.*;

public class ByteBug {
    public static void main(String... av) throws Throwable {
        MethodHandle id_f = identity(float.class);
        System.out.println((float)id_f.invokeExact((float)42.1));
        MethodHandle id_O = id_f.asType(methodType(Object.class, Object.class));
        System.out.println(id_O.invokeExact((Object)(float)42.2));
        System.out.println(id_O.invokeExact((Object)(long)43));
        System.out.println(id_O.invokeExact((Object)(byte)44));
        try {
            System.out.println(id_O.invokeExact((Object)(double)42.99)); //CCE Double=>Float
            throw new AssertionError("notreached");
        } catch (ClassCastException ex) {
            ex.printStackTrace();
        }
        MethodHandle id_b = id_f.asType(methodType(Object.class, byte.class));
        System.out.println(id_b.invokeExact((byte)45));
        MethodHandle id_B = id_f.asType(methodType(Object.class, Byte.class));
        System.out.println(id_B.invokeExact((Byte)(byte)46));
    }
}
/* output:
42.1
42.2
43.0
44.0
java.lang.ClassCastException: Cannot cast java.lang.Double to java.lang.Float
at java.lang.Class.cast(Class.java:3007)
at sun.invoke.util.ValueConversions.primitiveConversion(ValueConversions.java:203)
at sun.invoke.util.ValueConversions.unboxFloat(ValueConversions.java:112)
at ByteBug.main(ByteBug.java:14)
45.0
46.0
 */

John Rose

unread,
Nov 15, 2012, 6:31:42 PM11/15/12
to JVM Languages
On Nov 15, 2012, at 3:24 PM, John Rose wrote:

> What you are seeing is a bug. I can reproduce this in JDK 7 but not in JDK 8. The upcoming JDK 7 update will fix this, since it is a backport from 8.

Oops, I see Jochen sent this to mlvm-dev also. I will repost my answer and take replies there, since mlvm-dev is more appropriate for discussion of implementation bugs.

— John

Reply all
Reply to author
Forward
0 new messages