How to access a C_POINTER in a MemoryLayout using Java Panama FFI

Using the FFI preview in JDK 16, I have this memory layout:

class FfiTest {
    static GroupLayout layout = MemoryLayout.ofStruct(
        C_INT.withName("someInt"),
        MemoryLayout.ofPaddingBits(32), // So the following pointer is aligned at 64 bits
        C_POINTER.withName("somePtr")
    );
}

I then receive a pointer to such a structure in a callback from native code:

public static void someCallback(MemoryAddress address) {
    try (MemorySegment seg = address.asSegmentRestricted(FfiTest.layout.byteSize())) {                

        // Works: fetching int from native structure, correct value is returned
        VarHandle intHandle = FfiTest.layout.varHandle(int.class, MemoryLayout.PathElement.groupElement("someInt"));
        int intResult = (int) vh.get(seg);

        // Does not work: get the pointer as a MemoryAddress, fatal JVM crash with Hotspot log
        VarHandle badPtrHandle = FfiTest.layout.varHandle(MemoryAddress.class, MemoryLayout.PathElement.groupElement("somePtr"));

        // Works: get the pointer as a long, correct value is returned
        VarHandle goodPtrHandle = FfiTest.layout.varHandle(long.class, MemoryLayout.PathElement.groupElement("somePtr"));
        long longResult = (long) goodPtrHandle.get(seg);
    }
}

An exception is thrown inside the JDK code in jdk.internal.foreign.Utils:

    public static void checkPrimitiveCarrierCompat(Class<?> carrier, MemoryLayout layout) {
        checkLayoutType(layout, ValueLayout.class);
        if (!isValidPrimitiveCarrier(carrier))
            throw new IllegalArgumentException("Unsupported carrier: " + carrier); // Throws this exception, carrier has the value MemoryAddress
        if (Wrapper.forPrimitiveType(carrier).bitWidth() != layout.bitSize())
            throw new IllegalArgumentException("Carrier size mismatch: " + carrier + " != " + layout);
    }

According to the Panama documentation, the Java carrier for a C_POINTER is supposed to be MemoryAddress, but that does not work here.

So is it correct to use longs for accessing such pointers? Or something else?

Answer

As Johannes points out in the comments, MemoryHandles::asAddressVarHandle can be used to adapt the long handle you have to accept and return a MemoryAddress:

VarHandle goodPtrHandle = FfiTest.layout.varHandle(long.class, MemoryLayout.PathElement.groupElement("somePtr"));
long longResult = (long) goodPtrHandle.get(seg);
VarHandle addrHandle = MemoryHandles.asAddressVarHandle(goodPtrHandle);
MemoryAddress addrResult = (MemoryAddress) addrHandle.get(seg);

Leave a Reply

Your email address will not be published. Required fields are marked *