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