Skip to content

Commit 8c69803

Browse files
committed
Model Swift's type metadata and value witness table for memory layout information
Extend the Java SwiftKit with the ability to query the memory layout of an arbitrary Swift type given its Swift type metadata pointer (e.g., `Any.Type` in the Swift world). Use this to show the size, stride, and alignment of `Int?` from the Java side, which produces: Memory layout for Swift.Int?: size = 9 stride = 16 alignment = 8
1 parent ddef48a commit 8c69803

File tree

2 files changed

+125
-1
lines changed

2 files changed

+125
-1
lines changed

JavaSwiftKitDemo/src/main/java/org/example/HelloJava2Swift.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,11 @@ static void tests() {
6868

6969
obj.voidMethod();
7070
obj.takeIntMethod(42);
71+
72+
MemorySegment swiftType = SwiftKit.getTypeByMangledNameInEnvironment("SiSg");
73+
System.out.println("Memory layout for Swift.Int?:");
74+
System.out.println(" size = " + SwiftKit.sizeOfSwiftType(swiftType));
75+
System.out.println(" stride = " + SwiftKit.strideOfSwiftType(swiftType));
76+
System.out.println(" alignment = " + SwiftKit.alignmentOfSwiftType(swiftType));
7177
}
7278
}

JavaSwiftKitDemo/src/main/java/org/swift/javakit/SwiftKit.java

Lines changed: 119 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.swift.swiftkit.SwiftHeapObject;
1818

1919
import java.lang.foreign.*;
20+
import java.lang.foreign.MemoryLayout.PathElement;
2021
import java.lang.invoke.MethodHandle;
2122
import java.util.Arrays;
2223
import java.util.stream.Collectors;
@@ -200,7 +201,7 @@ public static MemorySegment getTypeByName(String string) {
200201
*/
201202
private static class swift_getTypeByMangledNameInEnvironment {
202203
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
203-
/*returns=*/ValueLayout.ADDRESS,
204+
/*returns=*/SWIFT_POINTER,
204205
ValueLayout.ADDRESS,
205206
ValueLayout.JAVA_INT,
206207
ValueLayout.ADDRESS,
@@ -230,4 +231,121 @@ public static MemorySegment getTypeByMangledNameInEnvironment(String string) {
230231
throw new AssertionError("should not reach here", ex$);
231232
}
232233
}
234+
235+
/**
236+
* The value layout for Swift's Int type, which is a signed type that follows
237+
* the size of a pointer (aka C's ptrdiff_t).
238+
*/
239+
public static ValueLayout SWIFT_INT = (ValueLayout.ADDRESS.byteSize() == 4) ?
240+
ValueLayout.JAVA_INT : ValueLayout.JAVA_LONG;
241+
242+
/**
243+
* The value layout for Swift's UInt type, which is an unsigned type that follows
244+
* the size of a pointer (aka C's size_t). Java does not have unsigned integer
245+
* types in general, so we use the layout for Swift's Int.
246+
*/
247+
public static ValueLayout SWIFT_UINT = SWIFT_INT;
248+
249+
/**
250+
* Read a Swift.Int value from memory at the given offset and translate it into a Java long.
251+
*
252+
* This function copes with the fact that a Swift.Int might be 32 or 64 bits.
253+
*/
254+
public static final long getSwiftInt(MemorySegment memorySegment, long offset) {
255+
if (SWIFT_INT == ValueLayout.JAVA_LONG) {
256+
return memorySegment.get(ValueLayout.JAVA_LONG, offset);
257+
} else {
258+
return memorySegment.get(ValueLayout.JAVA_INT, offset);
259+
}
260+
}
261+
262+
/**
263+
* Value witness table layout.
264+
*/
265+
public static final MemoryLayout valueWitnessTableLayout = MemoryLayout.structLayout(
266+
ValueLayout.ADDRESS.withName("initializeBufferWithCopyOfBuffer"),
267+
ValueLayout.ADDRESS.withName("destroy"),
268+
ValueLayout.ADDRESS.withName("initializeWithCopy"),
269+
ValueLayout.ADDRESS.withName("assignWithCopy"),
270+
ValueLayout.ADDRESS.withName("initializeWithTake"),
271+
ValueLayout.ADDRESS.withName("assignWithTake"),
272+
ValueLayout.ADDRESS.withName("getEnumTagSinglePayload"),
273+
ValueLayout.ADDRESS.withName("storeEnumTagSinglePayload"),
274+
SwiftKit.SWIFT_INT.withName("size"),
275+
SwiftKit.SWIFT_INT.withName("stride"),
276+
SwiftKit.SWIFT_UINT.withName("flags"),
277+
SwiftKit.SWIFT_UINT.withName("extraInhabitantCount")
278+
).withName("SwiftValueWitnessTable");
279+
280+
/**
281+
* Offset for the "size" field within the value witness table.
282+
*/
283+
static final long valueWitnessTable$size$offset =
284+
valueWitnessTableLayout.byteOffset(PathElement.groupElement("size"));
285+
286+
/**
287+
* Offset for the "stride" field within the value witness table.
288+
*/
289+
static final long valueWitnessTable$stride$offset =
290+
valueWitnessTableLayout.byteOffset(PathElement.groupElement("stride"));
291+
292+
/**
293+
* Offset for the "flags" field within the value witness table.
294+
*/
295+
static final long valueWitnessTable$flags$offset =
296+
valueWitnessTableLayout.byteOffset(PathElement.groupElement("flags"));
297+
298+
/**
299+
* Type metadata pointer.
300+
*/
301+
public static final StructLayout fullTypeMetadataLayout = MemoryLayout.structLayout(
302+
SWIFT_POINTER.withName("vwt")
303+
).withName("SwiftFullTypeMetadata");
304+
305+
/**
306+
* Offset for the "vwt" field within the full type metadata.
307+
*/
308+
static final long fullTypeMetadata$vwt$offset =
309+
fullTypeMetadataLayout.byteOffset(PathElement.groupElement("vwt"));
310+
311+
/**
312+
* Given the address of Swift type metadata for a type, return the addres
313+
* of the "full" type metadata that can be accessed via fullTypeMetadataLayout.
314+
*/
315+
public static MemorySegment fullTypeMetadata(MemorySegment typeMetadata) {
316+
return MemorySegment.ofAddress(typeMetadata.address() - SWIFT_POINTER.byteSize())
317+
.reinterpret(fullTypeMetadataLayout.byteSize());
318+
}
319+
320+
/**
321+
* Given the address of Swift type's metadata, return the address that
322+
* references the value witness table for the type.
323+
*/
324+
public static MemorySegment valueWitnessTable(MemorySegment typeMetadata) {
325+
return fullTypeMetadata(typeMetadata).get(SWIFT_POINTER, fullTypeMetadata$vwt$offset);
326+
}
327+
328+
/**
329+
* Determine the size of a Swift type given its type metadata.
330+
*/
331+
public static long sizeOfSwiftType(MemorySegment typeMetadata) {
332+
return getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$size$offset);
333+
}
334+
335+
/**
336+
* Determine the stride of a Swift type given its type metadata, which is
337+
* how many bytes are between successive elements of this type within an
338+
* array. It is >= the size.
339+
*/
340+
public static long strideOfSwiftType(MemorySegment typeMetadata) {
341+
return getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$stride$offset);
342+
}
343+
344+
/**
345+
* Determine the alignment of the given Swift type.
346+
*/
347+
public static long alignmentOfSwiftType(MemorySegment typeMetadata) {
348+
long flags = getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$flags$offset);
349+
return (flags & 0xFF) + 1;
350+
}
233351
}

0 commit comments

Comments
 (0)