[foreign-memaccess] [Rev 02] RFR: Add unsigned adapter handles

Maurizio Cimadamore mcimadamore at openjdk.java.net
Mon May 18 16:01:26 UTC 2020

On Mon, 18 May 2020 15:51:58 GMT, Chris Hegarty <chegar at openjdk.org> wrote:

>> Hi,
>> As part of feedback on the Foreign Memory API (when experimenting with its usage internally in the JDK), a small number
>> of potential usability enhancements could be made to the API. This is the third such.
>> This change proposes to add a new method:
>>   MemoryHandles::asUnsigned
>> When dealing with _unsigned_ native data types it is often convenient to model them as
>> wider _signed_ Java primitives (with appropriate magnitude checks). For example, it is convenient to model an _unsigned
>> short_ as a Java _int_ to avoid dealing with negative values, which would be the case if modeled as a Java short. We do
>> this all the time in the JDK implementation, and it even bleeds into the APIs sometimes.  To model this, I found myself
>> reaching for `MemoryHandles::filterValue` to do the narrowing and widening, so that my layout class could expose the
>> set of memory handles with appropriate carrier types that the higher-level java code was expecting. This worked fine,
>> but Maurizio pointed out that this is something that the API should probably expose at a slightly higher level. Writing
>> these widening and narrowing adapters using _filterValue_ is not a lot of code, but easy to make mistakes and a place
>> for bugs to hide.  A single static adapter factory, `MemoryHandles::asUnsigned(VarHandle target, Class<?>
>> adaptedType)`, (thanks Maurizio for the suggestion) would be sufficient to provide such functionality. It adapts a
>> target var handle by narrowing incoming values and widening outgoing values, to and from the given type, respectively.
>> When calling _set_ on the resulting var handle, the incoming value (of type adaptedType) is converted by a narrowing
>> primitive conversion and then passed to the target var handle. Conversely, when calling _get_ on the resulting var
>> handle, the returned value obtained from the target var handle is converted by an unsigned widening conversion before
>> being returned to the caller.  We don't necessarily need to, or can, support all combinations, but there seems to be a
>> sweet spot where Java longs and ints can be used to model _unsigned char_, _unsigned short_, and _unsigned int_, [1]
>> which covers the majority of the use-cases. This also seems to align nicely with the primitive wrapper unsigned
>> widening methods, e.g. `Byte::toUnsignedInt`, `Byte::toUnsignedLong`, etc.  I started this discussion as a PR so that
>> hopefully the code changes can help convey the idea and inform readers.  Comments welcome.   [1]
>> https://cr.openjdk.java.net/~chegar/foreign/asUnsigned.pdf
> Chris Hegarty has updated the pull request incrementally with one additional commit since the last revision:
>   remove additional whitespace and comment line

Overall looks very good - some minor suggestions on the code

src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/UnsignedAdapters.java line 124:

> 123:         return MemoryHandles.filterValue(target, LONG_TO_SHORT, SHORT_TO_UNSIGNED_LONG);
> 124:     }
> 125:

I'm not super sure of the added value of these - the check is essentially a no op, since you already have a switch
where, based on the var handle carrier, you decide which method to call. So perhaps we can just inline the right MH
filter inside the switch above?

src/jdk.incubator.foreign/share/classes/jdk/internal/foreign/UnsignedAdapters.java line 146:

> 145:     private static void checkTargetWiderThanCarrier(Class<?> carrier, Class<?> target) {
> 146:         if (Wrapper.forPrimitiveType(target).bitWidth() <= Wrapper.forPrimitiveType(carrier).bitWidth()) {
> 147:             throw newIllegalArgumentException(

Note that MemoryHandles has already a routine to retrieve carrier size - maybe there's some reuse possible there


PR: https://git.openjdk.java.net/panama-foreign/pull/173

More information about the panama-dev mailing list