Skip to content

Commit 8bbe21b

Browse files
authored
Merge pull request #42 from DougGregor/jextract-value-witness-memory-layout
Model Swift's type metadata and value witness table for memory layout information
2 parents ddef48a + 8cb407e commit 8bbe21b

File tree

2 files changed

+218
-1
lines changed

2 files changed

+218
-1
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,12 @@ 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));
77+
System.out.println(" Java layout = " + SwiftKit.layoutOfSwiftType(swiftType));
7178
}
7279
}

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

Lines changed: 211 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
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;
2324

25+
import static java.lang.foreign.ValueLayout.ADDRESS;
2426
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
2527

2628
public class SwiftKit {
@@ -61,6 +63,36 @@ static MemorySegment findOrThrow(String symbol) {
6163
.orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol)));
6264
}
6365

66+
// ==== ------------------------------------------------------------------------------------------------------------
67+
// free
68+
/**
69+
* Descriptor for the free C runtime function.
70+
*/
71+
public static final FunctionDescriptor free$descriptor = FunctionDescriptor.ofVoid(
72+
ValueLayout.ADDRESS
73+
);
74+
75+
/**
76+
* Address of the free C runtime function.
77+
*/
78+
public static final MemorySegment free$addr = findOrThrow("free");
79+
80+
/**
81+
* Handle for the free C runtime function.
82+
*/
83+
public static final MethodHandle free$handle = Linker.nativeLinker().downcallHandle(free$addr, free$descriptor);
84+
85+
/**
86+
* free the given pointer
87+
*/
88+
public static void cFree(MemorySegment pointer) {
89+
try {
90+
free$handle.invokeExact(pointer);
91+
} catch (Throwable ex$) {
92+
throw new AssertionError("should not reach here", ex$);
93+
}
94+
}
95+
6496
// ==== ------------------------------------------------------------------------------------------------------------
6597
// swift_retainCount
6698

@@ -200,7 +232,7 @@ public static MemorySegment getTypeByName(String string) {
200232
*/
201233
private static class swift_getTypeByMangledNameInEnvironment {
202234
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
203-
/*returns=*/ValueLayout.ADDRESS,
235+
/*returns=*/SWIFT_POINTER,
204236
ValueLayout.ADDRESS,
205237
ValueLayout.JAVA_INT,
206238
ValueLayout.ADDRESS,
@@ -230,4 +262,182 @@ public static MemorySegment getTypeByMangledNameInEnvironment(String string) {
230262
throw new AssertionError("should not reach here", ex$);
231263
}
232264
}
265+
266+
/**
267+
* The value layout for Swift's Int type, which is a signed type that follows
268+
* the size of a pointer (aka C's ptrdiff_t).
269+
*/
270+
public static ValueLayout SWIFT_INT = (ValueLayout.ADDRESS.byteSize() == 4) ?
271+
ValueLayout.JAVA_INT : ValueLayout.JAVA_LONG;
272+
273+
/**
274+
* The value layout for Swift's UInt type, which is an unsigned type that follows
275+
* the size of a pointer (aka C's size_t). Java does not have unsigned integer
276+
* types in general, so we use the layout for Swift's Int.
277+
*/
278+
public static ValueLayout SWIFT_UINT = SWIFT_INT;
279+
280+
/**
281+
* Read a Swift.Int value from memory at the given offset and translate it into a Java long.
282+
*
283+
* This function copes with the fact that a Swift.Int might be 32 or 64 bits.
284+
*/
285+
public static final long getSwiftInt(MemorySegment memorySegment, long offset) {
286+
if (SWIFT_INT == ValueLayout.JAVA_LONG) {
287+
return memorySegment.get(ValueLayout.JAVA_LONG, offset);
288+
} else {
289+
return memorySegment.get(ValueLayout.JAVA_INT, offset);
290+
}
291+
}
292+
293+
/**
294+
* Value witness table layout.
295+
*/
296+
public static final MemoryLayout valueWitnessTableLayout = MemoryLayout.structLayout(
297+
ValueLayout.ADDRESS.withName("initializeBufferWithCopyOfBuffer"),
298+
ValueLayout.ADDRESS.withName("destroy"),
299+
ValueLayout.ADDRESS.withName("initializeWithCopy"),
300+
ValueLayout.ADDRESS.withName("assignWithCopy"),
301+
ValueLayout.ADDRESS.withName("initializeWithTake"),
302+
ValueLayout.ADDRESS.withName("assignWithTake"),
303+
ValueLayout.ADDRESS.withName("getEnumTagSinglePayload"),
304+
ValueLayout.ADDRESS.withName("storeEnumTagSinglePayload"),
305+
SwiftKit.SWIFT_INT.withName("size"),
306+
SwiftKit.SWIFT_INT.withName("stride"),
307+
SwiftKit.SWIFT_UINT.withName("flags"),
308+
SwiftKit.SWIFT_UINT.withName("extraInhabitantCount")
309+
).withName("SwiftValueWitnessTable");
310+
311+
/**
312+
* Offset for the "size" field within the value witness table.
313+
*/
314+
static final long valueWitnessTable$size$offset =
315+
valueWitnessTableLayout.byteOffset(PathElement.groupElement("size"));
316+
317+
/**
318+
* Offset for the "stride" field within the value witness table.
319+
*/
320+
static final long valueWitnessTable$stride$offset =
321+
valueWitnessTableLayout.byteOffset(PathElement.groupElement("stride"));
322+
323+
/**
324+
* Offset for the "flags" field within the value witness table.
325+
*/
326+
static final long valueWitnessTable$flags$offset =
327+
valueWitnessTableLayout.byteOffset(PathElement.groupElement("flags"));
328+
329+
/**
330+
* Type metadata pointer.
331+
*/
332+
public static final StructLayout fullTypeMetadataLayout = MemoryLayout.structLayout(
333+
SWIFT_POINTER.withName("vwt")
334+
).withName("SwiftFullTypeMetadata");
335+
336+
/**
337+
* Offset for the "vwt" field within the full type metadata.
338+
*/
339+
static final long fullTypeMetadata$vwt$offset =
340+
fullTypeMetadataLayout.byteOffset(PathElement.groupElement("vwt"));
341+
342+
/**
343+
* Given the address of Swift type metadata for a type, return the addres
344+
* of the "full" type metadata that can be accessed via fullTypeMetadataLayout.
345+
*/
346+
public static MemorySegment fullTypeMetadata(MemorySegment typeMetadata) {
347+
return MemorySegment.ofAddress(typeMetadata.address() - SWIFT_POINTER.byteSize())
348+
.reinterpret(fullTypeMetadataLayout.byteSize());
349+
}
350+
351+
/**
352+
* Given the address of Swift type's metadata, return the address that
353+
* references the value witness table for the type.
354+
*/
355+
public static MemorySegment valueWitnessTable(MemorySegment typeMetadata) {
356+
return fullTypeMetadata(typeMetadata).get(SWIFT_POINTER, fullTypeMetadata$vwt$offset);
357+
}
358+
359+
/**
360+
* Determine the size of a Swift type given its type metadata.
361+
*/
362+
public static long sizeOfSwiftType(MemorySegment typeMetadata) {
363+
return getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$size$offset);
364+
}
365+
366+
/**
367+
* Determine the stride of a Swift type given its type metadata, which is
368+
* how many bytes are between successive elements of this type within an
369+
* array. It is >= the size.
370+
*/
371+
public static long strideOfSwiftType(MemorySegment typeMetadata) {
372+
return getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$stride$offset);
373+
}
374+
375+
/**
376+
* Determine the alignment of the given Swift type.
377+
*/
378+
public static long alignmentOfSwiftType(MemorySegment typeMetadata) {
379+
long flags = getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$flags$offset);
380+
return (flags & 0xFF) + 1;
381+
}
382+
383+
/**
384+
* Descriptor for the swift_getTypeName runtime function.
385+
*/
386+
public static final FunctionDescriptor swift_getTypeName$descriptor = FunctionDescriptor.of(
387+
/*returns=*/MemoryLayout.structLayout(
388+
SWIFT_POINTER.withName("utf8Chars"),
389+
SWIFT_INT.withName("length")
390+
),
391+
ValueLayout.ADDRESS,
392+
ValueLayout.JAVA_BOOLEAN
393+
);
394+
395+
/**
396+
* Address of the swift_getTypeName runtime function.
397+
*/
398+
public static final MemorySegment swift_getTypeName$addr = findOrThrow("swift_getTypeName");
399+
400+
/**
401+
* Handle for the swift_getTypeName runtime function.
402+
*/
403+
public static final MethodHandle swift_getTypeName$handle = Linker.nativeLinker().downcallHandle(swift_getTypeName$addr, swift_getTypeName$descriptor);
404+
405+
/**
406+
* Produce the name of the Swift type given its Swift type metadata.
407+
*
408+
* If 'qualified' is true, leave all of the qualification in place to
409+
* disambiguate the type, producing a more complete (but longer) type name.
410+
*/
411+
public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualified) {
412+
try {
413+
try (Arena arena = Arena.ofConfined()) {
414+
MemorySegment charsAndLength = (MemorySegment)swift_getTypeName$handle.invokeExact((SegmentAllocator)arena, typeMetadata, qualified);
415+
MemorySegment utf8Chars = charsAndLength.get(SWIFT_POINTER, 0);
416+
String typeName = utf8Chars.getString(0);
417+
cFree(utf8Chars);
418+
return typeName;
419+
}
420+
} catch (Throwable ex$) {
421+
throw new AssertionError("should not reach here", ex$);
422+
}
423+
}
424+
425+
/**
426+
* Produce a layout that describes a Swift type based on its
427+
* type metadata. The resulting layout is completely opaque to Java, but
428+
* has appropriate size/alignment to model the memory associated with a
429+
* Swift type.
430+
*
431+
* In the future, this layout could be extended to provide more detail,
432+
* such as the fields of a Swift struct.
433+
*/
434+
public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) {
435+
long size = sizeOfSwiftType(typeMetadata);
436+
long stride = strideOfSwiftType(typeMetadata);
437+
return MemoryLayout.structLayout(
438+
MemoryLayout.sequenceLayout(size, JAVA_BYTE)
439+
.withByteAlignment(alignmentOfSwiftType(typeMetadata)),
440+
MemoryLayout.paddingLayout(stride - size)
441+
).withName(nameOfSwiftType(typeMetadata, true));
442+
}
233443
}

0 commit comments

Comments
 (0)