All this is with the new JDK 17.
I'm attempting to turn an on-heap byte array into a MemorySegment and pass that to a native function. I created simple sample code that shows this:
        final CLinker cLinker = CLinker.getInstance();
        
        // int strlen(const char *str);
        final Optional<MemoryAddress> oSymbolAddress = CLinker.systemLookup().lookup("strlen");
        
        final MethodHandle mh = cLinker.downcallHandle(oSymbolAddress.get(),
                MethodType.methodType(int.class, MemoryAddress.class),
                FunctionDescriptor.of(C_INT, C_POINTER));        
        
        out.println("I found this method handle: " + mh);
        final byte[] ba = new byte[100];
        ba[0] = 'h';
        ba[1] = 'e';
        ba[2] = 'l';
        ba[3] = 'l';
        ba[4] = 'o';
        ba[5] = 0;
        
        final MemorySegment stringSegment = MemorySegment.ofArray(ba);
        final int result = (Integer) mh.invoke(stringSegment.address());
        
        out.println("The length of the string is: " + result);
It tries to run but it throws:
Exception in thread "main" java.lang.UnsupportedOperationException: Not a native address
    at jdk.incubator.foreign/jdk.internal.foreign.MemoryAddressImpl.toRawLongValue(MemoryAddressImpl.java:91)
If instead of using MemorySegment.ofArray(ba), I use this:
        final MemorySegment stringSegment = MemorySegment.allocateNative(100, newImplicitScope());
        stringSegment.asByteBuffer().put(ba);
it works and gives the expected answer (5).
I looked up this function in MemoryAddressImpl.java and I can see:
    @Override
    public long toRawLongValue() {
        if (segment != null) {
            if (segment.base() != null) {
                throw new UnsupportedOperationException("Not a native address");
            }
            segment.checkValidState();
        }
        return offset();
    }
Clearly segment.base() is returning null.
I don't really understand what's going on. I thought that one of the major advantages of Project Panama would be that native code could access on-heap memory, to avoid having to copy. I certainly can do an allocateNative() and then copy the byte array into that, but that's a copy that I think should be avoided.
Any ideas on this? Am I doing something wrong or misunderstanding how to use on-heap memory?