A few considerations (mostly) on jextract

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Mon May 17 20:27:04 UTC 2021

On 17/05/2021 19:14, Markus Knetschke wrote:
> On 17/5/2021 11:54, Maurizio Cimadamore wrote:
>>> 1) the classes for structs jextract generates don't feel very oo to
>>> me. I would like to see a class with constructor and getter/setter
>>> methods encapsulating the MemorySegment.
>> I hear you - this is indeed possible, at the cost of more allocation.
>> For now we have tried to stick to a principle that jextract should not
>> add overhead, but I tend to agree that, in the case of struct, this ends
>> up being punitive at times (especially when you have structs nested
>> inside structs nested inside structs).
>> I believe at some point some other experiment will be made to make
>> struct handling more OO (as you say), and, if we can get some help from
>> Valhalla primitive types, maybe we won't even pay a performance price!
> Don't get me wrong, I don't suggest removing the
> public static  MemoryAddress xxx$get(MemorySegment seg)
> accessors but add a MemorySegment field, a constructor, and accessors.
> So everyone can choose if one uses the static methods or calls the constructor.
> Bonus Points if jextract generates additional downcalls extracting the
> MemorySegment from wrapper objects and upcall wrapper calling
> the constructor of the wrapper objects.
> I've tried a simple annotation processor which works well
> when there is a solution for 2). So it's not the highest priority
> and I guess we get at least 2 release cycles before jextract
> reaches preview status. Enough time to experiment with multiple variants.

So you are suggesting to offer both variants - both static (which is 
what we have) and object-oriented, with instance methods.

Yeah - the two might definitively coexist, and you are right that, there 
are, in general, good use cases for both.

>>> 3) I often encounter fixed-length arrays in structs holding stings.
>>> The best way I've found to extract them safely is to first get the
>>> start offset of the string with MemoryLayout.byteOffset(path) then
>>> extracting the size of the struct field with
>>> MemoryLayout.select(path).byteSize() and reading the string with
>>> CLinker.toJavaString(struct.asSlice(offset, size)) this is very bulky
>>> for simple things like putting ten strings from a struct into a
>>> record. I would like to see a simpler way for this for example a
>>> MemorySegment.asSlice(MemoryLayout, PathElement...) method. (This
>>> would be handy too for byte array struct fields.
>> In the new API, we have a new method in MemoryLayout like this:
>> default MethodHandle sliceHandle(PathElement... elements) {
>> Which I think does what you want?
> It looks a bit better than my own public static asSlice method but I'm not sure
> if a task simple as reading a fixed-length string of a struct should
> be as long as
> CLinker.toJavaString((MemorySegment)
> getSubvolInfoIoctlArgs.sliceHandle(groupElement("name")).invoke(ioctlArg))
> or reading a fixed number of bytes
> ((MemorySegment)
> getSubvolInfoIoctlArgs.sliceHandle(groupElement("parent_uuid")).invoke(ioctlArg)).toByteArray()
> having a
> MethodHandle(MemorySegment, PathElement...)MemorySegment
> would be more useful because you could .bindTo() the struct and access multiple
> fields through it. Bringing the aboth example to:
> var getSlice = getSubvolInfoIoctlArgs.sliceHandle().bindTo(ioctlArg)
> CLinker.toJavaString((MemorySegment) getSlice.invoke(groupElement("name"))
> ((MemorySegment) getSlice.invoke(groupElement("parent_uuid"))).toByteArray()
> But with this, the needed cast and the try {} catch (Throwable ignored) {}
> remain and are looking very ugly in my mind.
> On the other hand, it's only a problem if you don't want to use jextract.

I'll need to think more about this - but it seems to me that with the 
asSlice MethodHandle you want to build up as much access as you can 
before hand. For instance, you could create the sliceHandle before hand 
and stick it into a static final field (as that's the only way the MH 
will optimize properly). Then you can combine the slice method handle 
with a method handle for CLinker::toJavaString (using 
filterReturnValue). That way you can simply access the fixed length 
string by calling a single method handle, and, since the method handle 
is static, it will be optimized accordingly.

In other words, path elements are static coordinates which you should 
use to build var/method handles, which you then store in static fields. 
That way access expression is optimized all the way down. Having a 
method handle whose access expression is "parametric" means that there's 
no way for the underlying access to be optimized.


> Best regards,
> Markus Knetschke

More information about the panama-dev mailing list