Strict long type casting

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Tue Apr 7 09:59:47 UTC 2020


On 07/04/2020 02:54, Ty Young wrote:
>
> On 4/6/20 8:12 PM, Maurizio Cimadamore wrote:
>> Hi Ty,
>> I'd need to have a look at the code of MemoryNumber/MemoryLong to 
>> understand what's going on.
>
>
> The abstract class can be found here:
>
>
> https://github.com/BlueGoliath/Crosspoint/blob/master/src/main/java/org/goliath/crosspoint/abstracts/NativeNumber.java 
>
>
>
> implementation:
>
>
> https://github.com/BlueGoliath/Crosspoint/blob/master/src/main/java/org/goliath/crosspoint/numbers/NativeLong.java 
>
>
>
> (classes have just been renamed. Same code)
>
>
> Basic usage:
>
>
> NativeLong test = new NativeLong();
>
> test.setValue(Long.MAX_VALUE);
>
> System.out.println(test.byteValue());
>
>
> Which fails with:
>
>
> Caused by: java.lang.invoke.WrongMethodTypeException: cannot convert 
> MethodHandle(VarHandle,MemoryAddress)long to (VarHandle,MemoryAddress)int
>     at 
> java.base/java.lang.invoke.MethodHandle.asTypeUncached(MethodHandle.java:881)
>     at 
> java.base/java.lang.invoke.MethodHandle.asType(MethodHandle.java:866)
>     at 
> java.base/java.lang.invoke.VarHandleGuards.guard_L_I(VarHandleGuards.java:106)
>     at 
> org.goliath.crosspoint/org.goliath.crosspoint.abstracts.NativeNumber.intValue(NativeNumber.java:46)
>     at java.base/java.lang.Number.byteValue(Number.java:104)

This seems expected; intValue() does this:

return (int)this.handle.get(this.address);

You have a var handle whose carrier type is a long; but the client is 
enforcing a cast to int - meaning that the method type used to do the 
get will be something like

(MemoryAddress) -> int

The set of allowed conversions are described here:

https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/invoke/MethodHandle.html#asType(java.lang.invoke.MethodType)

More specifically, primitive "widening" conversions are allowed. Going 
from a "long" to an "int" is not a widening. For more information about 
widening conversions, see here:

https://docs.oracle.com/javase/specs/jls/se14/html/jls-5.html#jls-5.1.2

I suspect that if you setup a matrix test where you call all the 
XYZvalue() methods on _all_ the NativeXYZ instances you will see more 
failures like that. E.g. I would expect NativeDouble to suffer from same 
issues.


Now, back to your problem, I don't think there's a way to express all 
the XYZvalue() methods generically as you are trying to do in the 
abstract class. Well, there are ways, but they are very roundabout - for 
instance you could:

* turn the VarHandle into a MethodHandle and then use 
MethodHandles.explicitCastArguments - which does a true cast:

https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/invoke/MethodHandles.html#explicitCastArguments(java.lang.invoke.MethodHandle,java.lang.invoke.MethodType)

* Or you could adapt the VarHandle using the MethodHandles.filterValue 
combinator we have added, to turn the long VH into a truncating int VH


But, I believe, the best way to address this is to replicate the 
XYZvalue() code in the various subclasses, so that you insert all the 
correct cast - for instance you can do this:


class NativeLong<...> {
...
int intValue() {
    return (int)(long)this.handle.get(this.address);
}
}

This should address the issue, and you should also be able to make the 
code "more static" since, I suspect, the VarHandle to be used in a 
NativeLong is always the same and can be cached statically - this gives 
you quite a bit of a boost when using it to access off heap memory as 
the JIT compiler is then able to inline the VarHandle code in the caller.

Maurizio

>
>
> ...but only with long.
>
>
>
>>
>> Thanks
>> Maurizio
>>
>> On 07/04/2020 01:49, Ty Young wrote:
>>> Hi all,
>>>
>>>
>>> I'm trying to create a way of reading primitive number based struct 
>>> values(since VarHandle.get is polymorphic) by combining Java's 
>>> Number abstract class with my abstraction like so:
>>>
>>>
>>> public abstract class MemoryNumber<E extends Number> extends Number 
>>> implements MemoryValue<E>
>>>
>>>
>>> Which is then extended by classes like "MemoryLong".
>>>
>>>
>>> The problem here is that with long(and only long) the type casting 
>>> is strict and fails. Attempting to call byteValue() fails on 
>>> intValue(). This doesn't, for whatever reason, happen with normal 
>>> on-heap long values nor does it happen with an MemoryInt 
>>> implementation either.
>>>
>>>
>>> I tried looking in the source code of Long.class and I couldn't find 
>>> anything that I was doing differently besides using Panama, so I'm a 
>>> bit lost here. Does anyone know what's going on?
>>>


More information about the panama-dev mailing list