diff --git a/JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java b/JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java index 00264946..95870989 100644 --- a/JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java +++ b/JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java @@ -68,5 +68,12 @@ static void tests() { obj.voidMethod(); obj.takeIntMethod(42); + + MemorySegment swiftType = SwiftKit.getTypeByMangledNameInEnvironment("SiSg"); + System.out.println("Memory layout for Swift.Int?:"); + System.out.println(" size = " + SwiftKit.sizeOfSwiftType(swiftType)); + System.out.println(" stride = " + SwiftKit.strideOfSwiftType(swiftType)); + System.out.println(" alignment = " + SwiftKit.alignmentOfSwiftType(swiftType)); + System.out.println(" Java layout = " + SwiftKit.layoutOfSwiftType(swiftType)); } } diff --git a/JavaSwiftKitDemo/src/main/java/org/swift/javakit/SwiftKit.java b/JavaSwiftKitDemo/src/main/java/org/swift/javakit/SwiftKit.java index 07765a08..ba76232b 100644 --- a/JavaSwiftKitDemo/src/main/java/org/swift/javakit/SwiftKit.java +++ b/JavaSwiftKitDemo/src/main/java/org/swift/javakit/SwiftKit.java @@ -17,10 +17,12 @@ import org.swift.swiftkit.SwiftHeapObject; import java.lang.foreign.*; +import java.lang.foreign.MemoryLayout.PathElement; import java.lang.invoke.MethodHandle; import java.util.Arrays; import java.util.stream.Collectors; +import static java.lang.foreign.ValueLayout.ADDRESS; import static java.lang.foreign.ValueLayout.JAVA_BYTE; public class SwiftKit { @@ -61,6 +63,36 @@ static MemorySegment findOrThrow(String symbol) { .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); } + // ==== ------------------------------------------------------------------------------------------------------------ + // free + /** + * Descriptor for the free C runtime function. + */ + public static final FunctionDescriptor free$descriptor = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS + ); + + /** + * Address of the free C runtime function. + */ + public static final MemorySegment free$addr = findOrThrow("free"); + + /** + * Handle for the free C runtime function. + */ + public static final MethodHandle free$handle = Linker.nativeLinker().downcallHandle(free$addr, free$descriptor); + + /** + * free the given pointer + */ + public static void cFree(MemorySegment pointer) { + try { + free$handle.invokeExact(pointer); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + // ==== ------------------------------------------------------------------------------------------------------------ // swift_retainCount @@ -200,7 +232,7 @@ public static MemorySegment getTypeByName(String string) { */ private static class swift_getTypeByMangledNameInEnvironment { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /*returns=*/ValueLayout.ADDRESS, + /*returns=*/SWIFT_POINTER, ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, @@ -230,4 +262,182 @@ public static MemorySegment getTypeByMangledNameInEnvironment(String string) { throw new AssertionError("should not reach here", ex$); } } + + /** + * The value layout for Swift's Int type, which is a signed type that follows + * the size of a pointer (aka C's ptrdiff_t). + */ + public static ValueLayout SWIFT_INT = (ValueLayout.ADDRESS.byteSize() == 4) ? + ValueLayout.JAVA_INT : ValueLayout.JAVA_LONG; + + /** + * The value layout for Swift's UInt type, which is an unsigned type that follows + * the size of a pointer (aka C's size_t). Java does not have unsigned integer + * types in general, so we use the layout for Swift's Int. + */ + public static ValueLayout SWIFT_UINT = SWIFT_INT; + + /** + * Read a Swift.Int value from memory at the given offset and translate it into a Java long. + * + * This function copes with the fact that a Swift.Int might be 32 or 64 bits. + */ + public static final long getSwiftInt(MemorySegment memorySegment, long offset) { + if (SWIFT_INT == ValueLayout.JAVA_LONG) { + return memorySegment.get(ValueLayout.JAVA_LONG, offset); + } else { + return memorySegment.get(ValueLayout.JAVA_INT, offset); + } + } + + /** + * Value witness table layout. + */ + public static final MemoryLayout valueWitnessTableLayout = MemoryLayout.structLayout( + ValueLayout.ADDRESS.withName("initializeBufferWithCopyOfBuffer"), + ValueLayout.ADDRESS.withName("destroy"), + ValueLayout.ADDRESS.withName("initializeWithCopy"), + ValueLayout.ADDRESS.withName("assignWithCopy"), + ValueLayout.ADDRESS.withName("initializeWithTake"), + ValueLayout.ADDRESS.withName("assignWithTake"), + ValueLayout.ADDRESS.withName("getEnumTagSinglePayload"), + ValueLayout.ADDRESS.withName("storeEnumTagSinglePayload"), + SwiftKit.SWIFT_INT.withName("size"), + SwiftKit.SWIFT_INT.withName("stride"), + SwiftKit.SWIFT_UINT.withName("flags"), + SwiftKit.SWIFT_UINT.withName("extraInhabitantCount") + ).withName("SwiftValueWitnessTable"); + + /** + * Offset for the "size" field within the value witness table. + */ + static final long valueWitnessTable$size$offset = + valueWitnessTableLayout.byteOffset(PathElement.groupElement("size")); + + /** + * Offset for the "stride" field within the value witness table. + */ + static final long valueWitnessTable$stride$offset = + valueWitnessTableLayout.byteOffset(PathElement.groupElement("stride")); + + /** + * Offset for the "flags" field within the value witness table. + */ + static final long valueWitnessTable$flags$offset = + valueWitnessTableLayout.byteOffset(PathElement.groupElement("flags")); + + /** + * Type metadata pointer. + */ + public static final StructLayout fullTypeMetadataLayout = MemoryLayout.structLayout( + SWIFT_POINTER.withName("vwt") + ).withName("SwiftFullTypeMetadata"); + + /** + * Offset for the "vwt" field within the full type metadata. + */ + static final long fullTypeMetadata$vwt$offset = + fullTypeMetadataLayout.byteOffset(PathElement.groupElement("vwt")); + + /** + * Given the address of Swift type metadata for a type, return the addres + * of the "full" type metadata that can be accessed via fullTypeMetadataLayout. + */ + public static MemorySegment fullTypeMetadata(MemorySegment typeMetadata) { + return MemorySegment.ofAddress(typeMetadata.address() - SWIFT_POINTER.byteSize()) + .reinterpret(fullTypeMetadataLayout.byteSize()); + } + + /** + * Given the address of Swift type's metadata, return the address that + * references the value witness table for the type. + */ + public static MemorySegment valueWitnessTable(MemorySegment typeMetadata) { + return fullTypeMetadata(typeMetadata).get(SWIFT_POINTER, fullTypeMetadata$vwt$offset); + } + + /** + * Determine the size of a Swift type given its type metadata. + */ + public static long sizeOfSwiftType(MemorySegment typeMetadata) { + return getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$size$offset); + } + + /** + * Determine the stride of a Swift type given its type metadata, which is + * how many bytes are between successive elements of this type within an + * array. It is >= the size. + */ + public static long strideOfSwiftType(MemorySegment typeMetadata) { + return getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$stride$offset); + } + + /** + * Determine the alignment of the given Swift type. + */ + public static long alignmentOfSwiftType(MemorySegment typeMetadata) { + long flags = getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$flags$offset); + return (flags & 0xFF) + 1; + } + + /** + * Descriptor for the swift_getTypeName runtime function. + */ + public static final FunctionDescriptor swift_getTypeName$descriptor = FunctionDescriptor.of( + /*returns=*/MemoryLayout.structLayout( + SWIFT_POINTER.withName("utf8Chars"), + SWIFT_INT.withName("length") + ), + ValueLayout.ADDRESS, + ValueLayout.JAVA_BOOLEAN + ); + + /** + * Address of the swift_getTypeName runtime function. + */ + public static final MemorySegment swift_getTypeName$addr = findOrThrow("swift_getTypeName"); + + /** + * Handle for the swift_getTypeName runtime function. + */ + public static final MethodHandle swift_getTypeName$handle = Linker.nativeLinker().downcallHandle(swift_getTypeName$addr, swift_getTypeName$descriptor); + + /** + * Produce the name of the Swift type given its Swift type metadata. + * + * If 'qualified' is true, leave all of the qualification in place to + * disambiguate the type, producing a more complete (but longer) type name. + */ + public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualified) { + try { + try (Arena arena = Arena.ofConfined()) { + MemorySegment charsAndLength = (MemorySegment)swift_getTypeName$handle.invokeExact((SegmentAllocator)arena, typeMetadata, qualified); + MemorySegment utf8Chars = charsAndLength.get(SWIFT_POINTER, 0); + String typeName = utf8Chars.getString(0); + cFree(utf8Chars); + return typeName; + } + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Produce a layout that describes a Swift type based on its + * type metadata. The resulting layout is completely opaque to Java, but + * has appropriate size/alignment to model the memory associated with a + * Swift type. + * + * In the future, this layout could be extended to provide more detail, + * such as the fields of a Swift struct. + */ + public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { + long size = sizeOfSwiftType(typeMetadata); + long stride = strideOfSwiftType(typeMetadata); + return MemoryLayout.structLayout( + MemoryLayout.sequenceLayout(size, JAVA_BYTE) + .withByteAlignment(alignmentOfSwiftType(typeMetadata)), + MemoryLayout.paddingLayout(stride - size) + ).withName(nameOfSwiftType(typeMetadata, true)); + } }