RFR: 7903649: Field and global variables of array type should have indexed accessors

Maurizio Cimadamore mcimadamore at openjdk.org
Tue Jan 30 16:17:38 UTC 2024


On Tue, 30 Jan 2024 14:43:15 GMT, Maurizio Cimadamore <mcimadamore at openjdk.org> wrote:

>> FTR, if the constant class is called `<varName>$constants` all the ambiguities issues are resolved, and all tests pass. But, we're back with dollar names :-)
>> 
>> If we're fine with dollar names for "less common API points", we could settle by having a single extra layout accessor/descriptor accessor per var/field/function. I think the layout API should expose an handy method for computing dimensions of a possibly nested sequence layout.
>
>> we could settle by having a single extra layout accessor/descriptor accessor per var/field/function
> 
> Although, I think I like the idea of exposing multiple related constants under a single umbrella - given that in most cases we generate the holder class anyway. In many cases there seem to be multiple interesting constants for a given symbol - and if we were to expose different constant accessors, the constant accessor would quickly outnumber the _real_ API point, which would then lead us back in a situation of poor API discoverability.
> 
> In other words, while reserving the "good name" to the common API points remains a good move, if such good names are buried under several "less than good names", the API quickly becomes bloated, which is still problematic, unfortunately.

I've put together another branch where I fully explored the idea of exposing constants (for global vars, functions and struct fields) in a separate holder class:

https://github.com/openjdk/jextract/compare/panama...mcimadamore:jextract:public_holders?expand=1

For globals and functions, not much changes (as we needed an holder class even before this).

For structs, additional holder classes are generated (one per field). For instance, here's what `Point` looks like:


/**
 * {@snippet lang=c :
 * struct Point {
 *     int x;
 *     int y;
 * }
 * }
 */
public class Point {

    Point() {
        // Should not be called directly
    }

    private static final GroupLayout $LAYOUT = MemoryLayout.structLayout(
        foo_h.C_INT.withName("x"),
        foo_h.C_INT.withName("y")
    ).withName("Point");

    /**
     * The layout of this struct
     */
    public static final GroupLayout layout() {
        return $LAYOUT;
    }

    public static class x$constants {
        public static final OfInt LAYOUT = (OfInt)layout().select(groupElement("x"));
        public static final long OFFSET = layout().byteOffset(groupElement("x"));
    }

    /**
     * Getter for field:
     * {@snippet lang=c :
     * int x
     * }
     */
    public static int x(MemorySegment struct) {
        return struct.get(x$constants.LAYOUT, x$constants.OFFSET);
    }

    /**
     * Setter for field:
     * {@snippet lang=c :
     * int x
     * }
     */
    public static void x(MemorySegment struct, int fieldValue) {
        struct.set(x$constants.LAYOUT, x$constants.OFFSET, fieldValue);
    }

    public static class y$constants {
        public static final OfInt LAYOUT = (OfInt)layout().select(groupElement("y"));
        public static final long OFFSET = layout().byteOffset(groupElement("y"));
    }

    /**
     * Getter for field:
     * {@snippet lang=c :
     * int y
     * }
     */
    public static int y(MemorySegment struct) {
        return struct.get(y$constants.LAYOUT, y$constants.OFFSET);
    }

    /**
     * Setter for field:
     * {@snippet lang=c :
     * int y
     * }
     */
    public static void y(MemorySegment struct, int fieldValue) {
        struct.set(y$constants.LAYOUT, y$constants.OFFSET, fieldValue);
    }

    /**
     * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}.
     * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()}
     */
    public static MemorySegment asSlice(MemorySegment array, long index) {
        return array.asSlice(layout().byteSize() * index);
    }

    /**
     * The size (in bytes) of this struct
     */
    public static long sizeof() { return layout().byteSize(); }

    /**
     * Allocate a segment of size {@code layout().byteSize()} using {@code allocator}
     */
    public static MemorySegment allocate(SegmentAllocator allocator) {
        return allocator.allocate(layout());
    }

    /**
     * Allocate an array of size {@code elementCount} using {@code allocator}.
     * The returned segment has size {@code elementCount * layout().byteSize()}.
     */
    public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) {
        return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout()));
    }

    /**
     * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction) (if any).
     * The returned segment has size {@code layout().byteSize()}
     */
    public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer<MemorySegment> cleanup) {
        return reinterpret(addr, 1, arena, cleanup);
    }

    /**
     * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction) (if any).
     * The returned segment has size {@code elementCount * layout().byteSize()}
     */
    public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer<MemorySegment> cleanup) {
        return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup);
    }
}


Maybe this is too much - however I like how tidy the constants are under the dedicated constant class.

-------------

PR Review Comment: https://git.openjdk.org/jextract/pull/198#discussion_r1471469825


More information about the jextract-dev mailing list