From 6b640733d2fa19735031c1f616052836d678ca72 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 9 Oct 2024 17:14:53 +0900 Subject: [PATCH 01/18] SwiftKit: use inner-static class pattern for DESC/ADDR/HANDLE constants It's useful to keep the exact same pattern everywhere as it's so repetetive code. --- .../java/org/swift/swiftkit/SwiftKit.java | 85 ++++++++++--------- 1 file changed, 46 insertions(+), 39 deletions(-) diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index 821169b9..cb37e55f 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -80,29 +80,32 @@ static MemorySegment findOrThrow(String 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"); + static abstract class free { + /** + * Descriptor for the free C runtime function. + */ + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS + ); - /** - * Handle for the free C runtime function. - */ - public static final MethodHandle free$handle = Linker.nativeLinker().downcallHandle(free$addr, free$descriptor); + /** + * Address of the free C runtime function. + */ + public static final MemorySegment ADDR = findOrThrow("free"); + + /** + * Handle for the free C runtime function. + */ + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } /** * free the given pointer */ public static void cFree(MemorySegment pointer) { try { - free$handle.invokeExact(pointer); + free.HANDLE.invokeExact(pointer); } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } @@ -136,7 +139,7 @@ public static long retainCount(MemorySegment object) { } public static long retainCount(SwiftHeapObject object) { - return retainCount(object.$self()); + return retainCount(object.$memorySegment()); } // ==== ------------------------------------------------------------------------------------------------------------ @@ -166,7 +169,7 @@ public static void retain(MemorySegment object) { } public static long retain(SwiftHeapObject object) { - return retainCount(object.$self()); + return retainCount(object.$memorySegment()); } // ==== ------------------------------------------------------------------------------------------------------------ @@ -196,10 +199,11 @@ public static void release(MemorySegment object) { } public static long release(SwiftHeapObject object) { - return retainCount(object.$self()); + return retainCount(object.$memorySegment()); } // ==== ------------------------------------------------------------------------------------------------------------ + // getTypeByName /** * {@snippet lang = swift: @@ -395,38 +399,41 @@ public static long alignmentOfSwiftType(MemorySegment typeMetadata) { 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 - ); + private static class swift_getTypeName { - /** - * Address of the swift_getTypeName runtime function. - */ - public static final MemorySegment swift_getTypeName$addr = findOrThrow("swift_getTypeName"); + /** + * Descriptor for the swift_getTypeName runtime function. + */ + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /*returns=*/MemoryLayout.structLayout( + SWIFT_POINTER.withName("utf8Chars"), + SWIFT_INT.withName("length") + ), + ValueLayout.ADDRESS, + ValueLayout.JAVA_BOOLEAN + ); - /** - * Handle for the swift_getTypeName runtime function. - */ - public static final MethodHandle swift_getTypeName$handle = Linker.nativeLinker().downcallHandle(swift_getTypeName$addr, swift_getTypeName$descriptor); + /** + * Address of the swift_getTypeName runtime function. + */ + public static final MemorySegment ADDR = findOrThrow("swift_getTypeName"); + + /** + * Handle for the swift_getTypeName runtime function. + */ + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } /** * Produce the name of the Swift type given its Swift type metadata. *

- * If 'qualified' is true, leave all of the qualification in place to + * If 'qualified' is true, leave all 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 charsAndLength = (MemorySegment) swift_getTypeName.HANDLE.invokeExact((SegmentAllocator) arena, typeMetadata, qualified); MemorySegment utf8Chars = charsAndLength.get(SWIFT_POINTER, 0); String typeName = utf8Chars.getString(0); cFree(utf8Chars); From c802914cd90eb5c75da60523e1dbcee8e4ba83ca Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 9 Oct 2024 17:57:06 +0900 Subject: [PATCH 02/18] progress towards SwiftArena which manages swift values and heap objects --- .../org/swift/swiftkit/SwiftArenaTests.java | 55 ++++++++ .../Swift2JavaTranslator+Printing.swift | 2 +- .../java/org/swift/swiftkit/SwiftArena.java | 120 +++++++++++++++--- .../org/swift/swiftkit/SwiftHeapObject.java | 6 +- .../java/org/swift/swiftkit/SwiftKit.java | 88 ++----------- .../swift/swiftkit/SwiftMemoryResource.java | 21 +++ .../swiftkit/SwiftMemoryResourceCleanup.java | 42 ++++++ .../java/org/swift/swiftkit/SwiftValue.java | 18 +++ .../org/swift/swiftkit/SwiftValueLayout.java | 46 +++++++ .../swiftkit/SwiftValueWitnessTable.java | 78 ++++++++++++ 10 files changed, 379 insertions(+), 97 deletions(-) create mode 100644 Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTests.java create mode 100644 SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java create mode 100644 SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java create mode 100644 SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java create mode 100644 SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java create mode 100644 SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTests.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTests.java new file mode 100644 index 00000000..1ed8ccbe --- /dev/null +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTests.java @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import com.example.swift.generated.MySwiftClass; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledOnOs; +import org.junit.jupiter.api.condition.OS; + +import java.lang.foreign.Arena; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SwiftArenaTests { + + @BeforeAll + static void beforeAll() { + System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path")); + + System.loadLibrary("swiftCore"); + System.loadLibrary("ExampleSwiftLibrary"); + + System.setProperty("jextract.trace.downcalls", "true"); + } + + @Test + @DisabledOnOs(OS.LINUX) // FIXME: enable on Linux when we get new compiler with mangled names in swift interfaces + void arena_releaseClassOnClose() { + try (var arena = SwiftArena.ofConfined()) { + var obj = new MySwiftClass(1, 2); + + assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); + // TODO: test directly on SwiftHeapObject inheriting obj + + SwiftKit.retain(obj.$memorySegment()); + assertEquals(2, SwiftKit.retainCount(obj.$memorySegment())); + + SwiftKit.release(obj.$memorySegment()); + assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); + } + } +} diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 46d89dae..7a104450 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -168,7 +168,7 @@ extension Swift2JavaTranslator { } public func printClass(_ printer: inout CodePrinter, _ decl: ImportedNominalType, body: (inout CodePrinter) -> Void) { - printer.printTypeDecl("public final class \(decl.javaClassName)") { printer in + printer.printTypeDecl("public final class \(decl.javaClassName) implements SwiftHeapObject") { printer in // ==== Storage of the class // FIXME: implement the self storage for the memory address and accessors printClassSelfProperty(&printer, decl) diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java index 54c801b4..88c634e8 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java @@ -16,50 +16,134 @@ import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; +import java.util.LinkedList; +import java.util.List; import java.util.concurrent.ConcurrentSkipListSet; - +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A Swift arena manages Swift allocated memory for classes, structs, enums etc. + * When an arena is closed, it will destroy all managed swift objects in a way appropriate to their type. + *

+ * A confined arena has an associated owner thread that confines some operations to + * associated owner thread such as {@link #close()}. + */ public interface SwiftArena extends Arena { - void release(MemorySegment segment); + static SwiftArena ofConfined() { + return new ConfinedSwiftMemorySession(Thread.currentThread()); + } - void retain(MemorySegment segment); + /** + * Register a struct, enum or other non-reference counted Swift object. + * Its memory should be considered managed by this arena, and be destroyed when the arena is closed. + * + * @param object + */ + void register(SwiftHeapObject object); - long retainCount(MemorySegment segment); + void register(SwiftValue value); } -final class AutoSwiftArena implements SwiftArena { - Arena underlying; +final class ConfinedSwiftMemorySession implements SwiftArena { + + final Arena underlying; + final Thread owner; + final SwiftResourceList resources; - ConcurrentSkipListSet managedMemorySegments; + // TODO: just int and volatile updates + final int CLOSED = 0; + final int ACTIVE = 1; + final AtomicInteger state; + + public ConfinedSwiftMemorySession(Thread owner) { + this.owner = owner; + underlying = Arena.ofConfined(); + resources = new ConfinedResourceList(); + state = new AtomicInteger(ACTIVE); + } @Override public MemorySegment allocate(long byteSize, long byteAlignment) { - return null; + return underlying.allocate(byteSize, byteAlignment); } @Override public MemorySegment.Scope scope() { - return null; + return underlying.scope(); } - @Override - public void close() { - + public void checkValid() throws RuntimeException { + if (this.owner != null && this.owner != Thread.currentThread()) { + throw new WrongThreadException("ConfinedSwift arena is confined to %s but was closed from %s!".formatted(this.owner, Thread.currentThread())); + } else if (this.state.get() < ACTIVE) { + throw new RuntimeException("Arena is already closed!"); + } } @Override - public void release(MemorySegment segment) { - SwiftKit.release(segment); + public void register(SwiftHeapObject object) { + this.resources.add(new SwiftHeapObjectCleanup(object.$memorySegment())); } @Override - public void retain(MemorySegment segment) { - SwiftKit.retain(segment); + public void register(SwiftValue value) { + this.resources.add(new SwiftHeapObjectCleanup(value.$memorySegment())); } @Override - public long retainCount(MemorySegment segment) { - return SwiftKit.retainCount(segment); + public void close() { + checkValid(); + + // Cleanup all resources + if (this.state.compareAndExchange(ACTIVE, CLOSED) == ACTIVE) { + this.resources.cleanup(); + } // else, was already closed; do nothing + + + // Those the underlying arena + this.underlying.close(); + + // After this method returns normally, the scope must be not alive anymore + assert (!this.scope().isAlive()); + } + + /** + * Represents a list of resources that need a cleanup, e.g. allocated classes/structs. + */ + static abstract class SwiftResourceList implements Runnable { + // TODO: Could use intrusive linked list to avoid one indirection here + final List resourceCleanups = new LinkedList<>(); + + abstract void add(SwiftMemoryResourceCleanup cleanup); + + public abstract void cleanup(); + + public final void run() { + cleanup(); // cleaner interop + } + } + + static final class ConfinedResourceList extends SwiftResourceList { + @Override + void add(SwiftMemoryResourceCleanup cleanup) { + resourceCleanups.add(cleanup); + } + + @Override + public void cleanup() { + for (SwiftMemoryResourceCleanup cleanup : resourceCleanups) { + cleanup.run(); + } + } + } +} + +final class UnexpectedRetainCountException extends RuntimeException { + public UnexpectedRetainCountException(MemorySegment resource, long retainCount, int expectedRetainCount) { + super(("Attempting to cleanup managed memory segment %s, but it's retain count was different than [%d] (was %d)! " + + "This would result in destroying a swift object that is still retained by other code somewhere." + ).formatted(resource, expectedRetainCount, retainCount)); } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java index 5d1945dd..dcedef95 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java @@ -16,6 +16,8 @@ import java.lang.foreign.MemorySegment; -public interface SwiftHeapObject { - MemorySegment $self(); +/** + * Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}. + */ +public interface SwiftHeapObject extends SwiftMemoryResource { } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index cb37e55f..95041119 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -15,12 +15,12 @@ package org.swift.swiftkit; 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.JAVA_BYTE; +import static org.swift.swiftkit.SwiftValueWitnessTable.fullTypeMetadataLayout; public class SwiftKit { @@ -34,9 +34,6 @@ public class SwiftKit { System.loadLibrary(STDLIB_DYLIB_NAME); } - public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS - .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); - static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); private static SymbolLookup getSymbolLookup() { @@ -251,7 +248,7 @@ public static MemorySegment getTypeByName(String string) { */ private static class swift_getTypeByMangledNameInEnvironment { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /*returns=*/SWIFT_POINTER, + /*returns=*/SwiftValueLayout.SWIFT_POINTER, ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, @@ -282,88 +279,26 @@ public static MemorySegment getTypeByMangledNameInEnvironment(String string) { } } - /** - * 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) { + if (SwiftValueLayout.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()) + return MemorySegment.ofAddress(typeMetadata.address() - SwiftValueLayout.SWIFT_POINTER.byteSize()) .reinterpret(fullTypeMetadataLayout.byteSize()); } @@ -372,14 +307,15 @@ public static MemorySegment fullTypeMetadata(MemorySegment typeMetadata) { * references the value witness table for the type. */ public static MemorySegment valueWitnessTable(MemorySegment typeMetadata) { - return fullTypeMetadata(typeMetadata).get(SWIFT_POINTER, fullTypeMetadata$vwt$offset); + return fullTypeMetadata(typeMetadata) + .get(SwiftValueLayout.SWIFT_POINTER, SwiftValueWitnessTable.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); + return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$size$offset); } /** @@ -388,14 +324,14 @@ public static long sizeOfSwiftType(MemorySegment typeMetadata) { * array. It is >= the size. */ public static long strideOfSwiftType(MemorySegment typeMetadata) { - return getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$stride$offset); + return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$stride$offset); } /** * Determine the alignment of the given Swift type. */ public static long alignmentOfSwiftType(MemorySegment typeMetadata) { - long flags = getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$flags$offset); + long flags = getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$flags$offset); return (flags & 0xFF) + 1; } @@ -406,8 +342,8 @@ private static class swift_getTypeName { */ public static final FunctionDescriptor DESC = FunctionDescriptor.of( /*returns=*/MemoryLayout.structLayout( - SWIFT_POINTER.withName("utf8Chars"), - SWIFT_INT.withName("length") + SwiftValueLayout.SWIFT_POINTER.withName("utf8Chars"), + SwiftValueLayout.SWIFT_INT.withName("length") ), ValueLayout.ADDRESS, ValueLayout.JAVA_BOOLEAN @@ -434,7 +370,7 @@ public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualifi try { try (Arena arena = Arena.ofConfined()) { MemorySegment charsAndLength = (MemorySegment) swift_getTypeName.HANDLE.invokeExact((SegmentAllocator) arena, typeMetadata, qualified); - MemorySegment utf8Chars = charsAndLength.get(SWIFT_POINTER, 0); + MemorySegment utf8Chars = charsAndLength.get(SwiftValueLayout.SWIFT_POINTER, 0); String typeName = utf8Chars.getString(0); cFree(utf8Chars); return typeName; diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java new file mode 100644 index 00000000..6f27a989 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.MemorySegment; + +public interface SwiftMemoryResource { + MemorySegment $memorySegment(); +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java new file mode 100644 index 00000000..04a6a2c6 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.MemorySegment; + +/** + * A Swift memory resource cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. + */ +interface SwiftMemoryResourceCleanup extends Runnable { +} + +record SwiftHeapObjectCleanup(MemorySegment resource) implements SwiftMemoryResourceCleanup { + + @Override + public void run() { + long retainedCount = SwiftKit.retainCount(this.resource); + if (retainedCount > 1) { + throw new UnexpectedRetainCountException(this.resource, retainedCount, 1); + } + + } +} + +record SwiftValueCleanup(MemorySegment resource) implements SwiftMemoryResourceCleanup { + @Override + public void run() { + SwiftKit.retainCount(this.resource); + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java new file mode 100644 index 00000000..98739d9c --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java @@ -0,0 +1,18 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +public interface SwiftValue extends SwiftMemoryResource { +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java new file mode 100644 index 00000000..f02215be --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java @@ -0,0 +1,46 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.AddressLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.StructLayout; +import java.lang.foreign.ValueLayout; + +import static java.lang.foreign.ValueLayout.JAVA_BYTE; + +/** + * Similar to {@link java.lang.foreign.ValueLayout} however with some Swift specifics. + */ +public class SwiftValueLayout { + public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE)); + /** + * The value layout for Swift's {@code Int} type, which is a signed type that follows + * the size of a pointer (aka C's {@code ptrdiff_t}). + */ + public static ValueLayout SWIFT_INT = (ValueLayout.ADDRESS.byteSize() == 4) ? + ValueLayout.JAVA_INT : ValueLayout.JAVA_LONG; + + /** + * The value layout for Swift's {@code UInt} type, which is an unsigned type that follows + * the size of a pointer (aka C's {@code size_t}). + *

+ * Java does not have unsigned integer types, so we use the layout for Swift's {@code Int}. + */ + public static ValueLayout SWIFT_UINT = SWIFT_INT; + + +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java new file mode 100644 index 00000000..fc941779 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; +import java.lang.foreign.StructLayout; +import java.lang.foreign.ValueLayout; + +public abstract class SwiftValueWitnessTable { + /** + * Value witness table layout. + */ + public static final MemoryLayout $LAYOUT = 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"), + SwiftValueLayout.SWIFT_INT.withName("size"), + SwiftValueLayout.SWIFT_INT.withName("stride"), + SwiftValueLayout.SWIFT_UINT.withName("flags"), + SwiftValueLayout.SWIFT_UINT.withName("extraInhabitantCount") + ).withName("SwiftValueWitnessTable"); + + + /** + * Offset for the "size" field within the value witness table. + */ + static final long $size$offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("size")); + + /** + * Offset for the "stride" field within the value witness table. + */ + static final long $stride$offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("stride")); + + /** + * Offset for the "flags" field within the value witness table. + */ + static final long $flags$offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("flags")); + + /** + * Type metadata pointer. + */ + static final StructLayout fullTypeMetadataLayout = MemoryLayout.structLayout( + SwiftValueLayout.SWIFT_POINTER.withName("vwt") + ).withName("SwiftFullTypeMetadata"); + + /** + * Offset for the "vwt" field within the full type metadata. + */ + static final long fullTypeMetadata$vwt$offset = + fullTypeMetadataLayout.byteOffset(MemoryLayout.PathElement.groupElement("vwt")); + + private static class destroy { + static final long $offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("destroy")); + } + +} From ce04978fdaafa66573a4914ba3ba4c20da5e592f Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 10 Oct 2024 20:39:30 +0900 Subject: [PATCH 03/18] introduce tests for metadata size etc Previously this was just in a hello world app. --- .../com/example/swift/HelloJava2Swift.java | 9 +-- .../java/org/swift/swiftkit/SwiftKit.java | 38 ++++++++++-- .../swiftkit/SwiftRuntimeMetadataTest.java | 62 +++++++++++++++++++ 3 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index 28054047..e2b3a705 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -34,7 +34,7 @@ public static void main(String[] args) { final var dylibNames = List.of( "swiftCore", - "JavaKitExample" + "ExampleSwiftLibrary" ); @@ -61,12 +61,5 @@ static void examples() { 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/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index 95041119..610eb648 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -48,6 +48,7 @@ private static SymbolLookup getSymbolLookup() { .or(Linker.nativeLinker().defaultLookup()); } } + public SwiftKit() { } @@ -313,6 +314,9 @@ public static MemorySegment valueWitnessTable(MemorySegment typeMetadata) { /** * Determine the size of a Swift type given its type metadata. + * + * @param typeMetadata the memory segment must point to a Swift metadata, + * e.g. the result of a {@link SwiftKit.swift_getTypeByMangledNameInEnvironment} call */ public static long sizeOfSwiftType(MemorySegment typeMetadata) { return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$size$offset); @@ -321,7 +325,12 @@ public static long sizeOfSwiftType(MemorySegment typeMetadata) { /** * 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. + * array. + * + * It is >= the size. + * + * @param typeMetadata the memory segment must point to a Swift metadata, + * e.g. the result of a {@link SwiftKit.swift_getTypeByMangledNameInEnvironment} call */ public static long strideOfSwiftType(MemorySegment typeMetadata) { return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$stride$offset); @@ -329,6 +338,9 @@ public static long strideOfSwiftType(MemorySegment typeMetadata) { /** * Determine the alignment of the given Swift type. + * + * @param typeMetadata the memory segment must point to a Swift metadata, + * e.g. the result of a {@link SwiftKit.swift_getTypeByMangledNameInEnvironment} call */ public static long alignmentOfSwiftType(MemorySegment typeMetadata) { long flags = getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$flags$offset); @@ -365,6 +377,9 @@ private static class swift_getTypeName { *

* If 'qualified' is true, leave all the qualification in place to * disambiguate the type, producing a more complete (but longer) type name. + * + * @param typeMetadata the memory segment must point to a Swift metadata, + * e.g. the result of a {@link SwiftKit.swift_getTypeByMangledNameInEnvironment} call */ public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualified) { try { @@ -388,14 +403,29 @@ public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualifi *

* In the future, this layout could be extended to provide more detail, * such as the fields of a Swift struct. + * + * @param typeMetadata the memory segment must point to a Swift metadata, + * e.g. the result of a {@link SwiftKit.swift_getTypeByMangledNameInEnvironment} call */ public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { long size = sizeOfSwiftType(typeMetadata); long stride = strideOfSwiftType(typeMetadata); + long padding = stride - size; + + // constructing a zero-length paddingLayout is illegal, so we avoid doing so + MemoryLayout[] layouts = padding == 0 ? + new MemoryLayout[]{ + MemoryLayout.sequenceLayout(size, JAVA_BYTE) + .withByteAlignment(alignmentOfSwiftType(typeMetadata)) + } : + new MemoryLayout[]{ + MemoryLayout.sequenceLayout(size, JAVA_BYTE) + .withByteAlignment(alignmentOfSwiftType(typeMetadata)), + MemoryLayout.paddingLayout(stride - size) + }; + return MemoryLayout.structLayout( - MemoryLayout.sequenceLayout(size, JAVA_BYTE) - .withByteAlignment(alignmentOfSwiftType(typeMetadata)), - MemoryLayout.paddingLayout(stride - size) + layouts ).withName(nameOfSwiftType(typeMetadata, true)); } } diff --git a/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java b/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java new file mode 100644 index 00000000..b00b88a8 --- /dev/null +++ b/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.foreign.MemorySegment; + +public class SwiftRuntimeMetadataTest { + + @Test + public void integer_layout_metadata() { + MemorySegment swiftType = SwiftKit.getTypeByMangledNameInEnvironment("Si"); + + if (SwiftValueLayout.addressByteSize() == 4) { + // 32-bit platform + Assertions.assertEquals(8, SwiftKit.sizeOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftKit.strideOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftKit.alignmentOfSwiftType(swiftType)); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Int)", SwiftKit.layoutOfSwiftType(swiftType).toString()); + } else { + // 64-bit platform + Assertions.assertEquals(8, SwiftKit.sizeOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftKit.strideOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftKit.alignmentOfSwiftType(swiftType)); + Assertions.assertEquals("[8%[8:b1]](Swift.Int)", SwiftKit.layoutOfSwiftType(swiftType).toString()); + } + } + + @Test + public void optional_integer_layout_metadata() { + MemorySegment swiftType = SwiftKit.getTypeByMangledNameInEnvironment("SiSg"); + + if (SwiftValueLayout.addressByteSize() == 4) { + // 64-bit platform + Assertions.assertEquals(9, SwiftKit.sizeOfSwiftType(swiftType)); + Assertions.assertEquals(16, SwiftKit.strideOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftKit.alignmentOfSwiftType(swiftType)); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftKit.layoutOfSwiftType(swiftType).toString()); + } else { + // 64-bit platform + Assertions.assertEquals(9, SwiftKit.sizeOfSwiftType(swiftType)); + Assertions.assertEquals(16, SwiftKit.strideOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftKit.alignmentOfSwiftType(swiftType)); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftKit.layoutOfSwiftType(swiftType).toString()); + } + } + +} From f968343fedd82165c593de1e588f8761bc342752 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Thu, 10 Oct 2024 20:45:39 +0900 Subject: [PATCH 04/18] avoid explicitly loading libs; they can be loaded by the generated module --- Makefile | 3 +- .../com/example/swift/HelloJava2Swift.java | 58 ++++--- .../com/example/swift/ManualMySwiftClass.java | 8 +- .../swift/generated/MySwiftClassTest.java | 4 +- .../org/swift/swiftkit/SwiftArenaTest.java | 76 +++++++++ .../org/swift/swiftkit/SwiftArenaTests.java | 55 ------ .../ExampleSwiftLibrary/MySwiftLibrary.swift | 33 ++++ .../Swift2JavaTranslator+Printing.swift | 94 +++++----- .../JExtractSwift/Swift2JavaTranslator.swift | 3 +- Sources/SwiftKitSwift/SwiftKit.swift | 1 + .../org/swift/swiftkit/ManagedSwiftType.java | 1 - .../java/org/swift/swiftkit/SwiftAnyType.java | 74 ++++++++ .../java/org/swift/swiftkit/SwiftArena.java | 5 +- .../java/org/swift/swiftkit/SwiftKit.java | 161 ++++++------------ .../swift/swiftkit/SwiftMemoryResource.java | 12 ++ .../swiftkit/SwiftMemoryResourceCleanup.java | 9 +- .../org/swift/swiftkit/SwiftValueLayout.java | 11 +- .../swiftkit/SwiftValueWitnessTable.java | 156 +++++++++++++++-- .../swift/swiftkit/util/PlatformUtils.java | 15 ++ .../org/swift/swiftkit/util/StringUtils.java | 32 ++++ .../swiftkit/SwiftRuntimeMetadataTest.java | 32 ++-- .../JExtractSwiftTests/FuncImportTests.swift | 2 +- 22 files changed, 561 insertions(+), 284 deletions(-) create mode 100644 Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java delete mode 100644 Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTests.java create mode 100644 SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java create mode 100644 SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java create mode 100644 SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java diff --git a/Makefile b/Makefile index ab0a89b6..7bcef3c3 100644 --- a/Makefile +++ b/Makefile @@ -106,7 +106,8 @@ JEXTRACT_BUILD_DIR="$(BUILD_DIR)/jextract" define make_swiftinterface $(eval $@_MODULE = $(1)) $(eval $@_FILENAME = $(2)) - eval ${SWIFTC} \ + # eval ${SWIFTC} \ + eval /Library/Developer/Toolchains/swift-PR-76905-1539.xctoolchain/usr/bin/swiftc \ -emit-module-interface-path ${JEXTRACT_BUILD_DIR}/${$@_MODULE}/${$@_FILENAME}.swiftinterface \ -emit-module-path ${JEXTRACT_BUILD_DIR}/${$@_MODULE}/${$@_FILENAME}.swiftmodule \ -enable-library-evolution \ diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index e2b3a705..147cbd30 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -15,16 +15,13 @@ package com.example.swift; // Import swift-extract generated sources -import com.example.swift.generated.ExampleSwiftLibrary; import com.example.swift.generated.MySwiftClass; // Import javakit/swiftkit support libraries import org.swift.swiftkit.SwiftKit; - +import org.swift.swiftkit.SwiftValueWitnessTable; import java.lang.foreign.*; -import java.util.List; - public class HelloJava2Swift { @@ -32,34 +29,39 @@ public static void main(String[] args) { boolean traceDowncalls = Boolean.getBoolean("jextract.trace.downcalls"); System.out.println("Property: jextract.trace.downcalls = " + traceDowncalls); - final var dylibNames = List.of( - "swiftCore", - "ExampleSwiftLibrary" - ); - - - System.out.println("Loading libraries..."); - - for (var lib : dylibNames) { - System.out.printf("Loading: %s... ", lib); - System.loadLibrary(lib); - System.out.println("ok."); - } - examples(); } static void examples() { - ExampleSwiftLibrary.helloWorld(); - - ExampleSwiftLibrary.globalTakeInt(1337); - - MySwiftClass obj = new MySwiftClass(2222, 7777); - - SwiftKit.retain(obj.$memorySegment()); - System.out.println("[java] obj ref count = " + SwiftKit.retainCount(obj.$memorySegment())); +// ExampleSwiftLibrary.helloWorld(); +// +// ExampleSwiftLibrary.globalTakeInt(1337); +// +// MySwiftClass obj = new MySwiftClass(2222, 7777); +// +// SwiftKit.retain(obj.$memorySegment()); +// System.out.println("[java] obj ref count = " + SwiftKit.retainCount(obj.$memorySegment())); +// +// obj.voidMethod(); +// obj.takeIntMethod(42); + +// try (var arena = SwiftArena.ofConfined()) { + var instance = new MySwiftClass( +// arena, + 1111, 2222); + + System.out.println("MySwiftClass.TYPE_MANGLED_NAME = " + MySwiftClass.TYPE_MANGLED_NAME); + var swiftType = SwiftKit.getTypeByMangledNameInEnvironment(MySwiftClass.TYPE_MANGLED_NAME); + System.out.println("swiftType = " + swiftType); +// MemorySegment typeMetadata = SwiftValueWitnessTable.fullTypeMetadata(swiftType.$memorySegment()); +// System.out.println("typeMetadata = " + typeMetadata); +// +// +// System.out.printf("size of type = %d%n", SwiftValueWitnessTable.sizeOfSwiftType(swiftType.$memorySegment())); +// System.out.printf("stride of type = %d%n", SwiftValueWitnessTable.strideOfSwiftType(swiftType.$memorySegment())); +// System.out.printf("alignment of type = %d%n", SwiftValueWitnessTable.alignmentOfSwiftType(swiftType.$memorySegment())); +// System.out.printf("layout of type = %s%n", SwiftValueWitnessTable.layoutOfSwiftType(swiftType.$memorySegment()).toString()); - obj.voidMethod(); - obj.takeIntMethod(42); +// } // instance should be deallocated } } diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java index 0a40f293..9aa63f22 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java @@ -27,7 +27,7 @@ public final class ManualMySwiftClass extends Manual_MySwiftClass implements Man // 000000000003f4a8 S type metadata for JavaKitExample.MySwiftSlice // strip the _$s // drop the N - public static final String TYPE_METADATA_NAME = "14JavaKitExample12MySwiftClassC"; + public static final String TYPE_MANGLED_NAME = "14JavaKitExample12MySwiftClassC"; private final MemorySegment self; @@ -167,13 +167,13 @@ static MemorySegment __allocating_init_len_cap(long len, long cap) { if (ManualJavaKitExample.TRACE_DOWNCALLS) { ManualJavaKitExample.traceDowncall("MySwiftClass.__allocating_init(len:cap:)", len, cap); } - ManualJavaKitExample.trace("type name = " + TYPE_METADATA_NAME); + ManualJavaKitExample.trace("type name = " + TYPE_MANGLED_NAME); // FIXME: problems with _getTypeByName because of the String memory repr - // final MemorySegment type = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_METADATA_NAME); + // final MemorySegment type = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_MANGLED_NAME); // we must get a method we can call like this into SwiftKit: - MemorySegment type = ManualJavaKitExample.swiftkit_getTypeByStringByteArray(TYPE_METADATA_NAME); + MemorySegment type = ManualJavaKitExample.swiftkit_getTypeByStringByteArray(TYPE_MANGLED_NAME); ManualJavaKitExample.trace("type = " + type); var address = (MemorySegment) mh$.invokeExact(len, cap, type); diff --git a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java index 0f20c172..7495ff19 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java @@ -15,6 +15,7 @@ package com.example.swift.generated; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; @@ -48,9 +49,10 @@ void test_MySwiftClass_makeIntMethod() { } @Test + @Disabled // TODO: Need var mangled names in interfaces void test_MySwiftClass_property_len() { MySwiftClass o = new MySwiftClass(12, 42); - var got = o.makeIntMethod(); + var got = o.getLen(); assertEquals(12, got); } diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java new file mode 100644 index 00000000..4c7b5be0 --- /dev/null +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import com.example.swift.generated.MySwiftClass; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +import static org.swift.swiftkit.SwiftKit.*; +import static org.swift.swiftkit.SwiftKit.retainCount; + +public class SwiftArenaTest { + + @BeforeAll + static void beforeAll() { + System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path")); + + System.loadLibrary("swiftCore"); + System.loadLibrary("ExampleSwiftLibrary"); + + System.setProperty("jextract.trace.downcalls", "true"); + } + + @Test + void arena_releaseClassOnClose_class_ok() { + MySwiftClass unsafelyEscaped = null; + + try (var arena = SwiftArena.ofConfined()) { + var obj = new MySwiftClass(arena,1, 2); + unsafelyEscaped = obj; // also known as "don't do this" (outliving the arena) + + retain(obj.$memorySegment()); + assertEquals(2, retainCount(obj.$memorySegment())); + + release(obj.$memorySegment()); + assertEquals(1, retainCount(obj.$memorySegment())); + } + + // TODO: should we zero out the $memorySegment perhaps? + } + + @Test + void arena_releaseClassOnClose_class_leaked() { + String memorySegmentDescription = ""; + + try { + try (var arena = SwiftArena.ofConfined()) { + var obj = new MySwiftClass(arena,1, 2); + memorySegmentDescription = obj.$memorySegment().toString(); + + // Pretend that we "leaked" the class, something still holds a reference to it while we try to destroy it + retain(obj.$memorySegment()); + assertEquals(2, retainCount(obj.$memorySegment())); + } + + fail("Expected exception to be thrown while the arena is closed!"); + } catch (Exception ex) { + // The message should point out which objects "leaked": + assertTrue(ex.getMessage().contains(memorySegmentDescription)); + } + + } +} diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTests.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTests.java deleted file mode 100644 index 1ed8ccbe..00000000 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTests.java +++ /dev/null @@ -1,55 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift.org project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift.org project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -package org.swift.swiftkit; - -import com.example.swift.generated.MySwiftClass; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledOnOs; -import org.junit.jupiter.api.condition.OS; - -import java.lang.foreign.Arena; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SwiftArenaTests { - - @BeforeAll - static void beforeAll() { - System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path")); - - System.loadLibrary("swiftCore"); - System.loadLibrary("ExampleSwiftLibrary"); - - System.setProperty("jextract.trace.downcalls", "true"); - } - - @Test - @DisabledOnOs(OS.LINUX) // FIXME: enable on Linux when we get new compiler with mangled names in swift interfaces - void arena_releaseClassOnClose() { - try (var arena = SwiftArena.ofConfined()) { - var obj = new MySwiftClass(1, 2); - - assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); - // TODO: test directly on SwiftHeapObject inheriting obj - - SwiftKit.retain(obj.$memorySegment()); - assertEquals(2, SwiftKit.retainCount(obj.$memorySegment())); - - SwiftKit.release(obj.$memorySegment()); - assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); - } - } -} diff --git a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift index 4928233e..064cee49 100644 --- a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift +++ b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift @@ -48,6 +48,30 @@ public class MySwiftClass { p("\(MySwiftClass.self).cap = \(self.cap)") let addr = unsafeBitCast(self, to: UInt64.self) p("initializer done, self = 0x\(String(addr, radix: 16, uppercase: true))") + + p(" ty = \(MySwiftClass.self) @ \(ObjectIdentifier(MySwiftClass.self))") + + let s = "19ExampleSwiftLibrary02MyB5ClassC" + if let ty = _typeByName(s) { + p("any ty (\(s) = \(ty) @ \(ObjectIdentifier(ty))") + assert(ObjectIdentifier(ty) == ObjectIdentifier(MySwiftClass.self)) + } else { + p("any ty (\(s) = @ nil") + } + + let s2 = "$s19ExampleSwiftLibrary02MyB5ClassC" + if let ty = _typeByName(s2) { + p("any ty (\(s2) = \(ty) @ \(ObjectIdentifier(ty))") + } else { + p("any ty (\(s2) = @ nil") + } + p("USE THE swift_getTypeByMangledNameInEnvironment -----") + if let ty = _getTypeByMangledNameInEnvironment(s, UInt(s.count), genericEnvironment: nil, genericArguments: nil) { + p("any ty (\(s) = \(ty) @ \(ObjectIdentifier(ty))") + assert(ObjectIdentifier(ty) == ObjectIdentifier(MySwiftClass.self)) + } else { + p("any ty (\(s) = @ nil") + } } deinit { @@ -76,6 +100,15 @@ public class MySwiftClass { } } +@_silgen_name("swift_getTypeByMangledNameInEnvironment") +public func _getTypeByMangledNameInEnvironment( + _ name: UnsafePointer, + _ nameLength: UInt, + genericEnvironment: UnsafeRawPointer?, + genericArguments: UnsafeRawPointer?) + -> Any.Type? + + // ==== Internal helpers private func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) { diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 7a104450..3fd86dbe 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -100,6 +100,9 @@ extension Swift2JavaTranslator { printImports(&printer) printModuleClass(&printer) { printer in + + printStaticLibraryLoad(&printer) + // TODO: print all "static" methods for decl in importedGlobalFuncs { printFunctionDowncallMethods(&printer, decl) @@ -116,16 +119,17 @@ extension Swift2JavaTranslator { // Metadata printer.print( """ - // FIXME: this detecting is somewhat off - public static final String TYPE_METADATA_NAME = "\(decl.swiftMangledName ?? "")"; - static final MemorySegment TYPE_METADATA = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_METADATA_NAME); + public static final String TYPE_MANGLED_NAME = "\(decl.swiftMangledName ?? "")"; + static final MemorySegment TYPE_METADATA = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_MANGLED_NAME); """ ) printer.print("") + printStaticLibraryLoad(&printer) + // Initializers for initDecl in decl.initializers { - printClassInitializer(&printer, initDecl) + printClassConstructors(&printer, initDecl) } // Properties @@ -170,18 +174,13 @@ extension Swift2JavaTranslator { public func printClass(_ printer: inout CodePrinter, _ decl: ImportedNominalType, body: (inout CodePrinter) -> Void) { printer.printTypeDecl("public final class \(decl.javaClassName) implements SwiftHeapObject") { printer in // ==== Storage of the class - // FIXME: implement the self storage for the memory address and accessors printClassSelfProperty(&printer, decl) - // Constructor - printClassMemorySegmentConstructor(&printer, decl) - // Constants printClassConstants(printer: &printer) printTypeMappingDecls(&printer) // Layout of the class - // TODO: this is missing information until we can get @layout from Swift printClassMemoryLayout(&printer, decl) // Render the 'trace' functions etc @@ -247,8 +246,8 @@ extension Swift2JavaTranslator { """ static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); private static SymbolLookup getSymbolLookup() { - if (SwiftKit.isMacOS()) { - return SymbolLookup.libraryLookup(System.mapLibraryName(DYLIB_NAME), LIBRARY_ARENA) + if (PlatformUtils.isMacOS()) { + return SymbolLookup.libraryLookup(System.mapLibraryName(LIB_NAME), LIBRARY_ARENA) .or(SymbolLookup.loaderLookup()) .or(Linker.nativeLinker().defaultLookup()); } else { @@ -266,7 +265,7 @@ extension Swift2JavaTranslator { private func printClassConstants(printer: inout CodePrinter) { printer.print( """ - static final String DYLIB_NAME = "\(swiftModuleName)"; + static final String LIB_NAME = "\(swiftModuleName)"; static final Arena LIBRARY_ARENA = Arena.ofAuto(); """ ) @@ -282,17 +281,6 @@ extension Swift2JavaTranslator { ) } - private func printClassMemorySegmentConstructor(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - printer.print( - """ - /** Instances are created using static {@code init} methods rather than through the constructor directly. */ - private \(decl.javaClassName)(MemorySegment selfMemorySegment) { - this.selfMemorySegment = selfMemorySegment; - } - """ - ) - } - /// Print a property where we can store the "self" pointer of a class. private func printClassSelfProperty(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { printer.print( @@ -308,31 +296,16 @@ extension Swift2JavaTranslator { } private func printClassMemoryLayout(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { + // TODO: make use of the swift runtime to get the layout printer.print( """ private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( SWIFT_POINTER - ); - - // TODO: depends on @layout in Swift, so we can't do these yet - private static final GroupLayout $CLASS_LAYOUT = MemoryLayout.structLayout( - // SWIFT_INT.withName("heapObject"), - // ... - // SWIFT_INT.withName("cap") - ).withName("\(decl.javaType)"); // TODO: is the name right? + ).withName("\(decl.swiftMangledName ?? decl.swiftTypeName)"); - /** - * When other types refer to this type, they refer to by a pointer, - * so the layout of a class is just a pointer - */ - public static final GroupLayout $layout() { + public final GroupLayout $layout() { return $LAYOUT; } - - /** The in-memory layout of this type */ - public static final GroupLayout $instanceLayout() { - return $CLASS_LAYOUT; - } """ ) } @@ -411,7 +384,7 @@ extension Swift2JavaTranslator { ) } - public func printClassInitializer(_ printer: inout CodePrinter, _ decl: ImportedFunc) { + public func printClassConstructors(_ printer: inout CodePrinter, _ decl: ImportedFunc) { guard let parentName = decl.parentName else { fatalError("init must be inside a parent type! Was: \(decl)") } @@ -424,25 +397,38 @@ extension Swift2JavaTranslator { printMethodDowncallHandleForAddrDesc(&printer) } - printClassInitializerConstructor(&printer, decl, parentName: parentName) + printClassInitializerConstructors(&printer, decl, parentName: parentName) } - public func printClassInitializerConstructor( + public func printClassInitializerConstructors( _ printer: inout CodePrinter, _ decl: ImportedFunc, parentName: TranslatedType ) { let descClassIdentifier = renderDescClassName(decl) + printer.print( """ /** * Create an instance of {@code \(parentName.unqualifiedJavaTypeName)}. * - * {@snippet lang=swift : - * \(decl.syntax ?? "") - * } + * \(decl.renderCommentSnippet ?? " *") */ public \(parentName.unqualifiedJavaTypeName)(\(renderJavaParamDecls(decl, selfVariant: .wrapper))) { + this(/*arena=*/null, \(renderForwardParams(decl, selfVariant: .wrapper))); + } + """ + ) + + printer.print( + """ + /** + * Create an instance of {@code \(parentName.unqualifiedJavaTypeName)}. + * This instance is managed by the passed in {@link SwiftArena} and may not outlive the arena's lifetime. + * + * \(decl.renderCommentSnippet ?? " *") + */ + public \(parentName.unqualifiedJavaTypeName)(SwiftArena arena, \(renderJavaParamDecls(decl, selfVariant: .wrapper))) { var mh$ = \(descClassIdentifier).HANDLE; try { if (TRACE_DOWNCALLS) { @@ -450,6 +436,9 @@ extension Swift2JavaTranslator { } this.selfMemorySegment = (MemorySegment) mh$.invokeExact(\(renderForwardParams(decl, selfVariant: nil)), TYPE_METADATA); + if (arena != null) { + arena.register(this); + } } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } @@ -458,6 +447,17 @@ extension Swift2JavaTranslator { ) } + public func printStaticLibraryLoad(_ printer: inout CodePrinter) { + printer.print( + """ + static { + System.loadLibrary("swiftCore"); + System.loadLibrary(LIB_NAME); + } + """ + ) + } + public func printFunctionDowncallMethods(_ printer: inout CodePrinter, _ decl: ImportedFunc) { printer.printSeparator(decl.identifier) diff --git a/Sources/JExtractSwift/Swift2JavaTranslator.swift b/Sources/JExtractSwift/Swift2JavaTranslator.swift index b57c19af..a954620b 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator.swift @@ -118,8 +118,9 @@ extension Swift2JavaTranslator { /// Default set Java imports for every generated file static let defaultJavaImports: Array = [ - // Support library in Java + "org.swift.swiftkit.*", "org.swift.swiftkit.SwiftKit", + "org.swift.swiftkit.util.*", // Necessary for native calls and type mapping "java.lang.foreign.*", diff --git a/Sources/SwiftKitSwift/SwiftKit.swift b/Sources/SwiftKitSwift/SwiftKit.swift index e034f6d0..9fdcdd80 100644 --- a/Sources/SwiftKitSwift/SwiftKit.swift +++ b/Sources/SwiftKitSwift/SwiftKit.swift @@ -18,6 +18,7 @@ // No annotations are necessary on the Swift side to perform the export. // FIXME: this is a workaround until we can pass String to Swift directly +@_silgen_name("getTypeByStringByteArray") public func getTypeByStringByteArray(_ name: UnsafePointer) -> Any.Type? { let string = String(cString: name) let type = _typeByName(string) diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java b/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java index 2abaf85a..da25ae4d 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java @@ -19,7 +19,6 @@ public interface ManagedSwiftType { /** * The memory segment of `self` of the managed Swift object/value. - * @return */ public MemorySegment $memorySegment(); diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java new file mode 100644 index 00000000..c13053f4 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; + +public final class SwiftAnyType implements SwiftMemoryResource { + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + SwiftValueLayout.SWIFT_POINTER + ); + + final MemorySegment memorySegment; + + public SwiftAnyType(MemorySegment memorySegment) { + if (memorySegment.byteSize() == 0) { + throw new IllegalArgumentException("A Swift Any.Type cannot be null!"); + } + + this.memorySegment = memorySegment; + } + + public SwiftAnyType(SwiftHeapObject object) { + if (object.$layout().name().isEmpty()) { + throw new IllegalArgumentException("SwiftHeapObject must have a mangled name in order to obtain its SwiftType."); + } + + String mangledName = object.$layout().name().get(); + var tySegment = SwiftKit.getTypeByMangledNameInEnvironment(mangledName); +// if (tySegment.isEmpty()) { +// throw new IllegalArgumentException("A Swift Any.Type cannot be null!"); +// } + this.memorySegment = tySegment; //.get().memorySegment; + } + + + @Override + public MemorySegment $memorySegment() { + return memorySegment; + } + + @Override + public GroupLayout $layout() { + return $LAYOUT; + } + + @Override + public boolean immortal() { + // Don't ever try to destroy a type memory segment. + return true; + } + + @Override + public String toString() { + return "AnySwiftType{" + + "name=" + SwiftKit.nameOfSwiftType(memorySegment, true) + + "memorySegment=" + memorySegment + + '}'; + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java index 88c634e8..00d63640 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Logger; /** * A Swift arena manages Swift allocated memory for classes, structs, enums etc. @@ -37,8 +38,6 @@ static SwiftArena ofConfined() { /** * Register a struct, enum or other non-reference counted Swift object. * Its memory should be considered managed by this arena, and be destroyed when the arena is closed. - * - * @param object */ void register(SwiftHeapObject object); @@ -84,6 +83,7 @@ public void checkValid() throws RuntimeException { @Override public void register(SwiftHeapObject object) { + SwiftKit.log.info("Registered " + object.$memorySegment() + " in " + this); this.resources.add(new SwiftHeapObjectCleanup(object.$memorySegment())); } @@ -94,6 +94,7 @@ public void register(SwiftValue value) { @Override public void close() { + System.out.println("CLOSE ARENA ..."); checkValid(); // Cleanup all resources diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index 610eb648..e55c36bb 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -14,13 +14,16 @@ package org.swift.swiftkit; +import org.swift.swiftkit.util.PlatformUtils; + import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.util.Arrays; +import java.util.logging.Logger; import java.util.stream.Collectors; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static org.swift.swiftkit.SwiftValueWitnessTable.fullTypeMetadataLayout; +import static org.swift.swiftkit.util.StringUtils.stripPrefix; +import static org.swift.swiftkit.util.StringUtils.stripSuffix; public class SwiftKit { @@ -30,14 +33,17 @@ public class SwiftKit { private static final Arena LIBRARY_ARENA = Arena.ofAuto(); static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + static final Logger log = Logger.getLogger("SwiftKit"); + static { System.loadLibrary(STDLIB_DYLIB_NAME); + System.loadLibrary("SwiftKitSwift"); } static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); private static SymbolLookup getSymbolLookup() { - if (isMacOS()) { + if (PlatformUtils.isMacOS()) { // FIXME: why does this not find just by name on macOS? // SymbolLookup.libraryLookup(System.mapLibraryName(STDLIB_DYLIB_NAME), LIBRARY_ARENA) return SymbolLookup.libraryLookup(STDLIB_MACOS_DYLIB_PATH, LIBRARY_ARENA) @@ -52,18 +58,6 @@ private static SymbolLookup getSymbolLookup() { public SwiftKit() { } - public static boolean isLinux() { - return System.getProperty("os.name").toLowerCase().contains("linux"); - } - - public static boolean isMacOS() { - return System.getProperty("os.name").toLowerCase().contains("mac"); - } - - public static boolean isWindows() { - return System.getProperty("os.name").toLowerCase().contains("windows"); - } - static void traceDowncall(String name, Object... args) { String traceArgs = Arrays.stream(args) .map(Object::toString) @@ -224,7 +218,7 @@ public static MemorySegment getTypeByName(String string) { var mh$ = swift_getTypeByName.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("_swift_getTypeByName"); + traceDowncall("_typeByName"); } // TODO: A bit annoying to generate, we need an arena for the conversion... try (Arena arena = Arena.ofConfined()) { @@ -261,19 +255,53 @@ private static class swift_getTypeByMangledNameInEnvironment { public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - public static MemorySegment getTypeByMangledNameInEnvironment(String string) { + private static class getTypeByStringByteArray { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /*returns=*/SwiftValueLayout.SWIFT_POINTER, + ValueLayout.ADDRESS + ); + + public static final MemorySegment ADDR = findOrThrow("getTypeByStringByteArray"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Get a Swift {@code Any.Type} wrapped by {@link SwiftAnyType} which represents the type metadata available at runtime. + * + * @param mangledName The mangled type name (often prefixed with {@code $s}). + * @return the Swift Type wrapper object + */ + public static MemorySegment getTypeByMangledNameInEnvironment(String mangledName) { + log.info("Get Any.Type for mangled name: " + mangledName); + var mh$ = swift_getTypeByMangledNameInEnvironment.HANDLE; try { - if (string.endsWith("CN")) { - string = string.substring(0, string.length() - 2); - } + // Strip the generic "$s" prefix always + mangledName = stripPrefix(mangledName, "$s"); + // Ma is the "metadata accessor" mangled names of types we get from swiftinterface + // contain this, but we don't need it for type lookup + mangledName = stripSuffix(mangledName, "Ma"); + mangledName = stripSuffix(mangledName, "CN"); if (TRACE_DOWNCALLS) { - traceDowncall(string); + traceDowncall("swift_getTypeByMangledNameInEnvironment", mangledName); } try (Arena arena = Arena.ofConfined()) { - MemorySegment stringMemorySegment = arena.allocateFrom(string); + MemorySegment stringMemorySegment = arena.allocateFrom(mangledName); + + var ty = (MemorySegment) mh$.invokeExact(stringMemorySegment, mangledName.length(), MemorySegment.NULL, MemorySegment.NULL); + + return ty; + +// if (ty.address() == 0) { +// return Optional.empty(); +// } +// +// var wrapper = new SwiftAnyType(ty); +// log.info("Resolved type '"+mangledName+"' as: " + wrapper); +// +// return Optional.of(wrapper); - return (MemorySegment) mh$.invokeExact(stringMemorySegment, string.length(), MemorySegment.NULL, MemorySegment.NULL); } } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); @@ -294,58 +322,6 @@ public static final long getSwiftInt(MemorySegment memorySegment, long offset) { } - /** - * 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() - SwiftValueLayout.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(SwiftValueLayout.SWIFT_POINTER, SwiftValueWitnessTable.fullTypeMetadata$vwt$offset); - } - - /** - * Determine the size of a Swift type given its type metadata. - * - * @param typeMetadata the memory segment must point to a Swift metadata, - * e.g. the result of a {@link SwiftKit.swift_getTypeByMangledNameInEnvironment} call - */ - public static long sizeOfSwiftType(MemorySegment typeMetadata) { - return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$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. - * - * @param typeMetadata the memory segment must point to a Swift metadata, - * e.g. the result of a {@link SwiftKit.swift_getTypeByMangledNameInEnvironment} call - */ - public static long strideOfSwiftType(MemorySegment typeMetadata) { - return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$stride$offset); - } - - /** - * Determine the alignment of the given Swift type. - * - * @param typeMetadata the memory segment must point to a Swift metadata, - * e.g. the result of a {@link SwiftKit.swift_getTypeByMangledNameInEnvironment} call - */ - public static long alignmentOfSwiftType(MemorySegment typeMetadata) { - long flags = getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$flags$offset); - return (flags & 0xFF) + 1; - } private static class swift_getTypeName { @@ -379,7 +355,7 @@ private static class swift_getTypeName { * disambiguate the type, producing a more complete (but longer) type name. * * @param typeMetadata the memory segment must point to a Swift metadata, - * e.g. the result of a {@link SwiftKit.swift_getTypeByMangledNameInEnvironment} call + * e.g. the result of a {@link swift_getTypeByMangledNameInEnvironment} call */ public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualified) { try { @@ -395,37 +371,4 @@ public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualifi } } - /** - * 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. - * - * @param typeMetadata the memory segment must point to a Swift metadata, - * e.g. the result of a {@link SwiftKit.swift_getTypeByMangledNameInEnvironment} call - */ - public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { - long size = sizeOfSwiftType(typeMetadata); - long stride = strideOfSwiftType(typeMetadata); - long padding = stride - size; - - // constructing a zero-length paddingLayout is illegal, so we avoid doing so - MemoryLayout[] layouts = padding == 0 ? - new MemoryLayout[]{ - MemoryLayout.sequenceLayout(size, JAVA_BYTE) - .withByteAlignment(alignmentOfSwiftType(typeMetadata)) - } : - new MemoryLayout[]{ - MemoryLayout.sequenceLayout(size, JAVA_BYTE) - .withByteAlignment(alignmentOfSwiftType(typeMetadata)), - MemoryLayout.paddingLayout(stride - size) - }; - - return MemoryLayout.structLayout( - layouts - ).withName(nameOfSwiftType(typeMetadata, true)); - } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java index 6f27a989..ae00537e 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java @@ -14,8 +14,20 @@ package org.swift.swiftkit; +import java.lang.foreign.GroupLayout; import java.lang.foreign.MemorySegment; public interface SwiftMemoryResource { + + /** + * The pointer to the instance in memory. I.e. the {@code self} of the Swift object or value. + */ MemorySegment $memorySegment(); + + /** The in memory layout of an instance of this Swift type. */ + GroupLayout $layout(); + + default boolean immortal() { + return false; + } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java index 04a6a2c6..85fad1a3 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java @@ -19,24 +19,27 @@ /** * A Swift memory resource cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. */ -interface SwiftMemoryResourceCleanup extends Runnable { +sealed interface SwiftMemoryResourceCleanup extends Runnable { } record SwiftHeapObjectCleanup(MemorySegment resource) implements SwiftMemoryResourceCleanup { @Override - public void run() { + public void run() throws UnexpectedRetainCountException { long retainedCount = SwiftKit.retainCount(this.resource); if (retainedCount > 1) { throw new UnexpectedRetainCountException(this.resource, retainedCount, 1); } + SwiftKit.log.info("Destroy heap object: " + this.resource); + + SwiftValueWitnessTable.destroy(this.resource); } } record SwiftValueCleanup(MemorySegment resource) implements SwiftMemoryResourceCleanup { @Override public void run() { - SwiftKit.retainCount(this.resource); + throw new RuntimeException("not implemented yet"); } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java index f02215be..dbf8efc3 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java @@ -16,7 +16,6 @@ import java.lang.foreign.AddressLayout; import java.lang.foreign.MemoryLayout; -import java.lang.foreign.StructLayout; import java.lang.foreign.ValueLayout; import static java.lang.foreign.ValueLayout.JAVA_BYTE; @@ -25,8 +24,17 @@ * Similar to {@link java.lang.foreign.ValueLayout} however with some Swift specifics. */ public class SwiftValueLayout { + + /** + * The width of a pointer on the current platform. + */ + public static long addressByteSize() { + return ValueLayout.ADDRESS.byteSize(); + } + public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE)); + /** * The value layout for Swift's {@code Int} type, which is a signed type that follows * the size of a pointer (aka C's {@code ptrdiff_t}). @@ -34,6 +42,7 @@ public class SwiftValueLayout { public static ValueLayout SWIFT_INT = (ValueLayout.ADDRESS.byteSize() == 4) ? ValueLayout.JAVA_INT : ValueLayout.JAVA_LONG; + /** * The value layout for Swift's {@code UInt} type, which is an unsigned type that follows * the size of a pointer (aka C's {@code size_t}). diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java index fc941779..429a1449 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -14,12 +14,14 @@ package org.swift.swiftkit; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; -import java.lang.foreign.StructLayout; -import java.lang.foreign.ValueLayout; +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; + +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static org.swift.swiftkit.SwiftKit.getSwiftInt; public abstract class SwiftValueWitnessTable { + /** * Value witness table layout. */ @@ -39,12 +41,55 @@ public abstract class SwiftValueWitnessTable { ).withName("SwiftValueWitnessTable"); + /** + * Type metadata pointer. + */ + private static final StructLayout fullTypeMetadataLayout = MemoryLayout.structLayout( + SwiftValueLayout.SWIFT_POINTER.withName("vwt") + ).withName("SwiftFullTypeMetadata"); + + /** + * Offset for the "vwt" field within the full type metadata. + */ + private static final long fullTypeMetadata$vwt$offset = + fullTypeMetadataLayout.byteOffset( + MemoryLayout.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() - SwiftValueLayout.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(SwiftValueLayout.SWIFT_POINTER, SwiftValueWitnessTable.fullTypeMetadata$vwt$offset); + } + + /** * Offset for the "size" field within the value witness table. */ static final long $size$offset = $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("size")); + /** + * Determine the size of a Swift type given its type metadata. + * + * @param typeMetadata the memory segment must point to a Swift metadata + */ + public static long sizeOfSwiftType(MemorySegment typeMetadata) { + return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$size$offset); + } + + /** * Offset for the "stride" field within the value witness table. */ @@ -52,27 +97,110 @@ public abstract class SwiftValueWitnessTable { $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("stride")); /** - * Offset for the "flags" field within the value witness table. + * 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. + * + * @param typeMetadata the memory segment must point to a Swift metadata */ - static final long $flags$offset = - $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("flags")); + public static long strideOfSwiftType(MemorySegment typeMetadata) { + return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$stride$offset); + } + /** - * Type metadata pointer. + * Determine the alignment of the given Swift type. + * + * @param typeMetadata the memory segment must point to a Swift metadata */ - static final StructLayout fullTypeMetadataLayout = MemoryLayout.structLayout( - SwiftValueLayout.SWIFT_POINTER.withName("vwt") - ).withName("SwiftFullTypeMetadata"); + public static long alignmentOfSwiftType(MemorySegment typeMetadata) { + long flags = getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$flags$offset); + return (flags & 0xFF) + 1; + } /** - * Offset for the "vwt" field within the full type metadata. + * 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. + * + * @param typeMetadata the memory segment must point to a Swift metadata + */ + public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { + long size = sizeOfSwiftType(typeMetadata); + long stride = strideOfSwiftType(typeMetadata); + long padding = stride - size; + + // constructing a zero-length paddingLayout is illegal, so we avoid doing so + MemoryLayout[] layouts = padding == 0 ? + new MemoryLayout[]{ + MemoryLayout.sequenceLayout(size, JAVA_BYTE) + .withByteAlignment(alignmentOfSwiftType(typeMetadata)) + } : + new MemoryLayout[]{ + MemoryLayout.sequenceLayout(size, JAVA_BYTE) + .withByteAlignment(alignmentOfSwiftType(typeMetadata)), + MemoryLayout.paddingLayout(stride - size) + }; + + return MemoryLayout.structLayout( + layouts + ).withName(SwiftKit.nameOfSwiftType(typeMetadata, true)); + } + + + /** + * Offset for the "flags" field within the value witness table. */ - static final long fullTypeMetadata$vwt$offset = - fullTypeMetadataLayout.byteOffset(MemoryLayout.PathElement.groupElement("vwt")); + static final long $flags$offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("flags")); private static class destroy { + static final long $offset = $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("destroy")); + + static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + SwiftValueLayout.SWIFT_POINTER + ); + + /** + * Function pointer for the destroy operation + */ + static MemorySegment addr(MemorySegment base) { + SwiftKit.log.info("$offset = " + $offset); + SwiftKit.log.info("base.address() = " + base.address()); + SwiftKit.log.info("fullTypeMetadata$vwt$offset = " + fullTypeMetadata$vwt$offset); + return MemorySegment.ofAddress(base.address() + fullTypeMetadata$vwt$offset + $offset); + } + + static MethodHandle handle(MemorySegment base) { + return Linker.nativeLinker().downcallHandle(addr(base), DESC); + } + } + + /** + * Destroy the value/object. + *

+ * This includes deallocating the Swift managed memory for the object. + */ + public static void destroy(MemorySegment object) { + SwiftKit.log.info("Destroy object: " + object); + +// SwiftKit.getTypeByMangledNameInEnvironment() + + var handle = destroy.handle(object); + SwiftKit.log.info("Destroy object handle: " + handle); + try { + handle.invokeExact(object); + } catch (Throwable th) { + throw new AssertionError("Failed to destroy heap object: " + object, th); + } } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java new file mode 100644 index 00000000..3857b989 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java @@ -0,0 +1,15 @@ +package org.swift.swiftkit.util; + +public class PlatformUtils { + public static boolean isLinux() { + return System.getProperty("os.name").toLowerCase().contains("linux"); + } + + public static boolean isMacOS() { + return System.getProperty("os.name").toLowerCase().contains("mac"); + } + + public static boolean isWindows() { + return System.getProperty("os.name").toLowerCase().contains("windows"); + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java new file mode 100644 index 00000000..08e84f49 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java @@ -0,0 +1,32 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit.util; + +public class StringUtils { + public static String stripPrefix(String mangledName, String prefix) { + if (mangledName.startsWith(prefix)) { + return mangledName.substring(prefix.length()); + } + return mangledName; + } + + public static String stripSuffix(String mangledName, String suffix) { + if (mangledName.endsWith(suffix)) { + return mangledName.substring(0, mangledName.length() - suffix.length()); + } + return mangledName; + } + +} diff --git a/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java b/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java index b00b88a8..4f002b12 100644 --- a/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java +++ b/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java @@ -27,16 +27,16 @@ public void integer_layout_metadata() { if (SwiftValueLayout.addressByteSize() == 4) { // 32-bit platform - Assertions.assertEquals(8, SwiftKit.sizeOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftKit.strideOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftKit.alignmentOfSwiftType(swiftType)); - Assertions.assertEquals("[8%[9:b1]x7](Swift.Int)", SwiftKit.layoutOfSwiftType(swiftType).toString()); + Assertions.assertEquals(8, SwiftValueWitnessTable.sizeOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftValueWitnessTable.strideOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType)); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Int)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType).toString()); } else { // 64-bit platform - Assertions.assertEquals(8, SwiftKit.sizeOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftKit.strideOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftKit.alignmentOfSwiftType(swiftType)); - Assertions.assertEquals("[8%[8:b1]](Swift.Int)", SwiftKit.layoutOfSwiftType(swiftType).toString()); + Assertions.assertEquals(8, SwiftValueWitnessTable.sizeOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftValueWitnessTable.strideOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType)); + Assertions.assertEquals("[8%[8:b1]](Swift.Int)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType).toString()); } } @@ -46,16 +46,16 @@ public void optional_integer_layout_metadata() { if (SwiftValueLayout.addressByteSize() == 4) { // 64-bit platform - Assertions.assertEquals(9, SwiftKit.sizeOfSwiftType(swiftType)); - Assertions.assertEquals(16, SwiftKit.strideOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftKit.alignmentOfSwiftType(swiftType)); - Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftKit.layoutOfSwiftType(swiftType).toString()); + Assertions.assertEquals(9, SwiftValueWitnessTable.sizeOfSwiftType(swiftType)); + Assertions.assertEquals(16, SwiftValueWitnessTable.strideOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType)); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType).toString()); } else { // 64-bit platform - Assertions.assertEquals(9, SwiftKit.sizeOfSwiftType(swiftType)); - Assertions.assertEquals(16, SwiftKit.strideOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftKit.alignmentOfSwiftType(swiftType)); - Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftKit.layoutOfSwiftType(swiftType).toString()); + Assertions.assertEquals(9, SwiftValueWitnessTable.sizeOfSwiftType(swiftType)); + Assertions.assertEquals(16, SwiftValueWitnessTable.strideOfSwiftType(swiftType)); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType)); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType).toString()); } } diff --git a/Tests/JExtractSwiftTests/FuncImportTests.swift b/Tests/JExtractSwiftTests/FuncImportTests.swift index 80f04f7d..48164ffb 100644 --- a/Tests/JExtractSwiftTests/FuncImportTests.swift +++ b/Tests/JExtractSwiftTests/FuncImportTests.swift @@ -403,7 +403,7 @@ final class MethodImportTests { }! let output = CodePrinter.toString { printer in - st.printClassInitializerConstructor(&printer, initDecl, parentName: initDecl.parentName!) + st.printClassInitializerConstructors(&printer, initDecl, parentName: initDecl.parentName!) } assertOutput( From 27895d3c155a384d409751bc169d4049ecac6b93 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 11 Oct 2024 14:34:00 +0900 Subject: [PATCH 05/18] add a default toString to imported heap objects --- .../Swift2JavaTranslator+Printing.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 3fd86dbe..5227dd3a 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -821,4 +821,16 @@ extension Swift2JavaTranslator { printer.print(");"); } + public func printHeapObjectToStringMethod(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { + printer.print( + """ + @Override + public String toString() { + return getClass().getSimpleName() + "(" + + SwiftKit.nameOfSwiftType($swiftType().$memorySegment(), true) + + ")@" + $memorySegment(); + } + """) + } + } From 18e7f94dfae4f98cc0efffb6782838c473873df3 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 11 Oct 2024 21:02:46 +0900 Subject: [PATCH 06/18] work in progress on calling destroy of a class --- .gitignore | 3 ++ .../com/example/swift/HelloJava2Swift.java | 30 ++++++------ .../Swift2JavaTranslator+Printing.swift | 26 ++++++---- .../java/org/swift/swiftkit/SwiftAnyType.java | 14 +++--- .../java/org/swift/swiftkit/SwiftArena.java | 22 +++++---- .../org/swift/swiftkit/SwiftHeapObject.java | 1 + .../java/org/swift/swiftkit/SwiftKit.java | 46 +++++++++-------- .../swiftkit/SwiftMemoryResourceCleanup.java | 7 +-- .../swiftkit/SwiftValueWitnessTable.java | 49 +++++++++++++------ .../org/swift/swiftkit/util/StringUtils.java | 4 ++ .../swiftkit/SwiftRuntimeMetadataTest.java | 38 +++++++------- 11 files changed, 137 insertions(+), 103 deletions(-) diff --git a/.gitignore b/.gitignore index f56abda5..618b65f8 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ BuildLogic/out/ **/build/ lib/ +# Ignore JVM crash logs +**/*.log + # Ignore package resolved Package.resolved diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index 147cbd30..4c1ad5dc 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -18,6 +18,7 @@ import com.example.swift.generated.MySwiftClass; // Import javakit/swiftkit support libraries +import org.swift.swiftkit.SwiftArena; import org.swift.swiftkit.SwiftKit; import org.swift.swiftkit.SwiftValueWitnessTable; @@ -45,23 +46,22 @@ static void examples() { // obj.voidMethod(); // obj.takeIntMethod(42); -// try (var arena = SwiftArena.ofConfined()) { - var instance = new MySwiftClass( -// arena, - 1111, 2222); + MySwiftClass unsafelyEscaped = null; + try (var arena = SwiftArena.ofConfined()) { + var instance = new MySwiftClass(arena, 1111, 2222); + unsafelyEscaped = instance; + + var num = instance.makeIntMethod(); System.out.println("MySwiftClass.TYPE_MANGLED_NAME = " + MySwiftClass.TYPE_MANGLED_NAME); - var swiftType = SwiftKit.getTypeByMangledNameInEnvironment(MySwiftClass.TYPE_MANGLED_NAME); - System.out.println("swiftType = " + swiftType); -// MemorySegment typeMetadata = SwiftValueWitnessTable.fullTypeMetadata(swiftType.$memorySegment()); -// System.out.println("typeMetadata = " + typeMetadata); -// -// -// System.out.printf("size of type = %d%n", SwiftValueWitnessTable.sizeOfSwiftType(swiftType.$memorySegment())); -// System.out.printf("stride of type = %d%n", SwiftValueWitnessTable.strideOfSwiftType(swiftType.$memorySegment())); -// System.out.printf("alignment of type = %d%n", SwiftValueWitnessTable.alignmentOfSwiftType(swiftType.$memorySegment())); -// System.out.printf("layout of type = %s%n", SwiftValueWitnessTable.layoutOfSwiftType(swiftType.$memorySegment()).toString()); + MemorySegment typeMetadata = SwiftValueWitnessTable.fullTypeMetadata(MySwiftClass.TYPE_METADATA.$memorySegment()); + System.out.println("typeMetadata = " + typeMetadata); + + SwiftKit.release(instance); + } // instance should be deallocated + + var num = unsafelyEscaped.makeIntMethod(); -// } // instance should be deallocated + System.out.println("DONE."); } } diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 5227dd3a..1d20114a 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -113,20 +113,24 @@ extension Swift2JavaTranslator { public func printImportedClass(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { printHeader(&printer) printPackage(&printer) - printImports(&printer) // TODO print any imports the file may need, it we talk to other Swift modules + printImports(&printer) printClass(&printer, decl) { printer in - // Metadata + // Ensure we have loaded the library where the Swift type was declared before we attempt to resolve types in Swift + printStaticLibraryLoad(&printer) + + // Prepare type metadata, we're going to need these when invoking e.g. initializers so cache them in a static printer.print( """ public static final String TYPE_MANGLED_NAME = "\(decl.swiftMangledName ?? "")"; - static final MemorySegment TYPE_METADATA = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_MANGLED_NAME); + public static final SwiftAnyType TYPE_METADATA = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_MANGLED_NAME).get(); + public final SwiftAnyType $swiftType() { + return TYPE_METADATA; + } """ ) printer.print("") - printStaticLibraryLoad(&printer) - // Initializers for initDecl in decl.initializers { printClassConstructors(&printer, initDecl) @@ -141,6 +145,9 @@ extension Swift2JavaTranslator { for funcDecl in decl.methods { printFunctionDowncallMethods(&printer, funcDecl) } + + // Helper methods and default implementations + printHeapObjectToStringMethod(&printer, decl) } } @@ -345,7 +352,6 @@ extension Swift2JavaTranslator { public static final ValueLayout.OfLong SWIFT_UINT = SWIFT_INT64; public static final AddressLayout SWIFT_SELF = SWIFT_POINTER; - public static final AddressLayout SWIFT_TYPE_METADATA_PTR = SWIFT_POINTER; """ ) } @@ -412,7 +418,7 @@ extension Swift2JavaTranslator { /** * Create an instance of {@code \(parentName.unqualifiedJavaTypeName)}. * - * \(decl.renderCommentSnippet ?? " *") + \(decl.renderCommentSnippet ?? " *") */ public \(parentName.unqualifiedJavaTypeName)(\(renderJavaParamDecls(decl, selfVariant: .wrapper))) { this(/*arena=*/null, \(renderForwardParams(decl, selfVariant: .wrapper))); @@ -426,7 +432,7 @@ extension Swift2JavaTranslator { * Create an instance of {@code \(parentName.unqualifiedJavaTypeName)}. * This instance is managed by the passed in {@link SwiftArena} and may not outlive the arena's lifetime. * - * \(decl.renderCommentSnippet ?? " *") + \(decl.renderCommentSnippet ?? " *") */ public \(parentName.unqualifiedJavaTypeName)(SwiftArena arena, \(renderJavaParamDecls(decl, selfVariant: .wrapper))) { var mh$ = \(descClassIdentifier).HANDLE; @@ -435,7 +441,7 @@ extension Swift2JavaTranslator { traceDowncall(\(renderForwardParams(decl, selfVariant: nil))); } - this.selfMemorySegment = (MemorySegment) mh$.invokeExact(\(renderForwardParams(decl, selfVariant: nil)), TYPE_METADATA); + this.selfMemorySegment = (MemorySegment) mh$.invokeExact(\(renderForwardParams(decl, selfVariant: nil)), TYPE_METADATA.$memorySegment()); if (arena != null) { arena.register(this); } @@ -824,7 +830,7 @@ extension Swift2JavaTranslator { public func printHeapObjectToStringMethod(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { printer.print( """ - @Override + @Override public String toString() { return getClass().getSimpleName() + "(" + SwiftKit.nameOfSwiftType($swiftType().$memorySegment(), true) + diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java index c13053f4..3612cc75 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java @@ -24,7 +24,7 @@ public final class SwiftAnyType implements SwiftMemoryResource { SwiftValueLayout.SWIFT_POINTER ); - final MemorySegment memorySegment; + private final MemorySegment memorySegment; public SwiftAnyType(MemorySegment memorySegment) { if (memorySegment.byteSize() == 0) { @@ -40,11 +40,11 @@ public SwiftAnyType(SwiftHeapObject object) { } String mangledName = object.$layout().name().get(); - var tySegment = SwiftKit.getTypeByMangledNameInEnvironment(mangledName); -// if (tySegment.isEmpty()) { -// throw new IllegalArgumentException("A Swift Any.Type cannot be null!"); -// } - this.memorySegment = tySegment; //.get().memorySegment; + var type = SwiftKit.getTypeByMangledNameInEnvironment(mangledName); + if (type.isEmpty()) { + throw new IllegalArgumentException("A Swift Any.Type cannot be null!"); + } + this.memorySegment = type.get().memorySegment; } @@ -68,7 +68,7 @@ public boolean immortal() { public String toString() { return "AnySwiftType{" + "name=" + SwiftKit.nameOfSwiftType(memorySegment, true) + - "memorySegment=" + memorySegment + + ", memorySegment=" + memorySegment + '}'; } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java index 00d63640..599e6e52 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java @@ -47,7 +47,7 @@ static SwiftArena ofConfined() { final class ConfinedSwiftMemorySession implements SwiftArena { - final Arena underlying; +// final Arena underlying; final Thread owner; final SwiftResourceList resources; @@ -58,19 +58,21 @@ final class ConfinedSwiftMemorySession implements SwiftArena { public ConfinedSwiftMemorySession(Thread owner) { this.owner = owner; - underlying = Arena.ofConfined(); +// underlying = Arena.ofConfined(); resources = new ConfinedResourceList(); state = new AtomicInteger(ACTIVE); } @Override public MemorySegment allocate(long byteSize, long byteAlignment) { - return underlying.allocate(byteSize, byteAlignment); +// return underlying.allocate(byteSize, byteAlignment); + return null; } @Override public MemorySegment.Scope scope() { - return underlying.scope(); + return null; +// return underlying.scope(); } public void checkValid() throws RuntimeException { @@ -83,13 +85,13 @@ public void checkValid() throws RuntimeException { @Override public void register(SwiftHeapObject object) { - SwiftKit.log.info("Registered " + object.$memorySegment() + " in " + this); - this.resources.add(new SwiftHeapObjectCleanup(object.$memorySegment())); + System.out.println("Registered " + object.$memorySegment() + " in " + this); + this.resources.add(new SwiftHeapObjectCleanup(object)); } @Override public void register(SwiftValue value) { - this.resources.add(new SwiftHeapObjectCleanup(value.$memorySegment())); + this.resources.add(new SwiftValueCleanup(value.$memorySegment())); } @Override @@ -104,10 +106,10 @@ public void close() { // Those the underlying arena - this.underlying.close(); +// this.underlying.close(); // After this method returns normally, the scope must be not alive anymore - assert (!this.scope().isAlive()); +// assert (!this.scope().isAlive()); } /** @@ -142,7 +144,7 @@ public void cleanup() { } final class UnexpectedRetainCountException extends RuntimeException { - public UnexpectedRetainCountException(MemorySegment resource, long retainCount, int expectedRetainCount) { + public UnexpectedRetainCountException(Object resource, long retainCount, int expectedRetainCount) { super(("Attempting to cleanup managed memory segment %s, but it's retain count was different than [%d] (was %d)! " + "This would result in destroying a swift object that is still retained by other code somewhere." ).formatted(resource, expectedRetainCount, retainCount)); diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java index dcedef95..8da8e3fc 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java @@ -20,4 +20,5 @@ * Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}. */ public interface SwiftHeapObject extends SwiftMemoryResource { + SwiftAnyType $swiftType(); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index e55c36bb..ba9ff040 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -19,6 +19,7 @@ import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.util.Arrays; +import java.util.Optional; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -33,8 +34,6 @@ public class SwiftKit { private static final Arena LIBRARY_ARENA = Arena.ofAuto(); static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); - static final Logger log = Logger.getLogger("SwiftKit"); - static { System.loadLibrary(STDLIB_DYLIB_NAME); System.loadLibrary("SwiftKitSwift"); @@ -272,8 +271,8 @@ private static class getTypeByStringByteArray { * @param mangledName The mangled type name (often prefixed with {@code $s}). * @return the Swift Type wrapper object */ - public static MemorySegment getTypeByMangledNameInEnvironment(String mangledName) { - log.info("Get Any.Type for mangled name: " + mangledName); + public static Optional getTypeByMangledNameInEnvironment(String mangledName) { + System.out.println("Get Any.Type for mangled name: " + mangledName); var mh$ = swift_getTypeByMangledNameInEnvironment.HANDLE; try { @@ -289,19 +288,14 @@ public static MemorySegment getTypeByMangledNameInEnvironment(String mangledName try (Arena arena = Arena.ofConfined()) { MemorySegment stringMemorySegment = arena.allocateFrom(mangledName); - var ty = (MemorySegment) mh$.invokeExact(stringMemorySegment, mangledName.length(), MemorySegment.NULL, MemorySegment.NULL); + var memorySegment = (MemorySegment) mh$.invokeExact(stringMemorySegment, mangledName.length(), MemorySegment.NULL, MemorySegment.NULL); - return ty; - -// if (ty.address() == 0) { -// return Optional.empty(); -// } -// -// var wrapper = new SwiftAnyType(ty); -// log.info("Resolved type '"+mangledName+"' as: " + wrapper); -// -// return Optional.of(wrapper); + if (memorySegment.address() == 0) { + return Optional.empty(); + } + var wrapper = new SwiftAnyType(memorySegment); + return Optional.of(wrapper); } } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); @@ -313,7 +307,7 @@ public static MemorySegment getTypeByMangledNameInEnvironment(String mangledName *

* 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) { + public static long getSwiftInt(MemorySegment memorySegment, long offset) { if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { return memorySegment.get(ValueLayout.JAVA_LONG, offset); } else { @@ -358,14 +352,18 @@ private static class swift_getTypeName { * e.g. the result of a {@link swift_getTypeByMangledNameInEnvironment} call */ 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(SwiftValueLayout.SWIFT_POINTER, 0); - String typeName = utf8Chars.getString(0); - cFree(utf8Chars); - return typeName; - } + MethodHandle mh = swift_getTypeName.HANDLE; + + try (Arena arena = Arena.ofConfined()) { + MemorySegment charsAndLength = (MemorySegment) mh.invokeExact((SegmentAllocator) arena, typeMetadata, qualified); + MemorySegment utf8Chars = charsAndLength.get(SwiftValueLayout.SWIFT_POINTER, 0); + String typeName = utf8Chars.getString(0); + + // FIXME: this free is not always correct: + // java(80175,0x17008f000) malloc: *** error for object 0x600000362610: pointer being freed was not allocated + // cFree(utf8Chars); + + return typeName; } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java index 85fad1a3..f7305a96 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java @@ -22,7 +22,7 @@ sealed interface SwiftMemoryResourceCleanup extends Runnable { } -record SwiftHeapObjectCleanup(MemorySegment resource) implements SwiftMemoryResourceCleanup { +record SwiftHeapObjectCleanup(SwiftHeapObject resource) implements SwiftMemoryResourceCleanup { @Override public void run() throws UnexpectedRetainCountException { @@ -31,9 +31,10 @@ public void run() throws UnexpectedRetainCountException { throw new UnexpectedRetainCountException(this.resource, retainedCount, 1); } - SwiftKit.log.info("Destroy heap object: " + this.resource); + System.out.println("Cleanup heap object: " + this.resource); + var ty = this.resource.$swiftType(); - SwiftValueWitnessTable.destroy(this.resource); + SwiftValueWitnessTable.destroy(ty, this.resource.$memorySegment()); } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java index 429a1449..1258d69e 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -16,6 +16,7 @@ import java.lang.foreign.*; import java.lang.invoke.MethodHandle; +import org.swift.swiftkit.util.StringUtils; import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static org.swift.swiftkit.SwiftKit.getSwiftInt; @@ -160,6 +161,20 @@ public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { static final long $flags$offset = $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("flags")); + /** + * {@snippet lang=C : + * ///void(*destroy)(T *object, witness_t *self); + * /// + * /// Given a valid object of this type, destroy it, leaving it as an + * /// invalid object. This is useful when generically destroying + * /// an object which has been allocated in-line, such as an array, + * /// struct,or tuple element. + * FUNCTION_VALUE_WITNESS(destroy, + * Destroy, + * VOID_TYPE, + * (MUTABLE_VALUE_TYPE, TYPE_TYPE)) + * } + */ private static class destroy { static final long $offset = @@ -167,39 +182,45 @@ private static class destroy { static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( SwiftValueLayout.SWIFT_POINTER +// , // the object +// SwiftValueLayout.SWIFT_POINTER // the type ); /** * Function pointer for the destroy operation */ - static MemorySegment addr(MemorySegment base) { - SwiftKit.log.info("$offset = " + $offset); - SwiftKit.log.info("base.address() = " + base.address()); - SwiftKit.log.info("fullTypeMetadata$vwt$offset = " + fullTypeMetadata$vwt$offset); - return MemorySegment.ofAddress(base.address() + fullTypeMetadata$vwt$offset + $offset); + static MemorySegment addr(SwiftAnyType ty, MemorySegment memory) { + // Get the value witness table of the type + final var vwt = SwiftValueWitnessTable.valueWitnessTable(ty.$memorySegment()); + + // Get the address of the destroy function stored at the offset of the witness table + long funcAddress = getSwiftInt(vwt, destroy.$offset); + return MemorySegment.ofAddress(funcAddress); } - static MethodHandle handle(MemorySegment base) { - return Linker.nativeLinker().downcallHandle(addr(base), DESC); + static MethodHandle handle(SwiftAnyType ty, MemorySegment memory) { + return Linker.nativeLinker().downcallHandle(addr(ty, memory), DESC); } } + /** * Destroy the value/object. *

* This includes deallocating the Swift managed memory for the object. */ - public static void destroy(MemorySegment object) { - SwiftKit.log.info("Destroy object: " + object); + public static void destroy(SwiftAnyType type, MemorySegment object) { + System.out.println("Destroy object: " + object); + System.out.println("Destroy object type: " + type); -// SwiftKit.getTypeByMangledNameInEnvironment() + var handle = destroy.handle(type, object); + System.out.println("Destroy object handle: " + handle); - var handle = destroy.handle(object); - SwiftKit.log.info("Destroy object handle: " + handle); try { - handle.invokeExact(object); + handle.invokeExact(object); +// handle.invokeExact(object, type.$memorySegment()); } catch (Throwable th) { - throw new AssertionError("Failed to destroy heap object: " + object, th); + throw new AssertionError("Failed to destroy '"+type+"' at " + object, th); } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java index 08e84f49..6753ea73 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java @@ -29,4 +29,8 @@ public static String stripSuffix(String mangledName, String suffix) { return mangledName; } + public static String hexString(long number) { + return String.format("0x%02x", number); + } + } diff --git a/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java b/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java index 4f002b12..5354cc4f 100644 --- a/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java +++ b/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java @@ -17,45 +17,43 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import java.lang.foreign.MemorySegment; - public class SwiftRuntimeMetadataTest { @Test public void integer_layout_metadata() { - MemorySegment swiftType = SwiftKit.getTypeByMangledNameInEnvironment("Si"); + SwiftAnyType swiftType = SwiftKit.getTypeByMangledNameInEnvironment("Si").get(); if (SwiftValueLayout.addressByteSize() == 4) { // 32-bit platform - Assertions.assertEquals(8, SwiftValueWitnessTable.sizeOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftValueWitnessTable.strideOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType)); - Assertions.assertEquals("[8%[9:b1]x7](Swift.Int)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType).toString()); + Assertions.assertEquals(8, SwiftValueWitnessTable.sizeOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.strideOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Int)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType.$memorySegment()).toString()); } else { // 64-bit platform - Assertions.assertEquals(8, SwiftValueWitnessTable.sizeOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftValueWitnessTable.strideOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType)); - Assertions.assertEquals("[8%[8:b1]](Swift.Int)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType).toString()); + Assertions.assertEquals(8, SwiftValueWitnessTable.sizeOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.strideOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals("[8%[8:b1]](Swift.Int)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType.$memorySegment()).toString()); } } @Test public void optional_integer_layout_metadata() { - MemorySegment swiftType = SwiftKit.getTypeByMangledNameInEnvironment("SiSg"); + SwiftAnyType swiftType = SwiftKit.getTypeByMangledNameInEnvironment("SiSg").get(); if (SwiftValueLayout.addressByteSize() == 4) { // 64-bit platform - Assertions.assertEquals(9, SwiftValueWitnessTable.sizeOfSwiftType(swiftType)); - Assertions.assertEquals(16, SwiftValueWitnessTable.strideOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType)); - Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType).toString()); + Assertions.assertEquals(9, SwiftValueWitnessTable.sizeOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(16, SwiftValueWitnessTable.strideOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType.$memorySegment()).toString()); } else { // 64-bit platform - Assertions.assertEquals(9, SwiftValueWitnessTable.sizeOfSwiftType(swiftType)); - Assertions.assertEquals(16, SwiftValueWitnessTable.strideOfSwiftType(swiftType)); - Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType)); - Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType).toString()); + Assertions.assertEquals(9, SwiftValueWitnessTable.sizeOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(16, SwiftValueWitnessTable.strideOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType.$memorySegment()).toString()); } } From 18ad313d1632f574b59da42371ac7c4207ae34e1 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 11 Oct 2024 23:52:30 +0900 Subject: [PATCH 07/18] pass the self pointer through another pointer indirectly to destroy --- .../com/example/swift/HelloJava2Swift.java | 8 ++-- .../org/swift/swiftkit/MySwiftClassTest.java | 15 ++++--- .../java/org/swift/swiftkit/SwiftKit.java | 8 ++-- .../org/swift/swiftkit/SwiftValueLayout.java | 7 +-- .../swiftkit/SwiftValueWitnessTable.java | 44 ++++++++++++++----- 5 files changed, 53 insertions(+), 29 deletions(-) diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index 4c1ad5dc..703fa68c 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -30,6 +30,8 @@ public static void main(String[] args) { boolean traceDowncalls = Boolean.getBoolean("jextract.trace.downcalls"); System.out.println("Property: jextract.trace.downcalls = " + traceDowncalls); + System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path")); + examples(); } @@ -51,13 +53,13 @@ static void examples() { var instance = new MySwiftClass(arena, 1111, 2222); unsafelyEscaped = instance; - var num = instance.makeIntMethod(); +// var num = instance.makeIntMethod(); + + System.out.println("SwiftKit.retainCount(instance) = " + SwiftKit.retainCount(instance)); System.out.println("MySwiftClass.TYPE_MANGLED_NAME = " + MySwiftClass.TYPE_MANGLED_NAME); MemorySegment typeMetadata = SwiftValueWitnessTable.fullTypeMetadata(MySwiftClass.TYPE_METADATA.$memorySegment()); System.out.println("typeMetadata = " + typeMetadata); - - SwiftKit.release(instance); } // instance should be deallocated var num = unsafelyEscaped.makeIntMethod(); diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java index b04da744..06fcb680 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java @@ -37,15 +37,16 @@ static void beforeAll() { @Test void call_retain_retainCount_release() { - var obj = new MySwiftClass(1, 2); + var arena = SwiftArena.ofConfined(); + var obj = new MySwiftClass(arena, 1, 2); - assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); - // TODO: test directly on SwiftHeapObject inheriting obj + assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); + // TODO: test directly on SwiftHeapObject inheriting obj - SwiftKit.retain(obj.$memorySegment()); - assertEquals(2, SwiftKit.retainCount(obj.$memorySegment())); + SwiftKit.retain(obj.$memorySegment()); + assertEquals(2, SwiftKit.retainCount(obj.$memorySegment())); - SwiftKit.release(obj.$memorySegment()); - assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); + SwiftKit.release(obj.$memorySegment()); + assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index ba9ff040..bb39c2a8 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -20,7 +20,6 @@ import java.lang.invoke.MethodHandle; import java.util.Arrays; import java.util.Optional; -import java.util.logging.Logger; import java.util.stream.Collectors; import static org.swift.swiftkit.util.StringUtils.stripPrefix; @@ -35,6 +34,7 @@ public class SwiftKit { static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); static { + System.load(STDLIB_MACOS_DYLIB_PATH); System.loadLibrary(STDLIB_DYLIB_NAME); System.loadLibrary("SwiftKitSwift"); } @@ -159,8 +159,8 @@ public static void retain(MemorySegment object) { } } - public static long retain(SwiftHeapObject object) { - return retainCount(object.$memorySegment()); + public static void retain(SwiftHeapObject object) { + retain(object.$memorySegment()); } // ==== ------------------------------------------------------------------------------------------------------------ @@ -181,7 +181,7 @@ public static void release(MemorySegment object) { var mh$ = swift_release.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("swift_release_retain", object); + traceDowncall("swift_release", object); } mh$.invokeExact(object); } catch (Throwable ex$) { diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java index dbf8efc3..bf1a82c5 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java @@ -19,6 +19,7 @@ import java.lang.foreign.ValueLayout; import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static java.lang.foreign.ValueLayout.JAVA_LONG; /** * Similar to {@link java.lang.foreign.ValueLayout} however with some Swift specifics. @@ -32,9 +33,6 @@ public static long addressByteSize() { return ValueLayout.ADDRESS.byteSize(); } - public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS - .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE)); - /** * The value layout for Swift's {@code Int} type, which is a signed type that follows * the size of a pointer (aka C's {@code ptrdiff_t}). @@ -52,4 +50,7 @@ public static long addressByteSize() { public static ValueLayout SWIFT_UINT = SWIFT_INT; + public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE)); + } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java index 1258d69e..721af54b 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -16,10 +16,11 @@ import java.lang.foreign.*; import java.lang.invoke.MethodHandle; -import org.swift.swiftkit.util.StringUtils; +import static java.lang.foreign.ValueLayout.ADDRESS; import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static org.swift.swiftkit.SwiftKit.getSwiftInt; +import static org.swift.swiftkit.util.StringUtils.hexString; public abstract class SwiftValueWitnessTable { @@ -162,7 +163,7 @@ public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("flags")); /** - * {@snippet lang=C : + * {@snippet lang = C: * ///void(*destroy)(T *object, witness_t *self); * /// * /// Given a valid object of this type, destroy it, leaving it as an @@ -173,7 +174,7 @@ public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { * Destroy, * VOID_TYPE, * (MUTABLE_VALUE_TYPE, TYPE_TYPE)) - * } + *} */ private static class destroy { @@ -181,7 +182,8 @@ private static class destroy { $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("destroy")); static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - SwiftValueLayout.SWIFT_POINTER + ValueLayout.ADDRESS + // SwiftValueLayout.SWIFT_POINTER // should be a "pointer to the self pointer" // , // the object // SwiftValueLayout.SWIFT_POINTER // the type ); @@ -189,7 +191,7 @@ private static class destroy { /** * Function pointer for the destroy operation */ - static MemorySegment addr(SwiftAnyType ty, MemorySegment memory) { + static MemorySegment addr(SwiftAnyType ty) { // Get the value witness table of the type final var vwt = SwiftValueWitnessTable.valueWitnessTable(ty.$memorySegment()); @@ -198,8 +200,8 @@ static MemorySegment addr(SwiftAnyType ty, MemorySegment memory) { return MemorySegment.ofAddress(funcAddress); } - static MethodHandle handle(SwiftAnyType ty, MemorySegment memory) { - return Linker.nativeLinker().downcallHandle(addr(ty, memory), DESC); + static MethodHandle handle(SwiftAnyType ty) { + return Linker.nativeLinker().downcallHandle(addr(ty), DESC); } } @@ -213,14 +215,32 @@ public static void destroy(SwiftAnyType type, MemorySegment object) { System.out.println("Destroy object: " + object); System.out.println("Destroy object type: " + type); - var handle = destroy.handle(type, object); + var handle = destroy.handle(type); System.out.println("Destroy object handle: " + handle); - try { - handle.invokeExact(object); -// handle.invokeExact(object, type.$memorySegment()); + try (var arena = Arena.ofConfined()) { + // we need to make a pointer to the self pointer when calling witness table functions: + MemorySegment indirect = arena.allocate(SwiftValueLayout.SWIFT_POINTER); + + // Write the address of as the value of the newly created pointer. + // We need to type-safely set the pointer value which may be 64 or 32-bit. + if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { + indirect.set(ValueLayout.JAVA_LONG, /*offset=*/0, object.address()); + } else { + indirect.set(ValueLayout.JAVA_INT, /*offset=*/0, (int) object.address()); + } + + System.out.println("indirect = " + indirect); + if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { + System.out.println("indirect.get(ValueLayout.JAVA_LONG, 0) = " + hexString(indirect.get(ValueLayout.JAVA_LONG, 0))); + } else { + System.out.println("indirect.get(ValueLayout.JAVA_INT, 0) = " + hexString(indirect.get(ValueLayout.JAVA_INT, 0))); + } + +// handle.invokeExact(object); // NOTE: This does "nothing" + handle.invokeExact(indirect); } catch (Throwable th) { - throw new AssertionError("Failed to destroy '"+type+"' at " + object, th); + throw new AssertionError("Failed to destroy '" + type + "' at " + object, th); } } From 97c2eac2ddcb5ae3d0d25e71b447a69f98d5af8e Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 16 Oct 2024 12:44:16 +0900 Subject: [PATCH 08/18] fix passing wtable to destroy --- Makefile | 3 +- .../org/swift/swiftkit/SwiftArenaTest.java | 4 +- SwiftKit/build.gradle | 3 ++ .../swift/swiftkit/MemorySegmentUtils.java | 23 +++++++++++ .../java/org/swift/swiftkit/SwiftAnyType.java | 10 +---- .../org/swift/swiftkit/SwiftHeapObject.java | 4 +- ...MemoryResource.java => SwiftInstance.java} | 17 +++++++-- .../swiftkit/SwiftMemoryResourceCleanup.java | 20 ++++++---- .../java/org/swift/swiftkit/SwiftValue.java | 3 +- .../swiftkit/SwiftValueWitnessTable.java | 38 +++++-------------- 10 files changed, 68 insertions(+), 57 deletions(-) create mode 100644 SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java rename SwiftKit/src/main/java/org/swift/swiftkit/{SwiftMemoryResource.java => SwiftInstance.java} (64%) diff --git a/Makefile b/Makefile index 7bcef3c3..ab0a89b6 100644 --- a/Makefile +++ b/Makefile @@ -106,8 +106,7 @@ JEXTRACT_BUILD_DIR="$(BUILD_DIR)/jextract" define make_swiftinterface $(eval $@_MODULE = $(1)) $(eval $@_FILENAME = $(2)) - # eval ${SWIFTC} \ - eval /Library/Developer/Toolchains/swift-PR-76905-1539.xctoolchain/usr/bin/swiftc \ + eval ${SWIFTC} \ -emit-module-interface-path ${JEXTRACT_BUILD_DIR}/${$@_MODULE}/${$@_FILENAME}.swiftinterface \ -emit-module-path ${JEXTRACT_BUILD_DIR}/${$@_MODULE}/${$@_FILENAME}.swiftmodule \ -enable-library-evolution \ diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java index 4c7b5be0..73085bf6 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java @@ -35,7 +35,7 @@ static void beforeAll() { } @Test - void arena_releaseClassOnClose_class_ok() { + public void arena_releaseClassOnClose_class_ok() { MySwiftClass unsafelyEscaped = null; try (var arena = SwiftArena.ofConfined()) { @@ -53,7 +53,7 @@ void arena_releaseClassOnClose_class_ok() { } @Test - void arena_releaseClassOnClose_class_leaked() { + public void arena_releaseClassOnClose_class_leaked() { String memorySegmentDescription = ""; try { diff --git a/SwiftKit/build.gradle b/SwiftKit/build.gradle index 5a6d15c0..3479e6c8 100644 --- a/SwiftKit/build.gradle +++ b/SwiftKit/build.gradle @@ -36,4 +36,7 @@ dependencies { tasks.test { useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java new file mode 100644 index 00000000..4f863e74 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java @@ -0,0 +1,23 @@ +package org.swift.swiftkit; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; + +public class MemorySegmentUtils { + /** + * Set the value of `target` to the {@link MemorySegment#address()} of the `memorySegment`, + * adjusting for the fact that Swift pointer may be 32 or 64 bit, depending on runtime. + * + * @param target the target to set a value on + * @param memorySegment the origin of the address value to write into `target` + */ + static void setSwiftPointerAddress(MemorySegment target, MemorySegment memorySegment) { + // Write the address of as the value of the newly created pointer. + // We need to type-safely set the pointer value which may be 64 or 32-bit. + if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { + target.set(ValueLayout.JAVA_LONG, /*offset=*/0, memorySegment.address()); + } else { + target.set(ValueLayout.JAVA_INT, /*offset=*/0, (int) memorySegment.address()); + } + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java index 3612cc75..6dcd1f6b 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java @@ -18,7 +18,7 @@ import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -public final class SwiftAnyType implements SwiftMemoryResource { +public final class SwiftAnyType { private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( SwiftValueLayout.SWIFT_POINTER @@ -48,22 +48,14 @@ public SwiftAnyType(SwiftHeapObject object) { } - @Override public MemorySegment $memorySegment() { return memorySegment; } - @Override public GroupLayout $layout() { return $LAYOUT; } - @Override - public boolean immortal() { - // Don't ever try to destroy a type memory segment. - return true; - } - @Override public String toString() { return "AnySwiftType{" + diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java index 8da8e3fc..a7add138 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java @@ -14,11 +14,9 @@ package org.swift.swiftkit; -import java.lang.foreign.MemorySegment; - /** * Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}. */ -public interface SwiftHeapObject extends SwiftMemoryResource { +public interface SwiftHeapObject extends SwiftInstance { SwiftAnyType $swiftType(); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java similarity index 64% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java rename to SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java index ae00537e..08cce158 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java @@ -17,17 +17,26 @@ import java.lang.foreign.GroupLayout; import java.lang.foreign.MemorySegment; -public interface SwiftMemoryResource { +public interface SwiftInstance { /** * The pointer to the instance in memory. I.e. the {@code self} of the Swift object or value. */ MemorySegment $memorySegment(); - /** The in memory layout of an instance of this Swift type. */ + /** + * The in memory layout of an instance of this Swift type. + */ GroupLayout $layout(); - default boolean immortal() { - return false; + SwiftAnyType $swiftType(); + + /** + * Returns `true` if this swift instance is a reference type, i.e. a `class` or (`distributed`) `actor`. + * + * @return `true` if this instance is a reference type, `false` otherwise. + */ + default boolean isReferenceType() { + return this instanceof SwiftHeapObject; } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java index f7305a96..e837ef4d 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java @@ -15,26 +15,32 @@ package org.swift.swiftkit; import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; + +import static org.swift.swiftkit.MemorySegmentUtils.setSwiftPointerAddress; /** - * A Swift memory resource cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. + * A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. */ sealed interface SwiftMemoryResourceCleanup extends Runnable { } -record SwiftHeapObjectCleanup(SwiftHeapObject resource) implements SwiftMemoryResourceCleanup { +record SwiftHeapObjectCleanup(SwiftHeapObject instance) implements SwiftMemoryResourceCleanup { @Override public void run() throws UnexpectedRetainCountException { - long retainedCount = SwiftKit.retainCount(this.resource); + // Verify we're only destroying an object that's indeed not retained by anyone else: + long retainedCount = SwiftKit.retainCount(this.instance); if (retainedCount > 1) { - throw new UnexpectedRetainCountException(this.resource, retainedCount, 1); + throw new UnexpectedRetainCountException(this.instance, retainedCount, 1); } - System.out.println("Cleanup heap object: " + this.resource); - var ty = this.resource.$swiftType(); + // Destroy (and deinit) the object: + var ty = this.instance.$swiftType(); + SwiftValueWitnessTable.destroy(ty, this.instance.$memorySegment()); - SwiftValueWitnessTable.destroy(ty, this.resource.$memorySegment()); + // Invalidate the Java wrapper class, in order to prevent effectively use-after-free issues. + // FIXME: some trouble with setting the pointer to null, need to figure out an appropriate way to do this } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java index 98739d9c..9387fa85 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java @@ -14,5 +14,6 @@ package org.swift.swiftkit; -public interface SwiftValue extends SwiftMemoryResource { +public interface SwiftValue extends SwiftInstance { + SwiftAnyType $swiftType(); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java index 721af54b..10938b71 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -17,10 +17,8 @@ import java.lang.foreign.*; import java.lang.invoke.MethodHandle; -import static java.lang.foreign.ValueLayout.ADDRESS; import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static org.swift.swiftkit.SwiftKit.getSwiftInt; -import static org.swift.swiftkit.util.StringUtils.hexString; public abstract class SwiftValueWitnessTable { @@ -182,10 +180,8 @@ private static class destroy { $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("destroy")); static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - ValueLayout.ADDRESS - // SwiftValueLayout.SWIFT_POINTER // should be a "pointer to the self pointer" -// , // the object -// SwiftValueLayout.SWIFT_POINTER // the type + ValueLayout.ADDRESS, // witness table functions expect a pointer to self pointer + ValueLayout.ADDRESS // pointer to the witness table ); /** @@ -212,33 +208,17 @@ static MethodHandle handle(SwiftAnyType ty) { * This includes deallocating the Swift managed memory for the object. */ public static void destroy(SwiftAnyType type, MemorySegment object) { - System.out.println("Destroy object: " + object); - System.out.println("Destroy object type: " + type); + var fullTypeMetadata = fullTypeMetadata(type.$memorySegment()); + var wtable = valueWitnessTable(fullTypeMetadata); - var handle = destroy.handle(type); - System.out.println("Destroy object handle: " + handle); + var mh = destroy.handle(type); try (var arena = Arena.ofConfined()) { // we need to make a pointer to the self pointer when calling witness table functions: - MemorySegment indirect = arena.allocate(SwiftValueLayout.SWIFT_POINTER); - - // Write the address of as the value of the newly created pointer. - // We need to type-safely set the pointer value which may be 64 or 32-bit. - if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { - indirect.set(ValueLayout.JAVA_LONG, /*offset=*/0, object.address()); - } else { - indirect.set(ValueLayout.JAVA_INT, /*offset=*/0, (int) object.address()); - } - - System.out.println("indirect = " + indirect); - if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { - System.out.println("indirect.get(ValueLayout.JAVA_LONG, 0) = " + hexString(indirect.get(ValueLayout.JAVA_LONG, 0))); - } else { - System.out.println("indirect.get(ValueLayout.JAVA_INT, 0) = " + hexString(indirect.get(ValueLayout.JAVA_INT, 0))); - } - -// handle.invokeExact(object); // NOTE: This does "nothing" - handle.invokeExact(indirect); + MemorySegment indirect = arena.allocate(SwiftValueLayout.SWIFT_POINTER); // TODO: remove this and just have classes have this always anyway + MemorySegmentUtils.setSwiftPointerAddress(indirect, object); + + mh.invokeExact(indirect, wtable); } catch (Throwable th) { throw new AssertionError("Failed to destroy '" + type + "' at " + object, th); } From 6ca633c821dd2782c8449edad0801372a95af4db Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 16 Oct 2024 12:46:13 +0900 Subject: [PATCH 09/18] cleanup --- .../ExampleSwiftLibrary/MySwiftLibrary.swift | 24 ------------------- .../swift/swiftkit/MemorySegmentUtils.java | 14 +++++++++++ .../swiftkit/SwiftMemoryResourceCleanup.java | 3 --- .../swift/swiftkit/util/PlatformUtils.java | 14 +++++++++++ 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift index 064cee49..5ad8e6fb 100644 --- a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift +++ b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift @@ -48,30 +48,6 @@ public class MySwiftClass { p("\(MySwiftClass.self).cap = \(self.cap)") let addr = unsafeBitCast(self, to: UInt64.self) p("initializer done, self = 0x\(String(addr, radix: 16, uppercase: true))") - - p(" ty = \(MySwiftClass.self) @ \(ObjectIdentifier(MySwiftClass.self))") - - let s = "19ExampleSwiftLibrary02MyB5ClassC" - if let ty = _typeByName(s) { - p("any ty (\(s) = \(ty) @ \(ObjectIdentifier(ty))") - assert(ObjectIdentifier(ty) == ObjectIdentifier(MySwiftClass.self)) - } else { - p("any ty (\(s) = @ nil") - } - - let s2 = "$s19ExampleSwiftLibrary02MyB5ClassC" - if let ty = _typeByName(s2) { - p("any ty (\(s2) = \(ty) @ \(ObjectIdentifier(ty))") - } else { - p("any ty (\(s2) = @ nil") - } - p("USE THE swift_getTypeByMangledNameInEnvironment -----") - if let ty = _getTypeByMangledNameInEnvironment(s, UInt(s.count), genericEnvironment: nil, genericArguments: nil) { - p("any ty (\(s) = \(ty) @ \(ObjectIdentifier(ty))") - assert(ObjectIdentifier(ty) == ObjectIdentifier(MySwiftClass.self)) - } else { - p("any ty (\(s) = @ nil") - } } deinit { diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java index 4f863e74..660a5500 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java @@ -1,3 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + package org.swift.swiftkit; import java.lang.foreign.MemorySegment; diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java index e837ef4d..212b0922 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java @@ -15,9 +15,6 @@ package org.swift.swiftkit; import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; - -import static org.swift.swiftkit.MemorySegmentUtils.setSwiftPointerAddress; /** * A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java index 3857b989..03c6c9f5 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java @@ -1,3 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + package org.swift.swiftkit.util; public class PlatformUtils { From 0d15c84be971c54ad3de59ec67e8876a564209e7 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 16 Oct 2024 13:15:28 +0900 Subject: [PATCH 10/18] remove un-necessary loadLibrary with dylib --- SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index bb39c2a8..1b3f0eff 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -34,7 +34,6 @@ public class SwiftKit { static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); static { - System.load(STDLIB_MACOS_DYLIB_PATH); System.loadLibrary(STDLIB_DYLIB_NAME); System.loadLibrary("SwiftKitSwift"); } @@ -43,8 +42,7 @@ public class SwiftKit { private static SymbolLookup getSymbolLookup() { if (PlatformUtils.isMacOS()) { - // FIXME: why does this not find just by name on macOS? - // SymbolLookup.libraryLookup(System.mapLibraryName(STDLIB_DYLIB_NAME), LIBRARY_ARENA) + // On Apple platforms we need to lookup using the complete path return SymbolLookup.libraryLookup(STDLIB_MACOS_DYLIB_PATH, LIBRARY_ARENA) .or(SymbolLookup.loaderLookup()) .or(Linker.nativeLinker().defaultLookup()); From 079a6d29c5fc42fcc37456b03b25fb6c32be0276 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 16 Oct 2024 13:17:26 +0900 Subject: [PATCH 11/18] Fix constructor test --- .../JExtractSwiftTests/FuncImportTests.swift | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/Tests/JExtractSwiftTests/FuncImportTests.swift b/Tests/JExtractSwiftTests/FuncImportTests.swift index 48164ffb..3b2ef4f4 100644 --- a/Tests/JExtractSwiftTests/FuncImportTests.swift +++ b/Tests/JExtractSwiftTests/FuncImportTests.swift @@ -418,15 +418,28 @@ final class MethodImportTests { * } */ public MySwiftClass(long len, long cap) { + this(/*arena=*/null, len, cap); + } + /** + * Create an instance of {@code MySwiftClass}. + * This instance is managed by the passed in {@link SwiftArena} and may not outlive the arena's lifetime. + * + * {@snippet lang=swift : + * public init(len: Swift.Int, cap: Swift.Int) + * } + */ + public MySwiftClass(SwiftArena arena, long len, long cap) { var mh$ = init_len_cap.HANDLE; try { - if (TRACE_DOWNCALLS) { - traceDowncall(len, cap); - } - - this.selfMemorySegment = (MemorySegment) mh$.invokeExact(len, cap, TYPE_METADATA); + if (TRACE_DOWNCALLS) { + traceDowncall(len, cap); + } + this.selfMemorySegment = (MemorySegment) mh$.invokeExact(len, cap, TYPE_METADATA.$memorySegment()); + if (arena != null) { + arena.register(this); + } } catch (Throwable ex$) { - throw new AssertionError(\"should not reach here\", ex$); + throw new AssertionError("should not reach here", ex$); } } """ From 8fddb7f486bf6db7fa1f0a7cee31723aa385925b Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 18 Oct 2024 14:49:32 +0900 Subject: [PATCH 12/18] remove not used LockedState --- Sources/JavaKitVM/LockedState.swift | 160 ---------------------------- 1 file changed, 160 deletions(-) delete mode 100644 Sources/JavaKitVM/LockedState.swift diff --git a/Sources/JavaKitVM/LockedState.swift b/Sources/JavaKitVM/LockedState.swift deleted file mode 100644 index e095668c..00000000 --- a/Sources/JavaKitVM/LockedState.swift +++ /dev/null @@ -1,160 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift.org project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift.org project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -#if canImport(os) -internal import os -#if FOUNDATION_FRAMEWORK && canImport(C.os.lock) -internal import C.os.lock -#endif -#elseif canImport(Bionic) -import Bionic -#elseif canImport(Glibc) -import Glibc -#elseif canImport(Musl) -import Musl -#elseif canImport(WinSDK) -import WinSDK -#endif - -package struct LockedState { - - // Internal implementation for a cheap lock to aid sharing code across platforms - private struct _Lock { -#if canImport(os) - typealias Primitive = os_unfair_lock -#elseif canImport(Bionic) || canImport(Glibc) || canImport(Musl) - typealias Primitive = pthread_mutex_t -#elseif canImport(WinSDK) - typealias Primitive = SRWLOCK -#elseif os(WASI) - // WASI is single-threaded, so we don't need a lock. - typealias Primitive = Void -#endif - - typealias PlatformLock = UnsafeMutablePointer - var _platformLock: PlatformLock - - fileprivate static func initialize(_ platformLock: PlatformLock) { -#if canImport(os) - platformLock.initialize(to: os_unfair_lock()) -#elseif canImport(Bionic) || canImport(Glibc) - pthread_mutex_init(platformLock, nil) -#elseif canImport(WinSDK) - InitializeSRWLock(platformLock) -#elseif os(WASI) - // no-op -#endif - } - - fileprivate static func deinitialize(_ platformLock: PlatformLock) { -#if canImport(Bionic) || canImport(Glibc) - pthread_mutex_destroy(platformLock) -#endif - platformLock.deinitialize(count: 1) - } - - static fileprivate func lock(_ platformLock: PlatformLock) { -#if canImport(os) - os_unfair_lock_lock(platformLock) -#elseif canImport(Bionic) || canImport(Glibc) - pthread_mutex_lock(platformLock) -#elseif canImport(WinSDK) - AcquireSRWLockExclusive(platformLock) -#elseif os(WASI) - // no-op -#endif - } - - static fileprivate func unlock(_ platformLock: PlatformLock) { -#if canImport(os) - os_unfair_lock_unlock(platformLock) -#elseif canImport(Bionic) || canImport(Glibc) - pthread_mutex_unlock(platformLock) -#elseif canImport(WinSDK) - ReleaseSRWLockExclusive(platformLock) -#elseif os(WASI) - // no-op -#endif - } - } - - private class _Buffer: ManagedBuffer { - deinit { - withUnsafeMutablePointerToElements { - _Lock.deinitialize($0) - } - } - } - - private let _buffer: ManagedBuffer - - package init(initialState: State) { - _buffer = _Buffer.create(minimumCapacity: 1, makingHeaderWith: { buf in - buf.withUnsafeMutablePointerToElements { - _Lock.initialize($0) - } - return initialState - }) - } - - package func withLock(_ body: @Sendable (inout State) throws -> T) rethrows -> T { - try withLockUnchecked(body) - } - - package func withLockUnchecked(_ body: (inout State) throws -> T) rethrows -> T { - try _buffer.withUnsafeMutablePointers { state, lock in - _Lock.lock(lock) - defer { _Lock.unlock(lock) } - return try body(&state.pointee) - } - } - - // Ensures the managed state outlives the locked scope. - package func withLockExtendingLifetimeOfState(_ body: @Sendable (inout State) throws -> T) rethrows -> T { - try _buffer.withUnsafeMutablePointers { state, lock in - _Lock.lock(lock) - return try withExtendedLifetime(state.pointee) { - defer { _Lock.unlock(lock) } - return try body(&state.pointee) - } - } - } -} - -extension LockedState where State == Void { - package init() { - self.init(initialState: ()) - } - - package func withLock(_ body: @Sendable () throws -> R) rethrows -> R { - return try withLock { _ in - try body() - } - } - - package func lock() { - _buffer.withUnsafeMutablePointerToElements { lock in - _Lock.lock(lock) - } - } - - package func unlock() { - _buffer.withUnsafeMutablePointerToElements { lock in - _Lock.unlock(lock) - } - } -} - -extension LockedState: @unchecked Sendable where State: Sendable {} - From 92d11ee7c122f9596849d9fc13c4a5a3cae48ceb Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 18 Oct 2024 18:51:02 +0900 Subject: [PATCH 13/18] debugging CI, unsure why crash happens, probably paths --- .github/workflows/pull_request.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 251ba1b3..08ebc7b6 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -57,7 +57,10 @@ jobs: ${{ runner.os }}-swiftpm- # run the actual build - name: Gradle build - run: ./gradlew build --info --no-daemon + run: | + ./gradlew build -x test --no-daemon # skip tests + find . + ./gradlew build --info --no-daemon test-swift: name: Swift tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }}) From 5669314cfb950bdeaacc36706f0fe951d96be760 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 18 Oct 2024 21:48:32 +0900 Subject: [PATCH 14/18] Discard changes to Sources/JavaKitVM/LockedState.swift --- Sources/JavaKitVM/LockedState.swift | 160 ++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 Sources/JavaKitVM/LockedState.swift diff --git a/Sources/JavaKitVM/LockedState.swift b/Sources/JavaKitVM/LockedState.swift new file mode 100644 index 00000000..e095668c --- /dev/null +++ b/Sources/JavaKitVM/LockedState.swift @@ -0,0 +1,160 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +#if canImport(os) +internal import os +#if FOUNDATION_FRAMEWORK && canImport(C.os.lock) +internal import C.os.lock +#endif +#elseif canImport(Bionic) +import Bionic +#elseif canImport(Glibc) +import Glibc +#elseif canImport(Musl) +import Musl +#elseif canImport(WinSDK) +import WinSDK +#endif + +package struct LockedState { + + // Internal implementation for a cheap lock to aid sharing code across platforms + private struct _Lock { +#if canImport(os) + typealias Primitive = os_unfair_lock +#elseif canImport(Bionic) || canImport(Glibc) || canImport(Musl) + typealias Primitive = pthread_mutex_t +#elseif canImport(WinSDK) + typealias Primitive = SRWLOCK +#elseif os(WASI) + // WASI is single-threaded, so we don't need a lock. + typealias Primitive = Void +#endif + + typealias PlatformLock = UnsafeMutablePointer + var _platformLock: PlatformLock + + fileprivate static func initialize(_ platformLock: PlatformLock) { +#if canImport(os) + platformLock.initialize(to: os_unfair_lock()) +#elseif canImport(Bionic) || canImport(Glibc) + pthread_mutex_init(platformLock, nil) +#elseif canImport(WinSDK) + InitializeSRWLock(platformLock) +#elseif os(WASI) + // no-op +#endif + } + + fileprivate static func deinitialize(_ platformLock: PlatformLock) { +#if canImport(Bionic) || canImport(Glibc) + pthread_mutex_destroy(platformLock) +#endif + platformLock.deinitialize(count: 1) + } + + static fileprivate func lock(_ platformLock: PlatformLock) { +#if canImport(os) + os_unfair_lock_lock(platformLock) +#elseif canImport(Bionic) || canImport(Glibc) + pthread_mutex_lock(platformLock) +#elseif canImport(WinSDK) + AcquireSRWLockExclusive(platformLock) +#elseif os(WASI) + // no-op +#endif + } + + static fileprivate func unlock(_ platformLock: PlatformLock) { +#if canImport(os) + os_unfair_lock_unlock(platformLock) +#elseif canImport(Bionic) || canImport(Glibc) + pthread_mutex_unlock(platformLock) +#elseif canImport(WinSDK) + ReleaseSRWLockExclusive(platformLock) +#elseif os(WASI) + // no-op +#endif + } + } + + private class _Buffer: ManagedBuffer { + deinit { + withUnsafeMutablePointerToElements { + _Lock.deinitialize($0) + } + } + } + + private let _buffer: ManagedBuffer + + package init(initialState: State) { + _buffer = _Buffer.create(minimumCapacity: 1, makingHeaderWith: { buf in + buf.withUnsafeMutablePointerToElements { + _Lock.initialize($0) + } + return initialState + }) + } + + package func withLock(_ body: @Sendable (inout State) throws -> T) rethrows -> T { + try withLockUnchecked(body) + } + + package func withLockUnchecked(_ body: (inout State) throws -> T) rethrows -> T { + try _buffer.withUnsafeMutablePointers { state, lock in + _Lock.lock(lock) + defer { _Lock.unlock(lock) } + return try body(&state.pointee) + } + } + + // Ensures the managed state outlives the locked scope. + package func withLockExtendingLifetimeOfState(_ body: @Sendable (inout State) throws -> T) rethrows -> T { + try _buffer.withUnsafeMutablePointers { state, lock in + _Lock.lock(lock) + return try withExtendedLifetime(state.pointee) { + defer { _Lock.unlock(lock) } + return try body(&state.pointee) + } + } + } +} + +extension LockedState where State == Void { + package init() { + self.init(initialState: ()) + } + + package func withLock(_ body: @Sendable () throws -> R) rethrows -> R { + return try withLock { _ in + try body() + } + } + + package func lock() { + _buffer.withUnsafeMutablePointerToElements { lock in + _Lock.lock(lock) + } + } + + package func unlock() { + _buffer.withUnsafeMutablePointerToElements { lock in + _Lock.unlock(lock) + } + } +} + +extension LockedState: @unchecked Sendable where State: Sendable {} + From db8fc4fd74c7a677ef50e939a3687a09af6e7be5 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 22 Oct 2024 13:42:07 +0900 Subject: [PATCH 15/18] debugging linux x86 issue --- .github/workflows/pull_request.yml | 114 +-- .../swift/generated/ExampleSwiftLibrary.java | 293 +++++++ .../example/swift/generated/MySwiftClass.java | 765 ++++++++++++++++++ .../swiftkit/generated/SwiftKitSwift.java | 119 +++ .../org/swift/swiftkit/MySwiftClassTest.java | 22 +- .../org/swift/swiftkit/SwiftArenaTest.java | 11 +- .../swift/swiftkit/MemorySegmentUtils.java | 2 + .../java/org/swift/swiftkit/SwiftArena.java | 51 +- ...Cleanup.java => SwiftInstanceCleanup.java} | 9 +- .../java/org/swift/swiftkit/SwiftKit.java | 10 +- .../swiftkit/SwiftValueWitnessTable.java | 2 + 11 files changed, 1282 insertions(+), 116 deletions(-) create mode 100644 Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/ExampleSwiftLibrary.java create mode 100644 Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/MySwiftClass.java create mode 100644 Samples/SwiftKitSampleApp/src/generated/java/org/swift/swiftkit/generated/SwiftKitSwift.java rename SwiftKit/src/main/java/org/swift/swiftkit/{SwiftMemoryResourceCleanup.java => SwiftInstanceCleanup.java} (88%) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 08ebc7b6..0ea4e88f 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -5,13 +5,13 @@ on: types: [opened, reopened, synchronize] jobs: - soundness: - uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main - with: - api_breakage_check_enabled: false - # FIXME: Something is off with the format task and it gets "stuck", need to investigate - format_check_enabled: false - license_header_check_project_name: Swift.org +# soundness: +# uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main +# with: +# api_breakage_check_enabled: false +# # FIXME: Something is off with the format task and it gets "stuck", need to investigate +# format_check_enabled: false +# license_header_check_project_name: Swift.org test-java: name: Java tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }}) @@ -62,53 +62,53 @@ jobs: find . ./gradlew build --info --no-daemon - test-swift: - name: Swift tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }}) - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - swift_version: ['nightly-main'] - os_version: ['jammy'] - jdk_vendor: ['Corretto'] - container: - image: ${{ (contains(matrix.swift_version, 'nightly') && 'swiftlang/swift') || 'swift' }}:${{ matrix.swift_version }}-${{ matrix.os_version }} - env: - JAVA_HOME: "/usr/lib/jvm/default-jdk" - steps: - - uses: actions/checkout@v4 - - name: Install System Dependencies - run: apt-get -qq update && apt-get -qq install -y make curl wget - - name: Install JDK - run: "bash -xc 'JDK_VENDOR=${{ matrix.jdk_vendor }} ./docker/install_jdk.sh'" - - name: Install Untested Nightly Swift - run: "bash -xc './docker/install_untested_nightly_swift.sh'" - # setup caches - - name: Cache local Gradle repository - uses: actions/cache@v4 - continue-on-error: true - with: - path: | - /root/.gradle/caches - /root/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('*/*.gradle*', 'settings.gradle') }} - restore-keys: | - ${{ runner.os }}-gradle- - - name: Cache local SwiftPM repository - uses: actions/cache@v4 - continue-on-error: true - with: - path: /__w/swift-java/swift-java/.build/checkouts - key: ${{ runner.os }}-swiftpm-cache-${{ hashFiles('Package.swift') }} - restore-keys: | - ${{ runner.os }}-swiftpm-cache - ${{ runner.os }}-swiftpm- - # run the actual build - - name: Generate sources (make) (Temporary) - # TODO: this should be triggered by the respective builds - run: "make jextract-generate" - - name: Test Swift - run: "swift test" - - name: Build (Swift) Sample Apps - run: | - find Samples/ -name Package.swift -maxdepth 2 -exec swift build --package-path $(dirname {}) \;; +# test-swift: +# name: Swift tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }}) +# runs-on: ubuntu-latest +# strategy: +# fail-fast: false +# matrix: +# swift_version: ['nightly-main'] +# os_version: ['jammy'] +# jdk_vendor: ['Corretto'] +# container: +# image: ${{ (contains(matrix.swift_version, 'nightly') && 'swiftlang/swift') || 'swift' }}:${{ matrix.swift_version }}-${{ matrix.os_version }} +# env: +# JAVA_HOME: "/usr/lib/jvm/default-jdk" +# steps: +# - uses: actions/checkout@v4 +# - name: Install System Dependencies +# run: apt-get -qq update && apt-get -qq install -y make curl wget +# - name: Install JDK +# run: "bash -xc 'JDK_VENDOR=${{ matrix.jdk_vendor }} ./docker/install_jdk.sh'" +# - name: Install Untested Nightly Swift +# run: "bash -xc './docker/install_untested_nightly_swift.sh'" +# # setup caches +# - name: Cache local Gradle repository +# uses: actions/cache@v4 +# continue-on-error: true +# with: +# path: | +# /root/.gradle/caches +# /root/.gradle/wrapper +# key: ${{ runner.os }}-gradle-${{ hashFiles('*/*.gradle*', 'settings.gradle') }} +# restore-keys: | +# ${{ runner.os }}-gradle- +# - name: Cache local SwiftPM repository +# uses: actions/cache@v4 +# continue-on-error: true +# with: +# path: /__w/swift-java/swift-java/.build/checkouts +# key: ${{ runner.os }}-swiftpm-cache-${{ hashFiles('Package.swift') }} +# restore-keys: | +# ${{ runner.os }}-swiftpm-cache +# ${{ runner.os }}-swiftpm- +# # run the actual build +# - name: Generate sources (make) (Temporary) +# # TODO: this should be triggered by the respective builds +# run: "make jextract-generate" +# - name: Test Swift +# run: "swift test" +# - name: Build (Swift) Sample Apps +# run: | +# find Samples/ -name Package.swift -maxdepth 2 -exec swift build --package-path $(dirname {}) \;; diff --git a/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/ExampleSwiftLibrary.java b/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/ExampleSwiftLibrary.java new file mode 100644 index 00000000..98904dd5 --- /dev/null +++ b/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/ExampleSwiftLibrary.java @@ -0,0 +1,293 @@ +// Generated by jextract-swift +// Swift module: ExampleSwiftLibrary + +package com.example.swift.generated; + +import org.swift.swiftkit.*; +import org.swift.swiftkit.SwiftKit; +import org.swift.swiftkit.util.*; +import java.lang.foreign.*; +import java.lang.invoke.*; +import java.util.Arrays; +import java.util.stream.Collectors; +import java.nio.charset.StandardCharsets; + + public final class ExampleSwiftLibrary { + private ExampleSwiftLibrary() { + // Should not be called directly + } + + static final String LIB_NAME = "ExampleSwiftLibrary"; + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + // TODO: rather than the C ones offer the Swift mappings + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; + public static final AddressLayout C_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, ValueLayout.JAVA_BYTE)); + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; + + + public static final ValueLayout.OfBoolean SWIFT_BOOL = ValueLayout.JAVA_BOOLEAN; + public static final ValueLayout.OfByte SWIFT_INT8 = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfChar SWIFT_UINT16 = ValueLayout.JAVA_CHAR; + public static final ValueLayout.OfShort SWIFT_INT16 = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfInt SWIFT_INT32 = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong SWIFT_INT64 = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfFloat SWIFT_FLOAT = ValueLayout.JAVA_FLOAT; + public static final ValueLayout.OfDouble SWIFT_DOUBLE = ValueLayout.JAVA_DOUBLE; + public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS; + // On the platform this was generated on, Int was Int64 + public static final SequenceLayout SWIFT_BYTE_ARRAY = MemoryLayout.sequenceLayout(8, ValueLayout.JAVA_BYTE); + public static final ValueLayout.OfLong SWIFT_INT = SWIFT_INT64; + public static final ValueLayout.OfLong SWIFT_UINT = SWIFT_INT64; + public static final AddressLayout SWIFT_SELF = SWIFT_POINTER; + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + static void traceDowncall(Object... args) { + var ex = new RuntimeException(); + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("[java][%s:%d] Downcall: %s(%s)\n", + ex.getStackTrace()[1].getFileName(), + ex.getStackTrace()[1].getLineNumber(), + ex.getStackTrace()[1].getMethodName(), + traceArgs); + } + static void trace(Object... args) { + var ex = new RuntimeException(); + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("[java][%s:%d] %s: %s\n", + ex.getStackTrace()[1].getFileName(), + ex.getStackTrace()[1].getLineNumber(), + ex.getStackTrace()[1].getMethodName(), + traceArgs); + } + + static MemorySegment findOrThrow(String symbol) { + return SYMBOL_LOOKUP.find(symbol) + .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } + + static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); + private static SymbolLookup getSymbolLookup() { + if (PlatformUtils.isMacOS()) { + return SymbolLookup.libraryLookup(System.mapLibraryName(LIB_NAME), LIBRARY_ARENA) + .or(SymbolLookup.loaderLookup()) + .or(Linker.nativeLinker().defaultLookup()); + } else { + return SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + } + } + + static { + System.loadLibrary("swiftCore"); + System.loadLibrary(LIB_NAME); + } + + // ==== -------------------------------------------------- + // helloWorld + + private static class helloWorld { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + ); + public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary10helloWorldyyF"); + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 + /** + * Function descriptor for: + * {@snippet lang=swift : + * public func helloWorld() + * } + */ + public static FunctionDescriptor helloWorld$descriptor() { + return helloWorld.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=swift : + * public func helloWorld() + * } + */ + public static MethodHandle helloWorld$handle() { + return helloWorld.HANDLE; + } + + /** + * Address for: + * {@snippet lang=swift : + * public func helloWorld() + * } + */ + public static MemorySegment helloWorld$address() { + return helloWorld.ADDR; + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func helloWorld() + * } + */ + public static void helloWorld() { + var mh$ = helloWorld.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(); + } + mh$.invokeExact(); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + // ==== -------------------------------------------------- + // globalTakeInt + + private static class globalTakeInt { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + SWIFT_INT + ); + public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary13globalTakeInt1iySi_tF"); + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 + /** + * Function descriptor for: + * {@snippet lang=swift : + * public func globalTakeInt(i: Swift.Int) + * } + */ + public static FunctionDescriptor globalTakeInt$descriptor() { + return globalTakeInt.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=swift : + * public func globalTakeInt(i: Swift.Int) + * } + */ + public static MethodHandle globalTakeInt$handle() { + return globalTakeInt.HANDLE; + } + + /** + * Address for: + * {@snippet lang=swift : + * public func globalTakeInt(i: Swift.Int) + * } + */ + public static MemorySegment globalTakeInt$address() { + return globalTakeInt.ADDR; + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func globalTakeInt(i: Swift.Int) + * } + */ + public static void globalTakeInt(long i) { + var mh$ = globalTakeInt.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(i); + } + mh$.invokeExact(i); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + // ==== -------------------------------------------------- + // globalTakeIntInt + + private static class globalTakeIntInt { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + SWIFT_INT, + SWIFT_INT + ); + public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary013globalTakeIntF01i1jySi_SitF"); + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 + /** + * Function descriptor for: + * {@snippet lang=swift : + * public func globalTakeIntInt(i: Swift.Int, j: Swift.Int) + * } + */ + public static FunctionDescriptor globalTakeIntInt$descriptor() { + return globalTakeIntInt.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=swift : + * public func globalTakeIntInt(i: Swift.Int, j: Swift.Int) + * } + */ + public static MethodHandle globalTakeIntInt$handle() { + return globalTakeIntInt.HANDLE; + } + + /** + * Address for: + * {@snippet lang=swift : + * public func globalTakeIntInt(i: Swift.Int, j: Swift.Int) + * } + */ + public static MemorySegment globalTakeIntInt$address() { + return globalTakeIntInt.ADDR; + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func globalTakeIntInt(i: Swift.Int, j: Swift.Int) + * } + */ + public static void globalTakeIntInt(long i, long j) { + var mh$ = globalTakeIntInt.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(i, j); + } + mh$.invokeExact(i, j); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + } // printModuleClass(_:body:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:201 diff --git a/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/MySwiftClass.java b/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/MySwiftClass.java new file mode 100644 index 00000000..80f10d4c --- /dev/null +++ b/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/MySwiftClass.java @@ -0,0 +1,765 @@ +// Generated by jextract-swift +// Swift module: ExampleSwiftLibrary + +package com.example.swift.generated; + +import org.swift.swiftkit.*; +import org.swift.swiftkit.SwiftKit; +import org.swift.swiftkit.util.*; +import java.lang.foreign.*; +import java.lang.invoke.*; +import java.util.Arrays; +import java.util.stream.Collectors; +import java.nio.charset.StandardCharsets; + + public final class MySwiftClass implements SwiftHeapObject { + // Pointer to the referred to class instance's "self". + private final MemorySegment selfMemorySegment; + public final MemorySegment $memorySegment() { + return this.selfMemorySegment; + } + + static final String LIB_NAME = "ExampleSwiftLibrary"; + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + // TODO: rather than the C ones offer the Swift mappings + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; + public static final AddressLayout C_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, ValueLayout.JAVA_BYTE)); + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; + + + public static final ValueLayout.OfBoolean SWIFT_BOOL = ValueLayout.JAVA_BOOLEAN; + public static final ValueLayout.OfByte SWIFT_INT8 = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfChar SWIFT_UINT16 = ValueLayout.JAVA_CHAR; + public static final ValueLayout.OfShort SWIFT_INT16 = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfInt SWIFT_INT32 = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong SWIFT_INT64 = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfFloat SWIFT_FLOAT = ValueLayout.JAVA_FLOAT; + public static final ValueLayout.OfDouble SWIFT_DOUBLE = ValueLayout.JAVA_DOUBLE; + public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS; + // On the platform this was generated on, Int was Int64 + public static final SequenceLayout SWIFT_BYTE_ARRAY = MemoryLayout.sequenceLayout(8, ValueLayout.JAVA_BYTE); + public static final ValueLayout.OfLong SWIFT_INT = SWIFT_INT64; + public static final ValueLayout.OfLong SWIFT_UINT = SWIFT_INT64; + public static final AddressLayout SWIFT_SELF = SWIFT_POINTER; + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + SWIFT_POINTER + ).withName("$s19ExampleSwiftLibrary02MyB5ClassCMa"); + public final GroupLayout $layout() { + return $LAYOUT; + } + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + static void traceDowncall(Object... args) { + var ex = new RuntimeException(); + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("[java][%s:%d] Downcall: %s(%s)\n", + ex.getStackTrace()[1].getFileName(), + ex.getStackTrace()[1].getLineNumber(), + ex.getStackTrace()[1].getMethodName(), + traceArgs); + } + static void trace(Object... args) { + var ex = new RuntimeException(); + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("[java][%s:%d] %s: %s\n", + ex.getStackTrace()[1].getFileName(), + ex.getStackTrace()[1].getLineNumber(), + ex.getStackTrace()[1].getMethodName(), + traceArgs); + } + + static { + System.loadLibrary("swiftCore"); + System.loadLibrary(LIB_NAME); + } + + public static final String TYPE_MANGLED_NAME = "$s19ExampleSwiftLibrary02MyB5ClassCMa"; + public static final SwiftAnyType TYPE_METADATA = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_MANGLED_NAME).get(); + public final SwiftAnyType $swiftType() { + return TYPE_METADATA; + } + + + // ==== -------------------------------------------------- + // init(len:cap:) + + private static class init_len_cap { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /* -> */SWIFT_POINTER, + SWIFT_INT, + SWIFT_INT, + SWIFT_POINTER + ); + public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary02MyB5ClassC3len3capACSi_SitcfC"); + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } // printClassConstructors(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:400 + /** + * Create an instance of {@code MySwiftClass}. + * + * {@snippet lang=swift : + * public init(len: Swift.Int, cap: Swift.Int) + * } + */ + public MySwiftClass(long len, long cap) { + this(/*arena=*/null, len, cap); + } + + /** + * Create an instance of {@code MySwiftClass}. + * This instance is managed by the passed in {@link SwiftArena} and may not outlive the arena's lifetime. + * + * {@snippet lang=swift : + * public init(len: Swift.Int, cap: Swift.Int) + * } + */ + public MySwiftClass(SwiftArena arena, long len, long cap) { + var mh$ = init_len_cap.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(len, cap); + } + this.selfMemorySegment = (MemorySegment) mh$.invokeExact(len, cap, TYPE_METADATA.$memorySegment()); + if (arena != null) { + arena.register(this); + } + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + // ==== -------------------------------------------------- + // len + + private static class len { + public static final FunctionDescriptor DESC_GET = FunctionDescriptor.of( + /* -> */SWIFT_INT, + SWIFT_POINTER + ); + public static final MemorySegment ADDR_GET = ExampleSwiftLibrary.findOrThrow("g"); + public static final MethodHandle HANDLE_GET = Linker.nativeLinker().downcallHandle(ADDR_GET, DESC_GET); + public static final FunctionDescriptor DESC_SET = FunctionDescriptor.ofVoid( + SWIFT_INT, + SWIFT_POINTER + ); + public static final MemorySegment ADDR_SET = ExampleSwiftLibrary.findOrThrow("s"); + public static final MethodHandle HANDLE_SET = Linker.nativeLinker().downcallHandle(ADDR_SET, DESC_SET); + } // printVariableDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:553 + /** + * Function descriptor for: + * + */ + public static FunctionDescriptor len$get$descriptor() { + return len.DESC_GET; + } + + /** + * Downcall method handle for: + * + */ + public static MethodHandle len$get$handle() { + return len.HANDLE_GET; + } + + /** + * Address for: + * + */ + public static MemorySegment len$get$address() { + return len.ADDR_GET; + } + + /** + * Function descriptor for: + * + */ + public static FunctionDescriptor len$set$descriptor() { + return len.DESC_SET; + } + + /** + * Downcall method handle for: + * + */ + public static MethodHandle len$set$handle() { + return len.HANDLE_SET; + } + + /** + * Address for: + * + */ + public static MemorySegment len$set$address() { + return len.ADDR_SET; + } + + /** + * Downcall to Swift: + * + */ + public static long getLen(java.lang.foreign.MemorySegment self$) { + var mh$ = len.HANDLE_GET; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(self$); + } + return (long) mh$.invokeExact(self$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Downcall to Swift: + * + */ + public long getLen() { + return (long) getLen($memorySegment()); + } + + /** + * Downcall to Swift: + * + */ + public static void setLen(long newValue, java.lang.foreign.MemorySegment self$) { + var mh$ = len.HANDLE_SET; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(newValue, self$); + } + mh$.invokeExact(newValue, self$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Downcall to Swift: + * + */ + public void setLen(long newValue) { + setLen(newValue, $memorySegment()); + } + + // ==== -------------------------------------------------- + // cap + + private static class cap { + public static final FunctionDescriptor DESC_GET = FunctionDescriptor.of( + /* -> */SWIFT_INT, + SWIFT_POINTER + ); + public static final MemorySegment ADDR_GET = ExampleSwiftLibrary.findOrThrow("g"); + public static final MethodHandle HANDLE_GET = Linker.nativeLinker().downcallHandle(ADDR_GET, DESC_GET); + public static final FunctionDescriptor DESC_SET = FunctionDescriptor.ofVoid( + SWIFT_INT, + SWIFT_POINTER + ); + public static final MemorySegment ADDR_SET = ExampleSwiftLibrary.findOrThrow("s"); + public static final MethodHandle HANDLE_SET = Linker.nativeLinker().downcallHandle(ADDR_SET, DESC_SET); + } // printVariableDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:553 + /** + * Function descriptor for: + * + */ + public static FunctionDescriptor cap$get$descriptor() { + return cap.DESC_GET; + } + + /** + * Downcall method handle for: + * + */ + public static MethodHandle cap$get$handle() { + return cap.HANDLE_GET; + } + + /** + * Address for: + * + */ + public static MemorySegment cap$get$address() { + return cap.ADDR_GET; + } + + /** + * Function descriptor for: + * + */ + public static FunctionDescriptor cap$set$descriptor() { + return cap.DESC_SET; + } + + /** + * Downcall method handle for: + * + */ + public static MethodHandle cap$set$handle() { + return cap.HANDLE_SET; + } + + /** + * Address for: + * + */ + public static MemorySegment cap$set$address() { + return cap.ADDR_SET; + } + + /** + * Downcall to Swift: + * + */ + public static long getCap(java.lang.foreign.MemorySegment self$) { + var mh$ = cap.HANDLE_GET; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(self$); + } + return (long) mh$.invokeExact(self$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Downcall to Swift: + * + */ + public long getCap() { + return (long) getCap($memorySegment()); + } + + /** + * Downcall to Swift: + * + */ + public static void setCap(long newValue, java.lang.foreign.MemorySegment self$) { + var mh$ = cap.HANDLE_SET; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(newValue, self$); + } + mh$.invokeExact(newValue, self$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Downcall to Swift: + * + */ + public void setCap(long newValue) { + setCap(newValue, $memorySegment()); + } + + // ==== -------------------------------------------------- + // counter + + private static class counter { + public static final FunctionDescriptor DESC_GET = FunctionDescriptor.of( + /* -> */SWIFT_INT32, + SWIFT_POINTER + ); + public static final MemorySegment ADDR_GET = ExampleSwiftLibrary.findOrThrow("g"); + public static final MethodHandle HANDLE_GET = Linker.nativeLinker().downcallHandle(ADDR_GET, DESC_GET); + public static final FunctionDescriptor DESC_SET = FunctionDescriptor.ofVoid( + SWIFT_INT32, + SWIFT_POINTER + ); + public static final MemorySegment ADDR_SET = ExampleSwiftLibrary.findOrThrow("s"); + public static final MethodHandle HANDLE_SET = Linker.nativeLinker().downcallHandle(ADDR_SET, DESC_SET); + } // printVariableDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:553 + /** + * Function descriptor for: + * + */ + public static FunctionDescriptor counter$get$descriptor() { + return counter.DESC_GET; + } + + /** + * Downcall method handle for: + * + */ + public static MethodHandle counter$get$handle() { + return counter.HANDLE_GET; + } + + /** + * Address for: + * + */ + public static MemorySegment counter$get$address() { + return counter.ADDR_GET; + } + + /** + * Function descriptor for: + * + */ + public static FunctionDescriptor counter$set$descriptor() { + return counter.DESC_SET; + } + + /** + * Downcall method handle for: + * + */ + public static MethodHandle counter$set$handle() { + return counter.HANDLE_SET; + } + + /** + * Address for: + * + */ + public static MemorySegment counter$set$address() { + return counter.ADDR_SET; + } + + /** + * Downcall to Swift: + * + */ + public static int getCounter(java.lang.foreign.MemorySegment self$) { + var mh$ = counter.HANDLE_GET; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(self$); + } + return (int) mh$.invokeExact(self$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Downcall to Swift: + * + */ + public int getCounter() { + return (int) getCounter($memorySegment()); + } + + /** + * Downcall to Swift: + * + */ + public static void setCounter(int newValue, java.lang.foreign.MemorySegment self$) { + var mh$ = counter.HANDLE_SET; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(newValue, self$); + } + mh$.invokeExact(newValue, self$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Downcall to Swift: + * + */ + public void setCounter(int newValue) { + setCounter(newValue, $memorySegment()); + } + + // ==== -------------------------------------------------- + // voidMethod + + private static class voidMethod { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + SWIFT_POINTER + ); + public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary02MyB5ClassC10voidMethodyyF"); + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 + /** + * Function descriptor for: + * {@snippet lang=swift : + * public func voidMethod() + * } + */ + public static FunctionDescriptor voidMethod$descriptor() { + return voidMethod.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=swift : + * public func voidMethod() + * } + */ + public static MethodHandle voidMethod$handle() { + return voidMethod.HANDLE; + } + + /** + * Address for: + * {@snippet lang=swift : + * public func voidMethod() + * } + */ + public static MemorySegment voidMethod$address() { + return voidMethod.ADDR; + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func voidMethod() + * } + */ + public static void voidMethod(java.lang.foreign.MemorySegment self$) { + var mh$ = voidMethod.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(self$); + } + mh$.invokeExact(self$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func voidMethod() + * } + */ + public void voidMethod() { + voidMethod($memorySegment()); + } + + // ==== -------------------------------------------------- + // takeIntMethod + + private static class takeIntMethod { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + SWIFT_INT, + SWIFT_POINTER + ); + public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary02MyB5ClassC13takeIntMethod1iySi_tF"); + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 + /** + * Function descriptor for: + * {@snippet lang=swift : + * public func takeIntMethod(i: Swift.Int) + * } + */ + public static FunctionDescriptor takeIntMethod$descriptor() { + return takeIntMethod.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=swift : + * public func takeIntMethod(i: Swift.Int) + * } + */ + public static MethodHandle takeIntMethod$handle() { + return takeIntMethod.HANDLE; + } + + /** + * Address for: + * {@snippet lang=swift : + * public func takeIntMethod(i: Swift.Int) + * } + */ + public static MemorySegment takeIntMethod$address() { + return takeIntMethod.ADDR; + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func takeIntMethod(i: Swift.Int) + * } + */ + public static void takeIntMethod(long i, java.lang.foreign.MemorySegment self$) { + var mh$ = takeIntMethod.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(i, self$); + } + mh$.invokeExact(i, self$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func takeIntMethod(i: Swift.Int) + * } + */ + public void takeIntMethod(long i) { + takeIntMethod(i, $memorySegment()); + } + + // ==== -------------------------------------------------- + // echoIntMethod + + private static class echoIntMethod { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /* -> */SWIFT_INT, + SWIFT_INT, + SWIFT_POINTER + ); + public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary02MyB5ClassC13echoIntMethod1iS2i_tF"); + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 + /** + * Function descriptor for: + * {@snippet lang=swift : + * public func echoIntMethod(i: Swift.Int) -> Swift.Int + * } + */ + public static FunctionDescriptor echoIntMethod$descriptor() { + return echoIntMethod.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=swift : + * public func echoIntMethod(i: Swift.Int) -> Swift.Int + * } + */ + public static MethodHandle echoIntMethod$handle() { + return echoIntMethod.HANDLE; + } + + /** + * Address for: + * {@snippet lang=swift : + * public func echoIntMethod(i: Swift.Int) -> Swift.Int + * } + */ + public static MemorySegment echoIntMethod$address() { + return echoIntMethod.ADDR; + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func echoIntMethod(i: Swift.Int) -> Swift.Int + * } + */ + public static long echoIntMethod(long i, java.lang.foreign.MemorySegment self$) { + var mh$ = echoIntMethod.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(i, self$); + } + return (long) mh$.invokeExact(i, self$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func echoIntMethod(i: Swift.Int) -> Swift.Int + * } + */ + public long echoIntMethod(long i) { + return (long) echoIntMethod(i, $memorySegment()); + } + + // ==== -------------------------------------------------- + // makeIntMethod + + private static class makeIntMethod { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /* -> */SWIFT_INT, + SWIFT_POINTER + ); + public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary02MyB5ClassC13makeIntMethodSiyF"); + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 + /** + * Function descriptor for: + * {@snippet lang=swift : + * public func makeIntMethod() -> Swift.Int + * } + */ + public static FunctionDescriptor makeIntMethod$descriptor() { + return makeIntMethod.DESC; + } + + /** + * Downcall method handle for: + * {@snippet lang=swift : + * public func makeIntMethod() -> Swift.Int + * } + */ + public static MethodHandle makeIntMethod$handle() { + return makeIntMethod.HANDLE; + } + + /** + * Address for: + * {@snippet lang=swift : + * public func makeIntMethod() -> Swift.Int + * } + */ + public static MemorySegment makeIntMethod$address() { + return makeIntMethod.ADDR; + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func makeIntMethod() -> Swift.Int + * } + */ + public static long makeIntMethod(java.lang.foreign.MemorySegment self$) { + var mh$ = makeIntMethod.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall(self$); + } + return (long) mh$.invokeExact(self$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func makeIntMethod() -> Swift.Int + * } + */ + public long makeIntMethod() { + return (long) makeIntMethod($memorySegment()); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "(" + + SwiftKit.nameOfSwiftType($swiftType().$memorySegment(), true) + + ")@" + $memorySegment(); + } + + } // printClass(_:_:body:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:182 diff --git a/Samples/SwiftKitSampleApp/src/generated/java/org/swift/swiftkit/generated/SwiftKitSwift.java b/Samples/SwiftKitSampleApp/src/generated/java/org/swift/swiftkit/generated/SwiftKitSwift.java new file mode 100644 index 00000000..fb1672a8 --- /dev/null +++ b/Samples/SwiftKitSampleApp/src/generated/java/org/swift/swiftkit/generated/SwiftKitSwift.java @@ -0,0 +1,119 @@ +// Generated by jextract-swift +// Swift module: SwiftKitSwift + +package org.swift.swiftkit.generated; + +import org.swift.swiftkit.*; +import org.swift.swiftkit.SwiftKit; +import org.swift.swiftkit.util.*; +import java.lang.foreign.*; +import java.lang.invoke.*; +import java.util.Arrays; +import java.util.stream.Collectors; +import java.nio.charset.StandardCharsets; + + public final class SwiftKitSwift { + private SwiftKitSwift() { + // Should not be called directly + } + + static final String LIB_NAME = "SwiftKitSwift"; + static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + // TODO: rather than the C ones offer the Swift mappings + public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; + public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; + public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; + public static final AddressLayout C_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, ValueLayout.JAVA_BYTE)); + public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; + + + public static final ValueLayout.OfBoolean SWIFT_BOOL = ValueLayout.JAVA_BOOLEAN; + public static final ValueLayout.OfByte SWIFT_INT8 = ValueLayout.JAVA_BYTE; + public static final ValueLayout.OfChar SWIFT_UINT16 = ValueLayout.JAVA_CHAR; + public static final ValueLayout.OfShort SWIFT_INT16 = ValueLayout.JAVA_SHORT; + public static final ValueLayout.OfInt SWIFT_INT32 = ValueLayout.JAVA_INT; + public static final ValueLayout.OfLong SWIFT_INT64 = ValueLayout.JAVA_LONG; + public static final ValueLayout.OfFloat SWIFT_FLOAT = ValueLayout.JAVA_FLOAT; + public static final ValueLayout.OfDouble SWIFT_DOUBLE = ValueLayout.JAVA_DOUBLE; + public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS; + // On the platform this was generated on, Int was Int64 + public static final SequenceLayout SWIFT_BYTE_ARRAY = MemoryLayout.sequenceLayout(8, ValueLayout.JAVA_BYTE); + public static final ValueLayout.OfLong SWIFT_INT = SWIFT_INT64; + public static final ValueLayout.OfLong SWIFT_UINT = SWIFT_INT64; + public static final AddressLayout SWIFT_SELF = SWIFT_POINTER; + + static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + static void traceDowncall(Object... args) { + var ex = new RuntimeException(); + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("[java][%s:%d] Downcall: %s(%s)\n", + ex.getStackTrace()[1].getFileName(), + ex.getStackTrace()[1].getLineNumber(), + ex.getStackTrace()[1].getMethodName(), + traceArgs); + } + static void trace(Object... args) { + var ex = new RuntimeException(); + String traceArgs = Arrays.stream(args) + .map(Object::toString) + .collect(Collectors.joining(", ")); + System.out.printf("[java][%s:%d] %s: %s\n", + ex.getStackTrace()[1].getFileName(), + ex.getStackTrace()[1].getLineNumber(), + ex.getStackTrace()[1].getMethodName(), + traceArgs); + } + + static MemorySegment findOrThrow(String symbol) { + return SYMBOL_LOOKUP.find(symbol) + .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); + } + + static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { + try { + return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); + } catch (ReflectiveOperationException ex) { + throw new AssertionError(ex); + } + } + + static MemoryLayout align(MemoryLayout layout, long align) { + return switch (layout) { + case PaddingLayout p -> p; + case ValueLayout v -> v.withByteAlignment(align); + case GroupLayout g -> { + MemoryLayout[] alignedMembers = g.memberLayouts().stream() + .map(m -> align(m, align)).toArray(MemoryLayout[]::new); + yield g instanceof StructLayout ? + MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); + } + case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); + }; + } + + static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); + private static SymbolLookup getSymbolLookup() { + if (PlatformUtils.isMacOS()) { + return SymbolLookup.libraryLookup(System.mapLibraryName(LIB_NAME), LIBRARY_ARENA) + .or(SymbolLookup.loaderLookup()) + .or(Linker.nativeLinker().defaultLookup()); + } else { + return SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + } + } + + static { + System.loadLibrary("swiftCore"); + System.loadLibrary(LIB_NAME); + } + + } // printModuleClass(_:body:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:201 diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java index 06fcb680..1b98d1e0 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java @@ -27,26 +27,22 @@ public class MySwiftClassTest { @BeforeAll static void beforeAll() { - System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path")); - - System.loadLibrary("swiftCore"); - System.loadLibrary("ExampleSwiftLibrary"); - - System.setProperty("jextract.trace.downcalls", "true"); + System.out.printf("java.library.path = %s\n", SwiftKit.getJavaLibraryPath()); + System.out.printf("jextract.trace.downcalls = %s\n", SwiftKit.getJextractTraceDowncalls()); } @Test void call_retain_retainCount_release() { var arena = SwiftArena.ofConfined(); - var obj = new MySwiftClass(arena, 1, 2); + var obj = new MySwiftClass(arena, 1, 2); - assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); - // TODO: test directly on SwiftHeapObject inheriting obj + assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); + // TODO: test directly on SwiftHeapObject inheriting obj - SwiftKit.retain(obj.$memorySegment()); - assertEquals(2, SwiftKit.retainCount(obj.$memorySegment())); + SwiftKit.retain(obj.$memorySegment()); + assertEquals(2, SwiftKit.retainCount(obj.$memorySegment())); - SwiftKit.release(obj.$memorySegment()); - assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); + SwiftKit.release(obj.$memorySegment()); + assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); } } diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java index 73085bf6..8bbb6239 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java @@ -26,21 +26,14 @@ public class SwiftArenaTest { @BeforeAll static void beforeAll() { - System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path")); - - System.loadLibrary("swiftCore"); - System.loadLibrary("ExampleSwiftLibrary"); - - System.setProperty("jextract.trace.downcalls", "true"); + System.out.printf("java.library.path = %s\n", SwiftKit.getJavaLibraryPath()); + System.out.printf("jextract.trace.downcalls = %s\n", SwiftKit.getJextractTraceDowncalls()); } @Test public void arena_releaseClassOnClose_class_ok() { - MySwiftClass unsafelyEscaped = null; - try (var arena = SwiftArena.ofConfined()) { var obj = new MySwiftClass(arena,1, 2); - unsafelyEscaped = obj; // also known as "don't do this" (outliving the arena) retain(obj.$memorySegment()); assertEquals(2, retainCount(obj.$memorySegment())); diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java index 660a5500..c1ab8995 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java @@ -29,8 +29,10 @@ static void setSwiftPointerAddress(MemorySegment target, MemorySegment memorySeg // Write the address of as the value of the newly created pointer. // We need to type-safely set the pointer value which may be 64 or 32-bit. if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { + System.out.println("[setSwiftPointerAddress] address is long = " + memorySegment.address()); target.set(ValueLayout.JAVA_LONG, /*offset=*/0, memorySegment.address()); } else { + System.out.println("[setSwiftPointerAddress] address is int = " + memorySegment.address()); target.set(ValueLayout.JAVA_INT, /*offset=*/0, (int) memorySegment.address()); } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java index 599e6e52..98bfd5ef 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java @@ -14,13 +14,9 @@ package org.swift.swiftkit; -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Logger; /** * A Swift arena manages Swift allocated memory for classes, structs, enums etc. @@ -29,20 +25,30 @@ * A confined arena has an associated owner thread that confines some operations to * associated owner thread such as {@link #close()}. */ -public interface SwiftArena extends Arena { +public interface SwiftArena extends AutoCloseable { static SwiftArena ofConfined() { return new ConfinedSwiftMemorySession(Thread.currentThread()); } /** - * Register a struct, enum or other non-reference counted Swift object. + * Register a Swift reference counted heap object with this arena (such as a {@code class} or {@code actor}). * Its memory should be considered managed by this arena, and be destroyed when the arena is closed. */ void register(SwiftHeapObject object); + /** + * Register a struct, enum or other non-reference counted Swift object. + * Its memory should be considered managed by this arena, and be destroyed when the arena is closed. + */ void register(SwiftValue value); + /** + * Close the arena and make sure all objects it managed are released. + * Throws if unable to verify all resources have been release (e.g. over retained Swift classes) + */ + void close(); + } final class ConfinedSwiftMemorySession implements SwiftArena { @@ -51,30 +57,16 @@ final class ConfinedSwiftMemorySession implements SwiftArena { final Thread owner; final SwiftResourceList resources; - // TODO: just int and volatile updates final int CLOSED = 0; final int ACTIVE = 1; final AtomicInteger state; public ConfinedSwiftMemorySession(Thread owner) { this.owner = owner; -// underlying = Arena.ofConfined(); resources = new ConfinedResourceList(); state = new AtomicInteger(ACTIVE); } - @Override - public MemorySegment allocate(long byteSize, long byteAlignment) { -// return underlying.allocate(byteSize, byteAlignment); - return null; - } - - @Override - public MemorySegment.Scope scope() { - return null; -// return underlying.scope(); - } - public void checkValid() throws RuntimeException { if (this.owner != null && this.owner != Thread.currentThread()) { throw new WrongThreadException("ConfinedSwift arena is confined to %s but was closed from %s!".formatted(this.owner, Thread.currentThread())); @@ -85,7 +77,6 @@ public void checkValid() throws RuntimeException { @Override public void register(SwiftHeapObject object) { - System.out.println("Registered " + object.$memorySegment() + " in " + this); this.resources.add(new SwiftHeapObjectCleanup(object)); } @@ -96,20 +87,12 @@ public void register(SwiftValue value) { @Override public void close() { - System.out.println("CLOSE ARENA ..."); checkValid(); // Cleanup all resources if (this.state.compareAndExchange(ACTIVE, CLOSED) == ACTIVE) { this.resources.cleanup(); } // else, was already closed; do nothing - - - // Those the underlying arena -// this.underlying.close(); - - // After this method returns normally, the scope must be not alive anymore -// assert (!this.scope().isAlive()); } /** @@ -117,9 +100,9 @@ public void close() { */ static abstract class SwiftResourceList implements Runnable { // TODO: Could use intrusive linked list to avoid one indirection here - final List resourceCleanups = new LinkedList<>(); + final List resourceCleanups = new LinkedList<>(); - abstract void add(SwiftMemoryResourceCleanup cleanup); + abstract void add(SwiftInstanceCleanup cleanup); public abstract void cleanup(); @@ -130,17 +113,19 @@ public final void run() { static final class ConfinedResourceList extends SwiftResourceList { @Override - void add(SwiftMemoryResourceCleanup cleanup) { + void add(SwiftInstanceCleanup cleanup) { resourceCleanups.add(cleanup); } @Override public void cleanup() { - for (SwiftMemoryResourceCleanup cleanup : resourceCleanups) { + for (SwiftInstanceCleanup cleanup : resourceCleanups) { cleanup.run(); } } } + + } final class UnexpectedRetainCountException extends RuntimeException { diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java similarity index 88% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java rename to SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java index 212b0922..e28e0dd7 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java @@ -19,10 +19,10 @@ /** * A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. */ -sealed interface SwiftMemoryResourceCleanup extends Runnable { +sealed interface SwiftInstanceCleanup extends Runnable { } -record SwiftHeapObjectCleanup(SwiftHeapObject instance) implements SwiftMemoryResourceCleanup { +record SwiftHeapObjectCleanup(SwiftHeapObject instance) implements SwiftInstanceCleanup { @Override public void run() throws UnexpectedRetainCountException { @@ -34,6 +34,9 @@ public void run() throws UnexpectedRetainCountException { // Destroy (and deinit) the object: var ty = this.instance.$swiftType(); + System.out.println("x destroy instance = " + this.instance); + System.out.println("x destroy ty = " + ty); + SwiftValueWitnessTable.destroy(ty, this.instance.$memorySegment()); // Invalidate the Java wrapper class, in order to prevent effectively use-after-free issues. @@ -41,7 +44,7 @@ public void run() throws UnexpectedRetainCountException { } } -record SwiftValueCleanup(MemorySegment resource) implements SwiftMemoryResourceCleanup { +record SwiftValueCleanup(MemorySegment resource) implements SwiftInstanceCleanup { @Override public void run() { throw new RuntimeException("not implemented yet"); diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index 1b3f0eff..064be4ec 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -59,7 +59,7 @@ static void traceDowncall(String name, Object... args) { String traceArgs = Arrays.stream(args) .map(Object::toString) .collect(Collectors.joining(", ")); - System.out.printf("%s(%s)\n", name, traceArgs); + System.out.printf("[java] Downcall: %s(%s)\n", name, traceArgs); } static MemorySegment findOrThrow(String symbol) { @@ -67,6 +67,14 @@ static MemorySegment findOrThrow(String symbol) { .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); } + public static String getJavaLibraryPath() { + return System.getProperty("java.library.path"); + } + + public static boolean getJextractTraceDowncalls() { + return Boolean.getBoolean("jextract.trace.downcalls"); + } + // ==== ------------------------------------------------------------------------------------------------------------ // free diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java index 10938b71..afc47dca 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -209,7 +209,9 @@ static MethodHandle handle(SwiftAnyType ty) { */ public static void destroy(SwiftAnyType type, MemorySegment object) { var fullTypeMetadata = fullTypeMetadata(type.$memorySegment()); + System.out.println("fullTypeMetadata = " + fullTypeMetadata); var wtable = valueWitnessTable(fullTypeMetadata); + System.out.println("wtable = " + wtable); var mh = destroy.handle(type); From 0f60bffeb4fc271fec00dc47a5efe5abac0e8fca Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 22 Oct 2024 14:41:31 +0900 Subject: [PATCH 16/18] Disable test on linux/amd64 until we figure it out, in meantime it works on arm macs --- .github/workflows/pull_request.yml | 114 +++++++++--------- .../com/example/swift/HelloJava2Swift.java | 31 ++--- .../org/swift/swiftkit/SwiftArenaTest.java | 9 ++ .../org/swift/swiftkit/SwiftValueLayout.java | 3 +- .../swiftkit/SwiftValueWitnessTable.java | 5 +- .../swift/swiftkit/util/PlatformUtils.java | 9 ++ .../swift/swiftkit/gradle/BuildUtils.groovy | 22 ++-- 7 files changed, 101 insertions(+), 92 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 0ea4e88f..08ebc7b6 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -5,13 +5,13 @@ on: types: [opened, reopened, synchronize] jobs: -# soundness: -# uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main -# with: -# api_breakage_check_enabled: false -# # FIXME: Something is off with the format task and it gets "stuck", need to investigate -# format_check_enabled: false -# license_header_check_project_name: Swift.org + soundness: + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main + with: + api_breakage_check_enabled: false + # FIXME: Something is off with the format task and it gets "stuck", need to investigate + format_check_enabled: false + license_header_check_project_name: Swift.org test-java: name: Java tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }}) @@ -62,53 +62,53 @@ jobs: find . ./gradlew build --info --no-daemon -# test-swift: -# name: Swift tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }}) -# runs-on: ubuntu-latest -# strategy: -# fail-fast: false -# matrix: -# swift_version: ['nightly-main'] -# os_version: ['jammy'] -# jdk_vendor: ['Corretto'] -# container: -# image: ${{ (contains(matrix.swift_version, 'nightly') && 'swiftlang/swift') || 'swift' }}:${{ matrix.swift_version }}-${{ matrix.os_version }} -# env: -# JAVA_HOME: "/usr/lib/jvm/default-jdk" -# steps: -# - uses: actions/checkout@v4 -# - name: Install System Dependencies -# run: apt-get -qq update && apt-get -qq install -y make curl wget -# - name: Install JDK -# run: "bash -xc 'JDK_VENDOR=${{ matrix.jdk_vendor }} ./docker/install_jdk.sh'" -# - name: Install Untested Nightly Swift -# run: "bash -xc './docker/install_untested_nightly_swift.sh'" -# # setup caches -# - name: Cache local Gradle repository -# uses: actions/cache@v4 -# continue-on-error: true -# with: -# path: | -# /root/.gradle/caches -# /root/.gradle/wrapper -# key: ${{ runner.os }}-gradle-${{ hashFiles('*/*.gradle*', 'settings.gradle') }} -# restore-keys: | -# ${{ runner.os }}-gradle- -# - name: Cache local SwiftPM repository -# uses: actions/cache@v4 -# continue-on-error: true -# with: -# path: /__w/swift-java/swift-java/.build/checkouts -# key: ${{ runner.os }}-swiftpm-cache-${{ hashFiles('Package.swift') }} -# restore-keys: | -# ${{ runner.os }}-swiftpm-cache -# ${{ runner.os }}-swiftpm- -# # run the actual build -# - name: Generate sources (make) (Temporary) -# # TODO: this should be triggered by the respective builds -# run: "make jextract-generate" -# - name: Test Swift -# run: "swift test" -# - name: Build (Swift) Sample Apps -# run: | -# find Samples/ -name Package.swift -maxdepth 2 -exec swift build --package-path $(dirname {}) \;; + test-swift: + name: Swift tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + swift_version: ['nightly-main'] + os_version: ['jammy'] + jdk_vendor: ['Corretto'] + container: + image: ${{ (contains(matrix.swift_version, 'nightly') && 'swiftlang/swift') || 'swift' }}:${{ matrix.swift_version }}-${{ matrix.os_version }} + env: + JAVA_HOME: "/usr/lib/jvm/default-jdk" + steps: + - uses: actions/checkout@v4 + - name: Install System Dependencies + run: apt-get -qq update && apt-get -qq install -y make curl wget + - name: Install JDK + run: "bash -xc 'JDK_VENDOR=${{ matrix.jdk_vendor }} ./docker/install_jdk.sh'" + - name: Install Untested Nightly Swift + run: "bash -xc './docker/install_untested_nightly_swift.sh'" + # setup caches + - name: Cache local Gradle repository + uses: actions/cache@v4 + continue-on-error: true + with: + path: | + /root/.gradle/caches + /root/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('*/*.gradle*', 'settings.gradle') }} + restore-keys: | + ${{ runner.os }}-gradle- + - name: Cache local SwiftPM repository + uses: actions/cache@v4 + continue-on-error: true + with: + path: /__w/swift-java/swift-java/.build/checkouts + key: ${{ runner.os }}-swiftpm-cache-${{ hashFiles('Package.swift') }} + restore-keys: | + ${{ runner.os }}-swiftpm-cache + ${{ runner.os }}-swiftpm- + # run the actual build + - name: Generate sources (make) (Temporary) + # TODO: this should be triggered by the respective builds + run: "make jextract-generate" + - name: Test Swift + run: "swift test" + - name: Build (Swift) Sample Apps + run: | + find Samples/ -name Package.swift -maxdepth 2 -exec swift build --package-path $(dirname {}) \;; diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index 703fa68c..51dd48b1 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -15,6 +15,7 @@ package com.example.swift; // Import swift-extract generated sources +import com.example.swift.generated.ExampleSwiftLibrary; import com.example.swift.generated.MySwiftClass; // Import javakit/swiftkit support libraries @@ -36,33 +37,17 @@ public static void main(String[] args) { } static void examples() { -// ExampleSwiftLibrary.helloWorld(); -// -// ExampleSwiftLibrary.globalTakeInt(1337); -// -// MySwiftClass obj = new MySwiftClass(2222, 7777); -// -// SwiftKit.retain(obj.$memorySegment()); -// System.out.println("[java] obj ref count = " + SwiftKit.retainCount(obj.$memorySegment())); -// -// obj.voidMethod(); -// obj.takeIntMethod(42); - - MySwiftClass unsafelyEscaped = null; - try (var arena = SwiftArena.ofConfined()) { - var instance = new MySwiftClass(arena, 1111, 2222); - unsafelyEscaped = instance; + ExampleSwiftLibrary.helloWorld(); -// var num = instance.makeIntMethod(); + ExampleSwiftLibrary.globalTakeInt(1337); - System.out.println("SwiftKit.retainCount(instance) = " + SwiftKit.retainCount(instance)); + MySwiftClass obj = new MySwiftClass(2222, 7777); - System.out.println("MySwiftClass.TYPE_MANGLED_NAME = " + MySwiftClass.TYPE_MANGLED_NAME); - MemorySegment typeMetadata = SwiftValueWitnessTable.fullTypeMetadata(MySwiftClass.TYPE_METADATA.$memorySegment()); - System.out.println("typeMetadata = " + typeMetadata); - } // instance should be deallocated + SwiftKit.retain(obj.$memorySegment()); + System.out.println("[java] obj ref count = " + SwiftKit.retainCount(obj.$memorySegment())); - var num = unsafelyEscaped.makeIntMethod(); + obj.voidMethod(); + obj.takeIntMethod(42); System.out.println("DONE."); } diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java index 8bbb6239..4f4ab9df 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java @@ -17,6 +17,8 @@ import com.example.swift.generated.MySwiftClass; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIf; +import org.swift.swiftkit.util.PlatformUtils; import static org.junit.jupiter.api.Assertions.*; import static org.swift.swiftkit.SwiftKit.*; @@ -30,7 +32,14 @@ static void beforeAll() { System.out.printf("jextract.trace.downcalls = %s\n", SwiftKit.getJextractTraceDowncalls()); } + static boolean isAmd64() { + return PlatformUtils.isAmd64(); + } + + // FIXME: The destroy witness table call hangs on x86_64 platforms during the destroy witness table call + // See: https://github.com/swiftlang/swift-java/issues/97 @Test + @DisabledIf("isAmd64") public void arena_releaseClassOnClose_class_ok() { try (var arena = SwiftArena.ofConfined()) { var obj = new MySwiftClass(arena,1, 2); diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java index bf1a82c5..565b6da5 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java @@ -18,8 +18,7 @@ import java.lang.foreign.MemoryLayout; import java.lang.foreign.ValueLayout; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static java.lang.foreign.ValueLayout.JAVA_LONG; +import static java.lang.foreign.ValueLayout.*; /** * Similar to {@link java.lang.foreign.ValueLayout} however with some Swift specifics. diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java index afc47dca..826a4fa2 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -70,7 +70,8 @@ public static MemorySegment fullTypeMetadata(MemorySegment typeMetadata) { */ public static MemorySegment valueWitnessTable(MemorySegment typeMetadata) { return fullTypeMetadata(typeMetadata) - .get(SwiftValueLayout.SWIFT_POINTER, SwiftValueWitnessTable.fullTypeMetadata$vwt$offset); + .get(SwiftValueLayout.SWIFT_POINTER, SwiftValueWitnessTable.fullTypeMetadata$vwt$offset); +// .get(ValueLayout.ADDRESS, SwiftValueWitnessTable.fullTypeMetadata$vwt$offset); } @@ -209,9 +210,7 @@ static MethodHandle handle(SwiftAnyType ty) { */ public static void destroy(SwiftAnyType type, MemorySegment object) { var fullTypeMetadata = fullTypeMetadata(type.$memorySegment()); - System.out.println("fullTypeMetadata = " + fullTypeMetadata); var wtable = valueWitnessTable(fullTypeMetadata); - System.out.println("wtable = " + wtable); var mh = destroy.handle(type); diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java index 03c6c9f5..2c51143f 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java @@ -26,4 +26,13 @@ public static boolean isMacOS() { public static boolean isWindows() { return System.getProperty("os.name").toLowerCase().contains("windows"); } + + public static boolean isAarch64() { + return System.getProperty("os.arch").equals("aarm64"); + } + + public static boolean isAmd64() { + String arch = System.getProperty("os.arch"); + return arch.equals("amd64") || arch.equals("x86_64"); + } } diff --git a/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy b/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy index 3f50890d..e6a29ae9 100644 --- a/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy +++ b/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy @@ -21,20 +21,28 @@ final class BuildUtils { def osName = System.getProperty("os.name") def osArch = System.getProperty("os.arch") def isLinux = osName.toLowerCase(Locale.getDefault()).contains("linux") + def base = rootDir == null ? "" : "${rootDir}/" + System.out.println("Root dir is = ${rootDir}") return [ isLinux ? - /* Linux */(osArch == "amd64" || osArch == "amd64" ? - "${rootDir}/.build/x86_64-unknown-linux-gnu/debug/" : - "${rootDir}/.build/${osArch}-unknown-linux-gnu/debug/") : - /* macOS */(osArch == "aarch64" ? - "${rootDir}/.build/arm64-apple-macosx/debug/" : - "${rootDir}/.build/${osArch}-apple-macosx/debug/"), + /* Linux */ (osArch == "amd64" || osArch == "x86_64" ? + "${base}.build/x86_64-unknown-linux-gnu/debug/" : + "${base}.build/${osArch}-unknown-linux-gnu/debug/") : + /* macOS */ (osArch == "aarch64" ? + "${base}.build/arm64-apple-macosx/debug/" : + "${base}.build/${osArch}-apple-macosx/debug/"), + isLinux ? + /* Linux */ (osArch == "amd64" || osArch == "x86_64" ? + "${base}../../.build/x86_64-unknown-linux-gnu/debug/" : + "${base}../../.build/${osArch}-unknown-linux-gnu/debug/") : + /* macOS */ (osArch == "aarch64" ? + "${base}../../.build/arm64-apple-macosx/debug/" : + "${base}../../.build/${osArch}-apple-macosx/debug/"), isLinux ? "/usr/lib/swift/linux" : // assume macOS "/usr/lib/swift/" ] } - } From 74076aa4f46c6b17ef50f3dae7e67a8d4ed3a073 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 22 Oct 2024 16:09:38 +0900 Subject: [PATCH 17/18] license ignore generated java files --- .licenseignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.licenseignore b/.licenseignore index 5d0e3172..02866b01 100644 --- a/.licenseignore +++ b/.licenseignore @@ -27,12 +27,12 @@ docker/* **/docker-compose.yaml **/docker/* **/.dockerignore -JavaSwiftKitDemo/src/main/java/com/example/swift/generated/* Makefile **/Makefile **/*.html **/CMakeLists.txt **/*.jar +**/generated/*.java **/generated/*.swift gradle/wrapper/gradle-wrapper.properties gradlew From fa6a5b8032338a30528eafb8a936e28a576aca0f Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 22 Oct 2024 16:24:19 +0900 Subject: [PATCH 18/18] ignore generated jextract files, we dont commit them anymore --- .gitignore | 3 + .../swift/generated/ExampleSwiftLibrary.java | 293 ------- .../example/swift/generated/MySwiftClass.java | 765 ------------------ .../swiftkit/generated/SwiftKitSwift.java | 119 --- 4 files changed, 3 insertions(+), 1177 deletions(-) delete mode 100644 Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/ExampleSwiftLibrary.java delete mode 100644 Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/MySwiftClass.java delete mode 100644 Samples/SwiftKitSampleApp/src/generated/java/org/swift/swiftkit/generated/SwiftKitSwift.java diff --git a/.gitignore b/.gitignore index 618b65f8..43b51002 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ Package.resolved # Cache of project .gradletasknamecache + +# Ignore files generated by jextract, we always can re-generate them +*/**/*.java diff --git a/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/ExampleSwiftLibrary.java b/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/ExampleSwiftLibrary.java deleted file mode 100644 index 98904dd5..00000000 --- a/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/ExampleSwiftLibrary.java +++ /dev/null @@ -1,293 +0,0 @@ -// Generated by jextract-swift -// Swift module: ExampleSwiftLibrary - -package com.example.swift.generated; - -import org.swift.swiftkit.*; -import org.swift.swiftkit.SwiftKit; -import org.swift.swiftkit.util.*; -import java.lang.foreign.*; -import java.lang.invoke.*; -import java.util.Arrays; -import java.util.stream.Collectors; -import java.nio.charset.StandardCharsets; - - public final class ExampleSwiftLibrary { - private ExampleSwiftLibrary() { - // Should not be called directly - } - - static final String LIB_NAME = "ExampleSwiftLibrary"; - static final Arena LIBRARY_ARENA = Arena.ofAuto(); - - // TODO: rather than the C ones offer the Swift mappings - public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; - public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; - public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; - public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; - public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; - public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; - public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; - public static final AddressLayout C_POINTER = ValueLayout.ADDRESS - .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, ValueLayout.JAVA_BYTE)); - public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; - - - public static final ValueLayout.OfBoolean SWIFT_BOOL = ValueLayout.JAVA_BOOLEAN; - public static final ValueLayout.OfByte SWIFT_INT8 = ValueLayout.JAVA_BYTE; - public static final ValueLayout.OfChar SWIFT_UINT16 = ValueLayout.JAVA_CHAR; - public static final ValueLayout.OfShort SWIFT_INT16 = ValueLayout.JAVA_SHORT; - public static final ValueLayout.OfInt SWIFT_INT32 = ValueLayout.JAVA_INT; - public static final ValueLayout.OfLong SWIFT_INT64 = ValueLayout.JAVA_LONG; - public static final ValueLayout.OfFloat SWIFT_FLOAT = ValueLayout.JAVA_FLOAT; - public static final ValueLayout.OfDouble SWIFT_DOUBLE = ValueLayout.JAVA_DOUBLE; - public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS; - // On the platform this was generated on, Int was Int64 - public static final SequenceLayout SWIFT_BYTE_ARRAY = MemoryLayout.sequenceLayout(8, ValueLayout.JAVA_BYTE); - public static final ValueLayout.OfLong SWIFT_INT = SWIFT_INT64; - public static final ValueLayout.OfLong SWIFT_UINT = SWIFT_INT64; - public static final AddressLayout SWIFT_SELF = SWIFT_POINTER; - - static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); - static void traceDowncall(Object... args) { - var ex = new RuntimeException(); - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("[java][%s:%d] Downcall: %s(%s)\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - static void trace(Object... args) { - var ex = new RuntimeException(); - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("[java][%s:%d] %s: %s\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - - static MemorySegment findOrThrow(String symbol) { - return SYMBOL_LOOKUP.find(symbol) - .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); - } - - static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { - try { - return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); - } catch (ReflectiveOperationException ex) { - throw new AssertionError(ex); - } - } - - static MemoryLayout align(MemoryLayout layout, long align) { - return switch (layout) { - case PaddingLayout p -> p; - case ValueLayout v -> v.withByteAlignment(align); - case GroupLayout g -> { - MemoryLayout[] alignedMembers = g.memberLayouts().stream() - .map(m -> align(m, align)).toArray(MemoryLayout[]::new); - yield g instanceof StructLayout ? - MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); - } - case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); - }; - } - - static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); - private static SymbolLookup getSymbolLookup() { - if (PlatformUtils.isMacOS()) { - return SymbolLookup.libraryLookup(System.mapLibraryName(LIB_NAME), LIBRARY_ARENA) - .or(SymbolLookup.loaderLookup()) - .or(Linker.nativeLinker().defaultLookup()); - } else { - return SymbolLookup.loaderLookup() - .or(Linker.nativeLinker().defaultLookup()); - } - } - - static { - System.loadLibrary("swiftCore"); - System.loadLibrary(LIB_NAME); - } - - // ==== -------------------------------------------------- - // helloWorld - - private static class helloWorld { - public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - ); - public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary10helloWorldyyF"); - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 - /** - * Function descriptor for: - * {@snippet lang=swift : - * public func helloWorld() - * } - */ - public static FunctionDescriptor helloWorld$descriptor() { - return helloWorld.DESC; - } - - /** - * Downcall method handle for: - * {@snippet lang=swift : - * public func helloWorld() - * } - */ - public static MethodHandle helloWorld$handle() { - return helloWorld.HANDLE; - } - - /** - * Address for: - * {@snippet lang=swift : - * public func helloWorld() - * } - */ - public static MemorySegment helloWorld$address() { - return helloWorld.ADDR; - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func helloWorld() - * } - */ - public static void helloWorld() { - var mh$ = helloWorld.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(); - } - mh$.invokeExact(); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - // ==== -------------------------------------------------- - // globalTakeInt - - private static class globalTakeInt { - public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - SWIFT_INT - ); - public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary13globalTakeInt1iySi_tF"); - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 - /** - * Function descriptor for: - * {@snippet lang=swift : - * public func globalTakeInt(i: Swift.Int) - * } - */ - public static FunctionDescriptor globalTakeInt$descriptor() { - return globalTakeInt.DESC; - } - - /** - * Downcall method handle for: - * {@snippet lang=swift : - * public func globalTakeInt(i: Swift.Int) - * } - */ - public static MethodHandle globalTakeInt$handle() { - return globalTakeInt.HANDLE; - } - - /** - * Address for: - * {@snippet lang=swift : - * public func globalTakeInt(i: Swift.Int) - * } - */ - public static MemorySegment globalTakeInt$address() { - return globalTakeInt.ADDR; - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func globalTakeInt(i: Swift.Int) - * } - */ - public static void globalTakeInt(long i) { - var mh$ = globalTakeInt.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(i); - } - mh$.invokeExact(i); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - // ==== -------------------------------------------------- - // globalTakeIntInt - - private static class globalTakeIntInt { - public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - SWIFT_INT, - SWIFT_INT - ); - public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary013globalTakeIntF01i1jySi_SitF"); - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 - /** - * Function descriptor for: - * {@snippet lang=swift : - * public func globalTakeIntInt(i: Swift.Int, j: Swift.Int) - * } - */ - public static FunctionDescriptor globalTakeIntInt$descriptor() { - return globalTakeIntInt.DESC; - } - - /** - * Downcall method handle for: - * {@snippet lang=swift : - * public func globalTakeIntInt(i: Swift.Int, j: Swift.Int) - * } - */ - public static MethodHandle globalTakeIntInt$handle() { - return globalTakeIntInt.HANDLE; - } - - /** - * Address for: - * {@snippet lang=swift : - * public func globalTakeIntInt(i: Swift.Int, j: Swift.Int) - * } - */ - public static MemorySegment globalTakeIntInt$address() { - return globalTakeIntInt.ADDR; - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func globalTakeIntInt(i: Swift.Int, j: Swift.Int) - * } - */ - public static void globalTakeIntInt(long i, long j) { - var mh$ = globalTakeIntInt.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(i, j); - } - mh$.invokeExact(i, j); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - } // printModuleClass(_:body:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:201 diff --git a/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/MySwiftClass.java b/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/MySwiftClass.java deleted file mode 100644 index 80f10d4c..00000000 --- a/Samples/SwiftKitSampleApp/src/generated/java/com/example/swift/generated/MySwiftClass.java +++ /dev/null @@ -1,765 +0,0 @@ -// Generated by jextract-swift -// Swift module: ExampleSwiftLibrary - -package com.example.swift.generated; - -import org.swift.swiftkit.*; -import org.swift.swiftkit.SwiftKit; -import org.swift.swiftkit.util.*; -import java.lang.foreign.*; -import java.lang.invoke.*; -import java.util.Arrays; -import java.util.stream.Collectors; -import java.nio.charset.StandardCharsets; - - public final class MySwiftClass implements SwiftHeapObject { - // Pointer to the referred to class instance's "self". - private final MemorySegment selfMemorySegment; - public final MemorySegment $memorySegment() { - return this.selfMemorySegment; - } - - static final String LIB_NAME = "ExampleSwiftLibrary"; - static final Arena LIBRARY_ARENA = Arena.ofAuto(); - - // TODO: rather than the C ones offer the Swift mappings - public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; - public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; - public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; - public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; - public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; - public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; - public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; - public static final AddressLayout C_POINTER = ValueLayout.ADDRESS - .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, ValueLayout.JAVA_BYTE)); - public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; - - - public static final ValueLayout.OfBoolean SWIFT_BOOL = ValueLayout.JAVA_BOOLEAN; - public static final ValueLayout.OfByte SWIFT_INT8 = ValueLayout.JAVA_BYTE; - public static final ValueLayout.OfChar SWIFT_UINT16 = ValueLayout.JAVA_CHAR; - public static final ValueLayout.OfShort SWIFT_INT16 = ValueLayout.JAVA_SHORT; - public static final ValueLayout.OfInt SWIFT_INT32 = ValueLayout.JAVA_INT; - public static final ValueLayout.OfLong SWIFT_INT64 = ValueLayout.JAVA_LONG; - public static final ValueLayout.OfFloat SWIFT_FLOAT = ValueLayout.JAVA_FLOAT; - public static final ValueLayout.OfDouble SWIFT_DOUBLE = ValueLayout.JAVA_DOUBLE; - public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS; - // On the platform this was generated on, Int was Int64 - public static final SequenceLayout SWIFT_BYTE_ARRAY = MemoryLayout.sequenceLayout(8, ValueLayout.JAVA_BYTE); - public static final ValueLayout.OfLong SWIFT_INT = SWIFT_INT64; - public static final ValueLayout.OfLong SWIFT_UINT = SWIFT_INT64; - public static final AddressLayout SWIFT_SELF = SWIFT_POINTER; - - private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( - SWIFT_POINTER - ).withName("$s19ExampleSwiftLibrary02MyB5ClassCMa"); - public final GroupLayout $layout() { - return $LAYOUT; - } - - static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); - static void traceDowncall(Object... args) { - var ex = new RuntimeException(); - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("[java][%s:%d] Downcall: %s(%s)\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - static void trace(Object... args) { - var ex = new RuntimeException(); - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("[java][%s:%d] %s: %s\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - - static { - System.loadLibrary("swiftCore"); - System.loadLibrary(LIB_NAME); - } - - public static final String TYPE_MANGLED_NAME = "$s19ExampleSwiftLibrary02MyB5ClassCMa"; - public static final SwiftAnyType TYPE_METADATA = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_MANGLED_NAME).get(); - public final SwiftAnyType $swiftType() { - return TYPE_METADATA; - } - - - // ==== -------------------------------------------------- - // init(len:cap:) - - private static class init_len_cap { - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /* -> */SWIFT_POINTER, - SWIFT_INT, - SWIFT_INT, - SWIFT_POINTER - ); - public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary02MyB5ClassC3len3capACSi_SitcfC"); - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } // printClassConstructors(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:400 - /** - * Create an instance of {@code MySwiftClass}. - * - * {@snippet lang=swift : - * public init(len: Swift.Int, cap: Swift.Int) - * } - */ - public MySwiftClass(long len, long cap) { - this(/*arena=*/null, len, cap); - } - - /** - * Create an instance of {@code MySwiftClass}. - * This instance is managed by the passed in {@link SwiftArena} and may not outlive the arena's lifetime. - * - * {@snippet lang=swift : - * public init(len: Swift.Int, cap: Swift.Int) - * } - */ - public MySwiftClass(SwiftArena arena, long len, long cap) { - var mh$ = init_len_cap.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(len, cap); - } - this.selfMemorySegment = (MemorySegment) mh$.invokeExact(len, cap, TYPE_METADATA.$memorySegment()); - if (arena != null) { - arena.register(this); - } - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - // ==== -------------------------------------------------- - // len - - private static class len { - public static final FunctionDescriptor DESC_GET = FunctionDescriptor.of( - /* -> */SWIFT_INT, - SWIFT_POINTER - ); - public static final MemorySegment ADDR_GET = ExampleSwiftLibrary.findOrThrow("g"); - public static final MethodHandle HANDLE_GET = Linker.nativeLinker().downcallHandle(ADDR_GET, DESC_GET); - public static final FunctionDescriptor DESC_SET = FunctionDescriptor.ofVoid( - SWIFT_INT, - SWIFT_POINTER - ); - public static final MemorySegment ADDR_SET = ExampleSwiftLibrary.findOrThrow("s"); - public static final MethodHandle HANDLE_SET = Linker.nativeLinker().downcallHandle(ADDR_SET, DESC_SET); - } // printVariableDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:553 - /** - * Function descriptor for: - * - */ - public static FunctionDescriptor len$get$descriptor() { - return len.DESC_GET; - } - - /** - * Downcall method handle for: - * - */ - public static MethodHandle len$get$handle() { - return len.HANDLE_GET; - } - - /** - * Address for: - * - */ - public static MemorySegment len$get$address() { - return len.ADDR_GET; - } - - /** - * Function descriptor for: - * - */ - public static FunctionDescriptor len$set$descriptor() { - return len.DESC_SET; - } - - /** - * Downcall method handle for: - * - */ - public static MethodHandle len$set$handle() { - return len.HANDLE_SET; - } - - /** - * Address for: - * - */ - public static MemorySegment len$set$address() { - return len.ADDR_SET; - } - - /** - * Downcall to Swift: - * - */ - public static long getLen(java.lang.foreign.MemorySegment self$) { - var mh$ = len.HANDLE_GET; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(self$); - } - return (long) mh$.invokeExact(self$); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * Downcall to Swift: - * - */ - public long getLen() { - return (long) getLen($memorySegment()); - } - - /** - * Downcall to Swift: - * - */ - public static void setLen(long newValue, java.lang.foreign.MemorySegment self$) { - var mh$ = len.HANDLE_SET; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(newValue, self$); - } - mh$.invokeExact(newValue, self$); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * Downcall to Swift: - * - */ - public void setLen(long newValue) { - setLen(newValue, $memorySegment()); - } - - // ==== -------------------------------------------------- - // cap - - private static class cap { - public static final FunctionDescriptor DESC_GET = FunctionDescriptor.of( - /* -> */SWIFT_INT, - SWIFT_POINTER - ); - public static final MemorySegment ADDR_GET = ExampleSwiftLibrary.findOrThrow("g"); - public static final MethodHandle HANDLE_GET = Linker.nativeLinker().downcallHandle(ADDR_GET, DESC_GET); - public static final FunctionDescriptor DESC_SET = FunctionDescriptor.ofVoid( - SWIFT_INT, - SWIFT_POINTER - ); - public static final MemorySegment ADDR_SET = ExampleSwiftLibrary.findOrThrow("s"); - public static final MethodHandle HANDLE_SET = Linker.nativeLinker().downcallHandle(ADDR_SET, DESC_SET); - } // printVariableDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:553 - /** - * Function descriptor for: - * - */ - public static FunctionDescriptor cap$get$descriptor() { - return cap.DESC_GET; - } - - /** - * Downcall method handle for: - * - */ - public static MethodHandle cap$get$handle() { - return cap.HANDLE_GET; - } - - /** - * Address for: - * - */ - public static MemorySegment cap$get$address() { - return cap.ADDR_GET; - } - - /** - * Function descriptor for: - * - */ - public static FunctionDescriptor cap$set$descriptor() { - return cap.DESC_SET; - } - - /** - * Downcall method handle for: - * - */ - public static MethodHandle cap$set$handle() { - return cap.HANDLE_SET; - } - - /** - * Address for: - * - */ - public static MemorySegment cap$set$address() { - return cap.ADDR_SET; - } - - /** - * Downcall to Swift: - * - */ - public static long getCap(java.lang.foreign.MemorySegment self$) { - var mh$ = cap.HANDLE_GET; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(self$); - } - return (long) mh$.invokeExact(self$); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * Downcall to Swift: - * - */ - public long getCap() { - return (long) getCap($memorySegment()); - } - - /** - * Downcall to Swift: - * - */ - public static void setCap(long newValue, java.lang.foreign.MemorySegment self$) { - var mh$ = cap.HANDLE_SET; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(newValue, self$); - } - mh$.invokeExact(newValue, self$); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * Downcall to Swift: - * - */ - public void setCap(long newValue) { - setCap(newValue, $memorySegment()); - } - - // ==== -------------------------------------------------- - // counter - - private static class counter { - public static final FunctionDescriptor DESC_GET = FunctionDescriptor.of( - /* -> */SWIFT_INT32, - SWIFT_POINTER - ); - public static final MemorySegment ADDR_GET = ExampleSwiftLibrary.findOrThrow("g"); - public static final MethodHandle HANDLE_GET = Linker.nativeLinker().downcallHandle(ADDR_GET, DESC_GET); - public static final FunctionDescriptor DESC_SET = FunctionDescriptor.ofVoid( - SWIFT_INT32, - SWIFT_POINTER - ); - public static final MemorySegment ADDR_SET = ExampleSwiftLibrary.findOrThrow("s"); - public static final MethodHandle HANDLE_SET = Linker.nativeLinker().downcallHandle(ADDR_SET, DESC_SET); - } // printVariableDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:553 - /** - * Function descriptor for: - * - */ - public static FunctionDescriptor counter$get$descriptor() { - return counter.DESC_GET; - } - - /** - * Downcall method handle for: - * - */ - public static MethodHandle counter$get$handle() { - return counter.HANDLE_GET; - } - - /** - * Address for: - * - */ - public static MemorySegment counter$get$address() { - return counter.ADDR_GET; - } - - /** - * Function descriptor for: - * - */ - public static FunctionDescriptor counter$set$descriptor() { - return counter.DESC_SET; - } - - /** - * Downcall method handle for: - * - */ - public static MethodHandle counter$set$handle() { - return counter.HANDLE_SET; - } - - /** - * Address for: - * - */ - public static MemorySegment counter$set$address() { - return counter.ADDR_SET; - } - - /** - * Downcall to Swift: - * - */ - public static int getCounter(java.lang.foreign.MemorySegment self$) { - var mh$ = counter.HANDLE_GET; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(self$); - } - return (int) mh$.invokeExact(self$); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * Downcall to Swift: - * - */ - public int getCounter() { - return (int) getCounter($memorySegment()); - } - - /** - * Downcall to Swift: - * - */ - public static void setCounter(int newValue, java.lang.foreign.MemorySegment self$) { - var mh$ = counter.HANDLE_SET; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(newValue, self$); - } - mh$.invokeExact(newValue, self$); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * Downcall to Swift: - * - */ - public void setCounter(int newValue) { - setCounter(newValue, $memorySegment()); - } - - // ==== -------------------------------------------------- - // voidMethod - - private static class voidMethod { - public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - SWIFT_POINTER - ); - public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary02MyB5ClassC10voidMethodyyF"); - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 - /** - * Function descriptor for: - * {@snippet lang=swift : - * public func voidMethod() - * } - */ - public static FunctionDescriptor voidMethod$descriptor() { - return voidMethod.DESC; - } - - /** - * Downcall method handle for: - * {@snippet lang=swift : - * public func voidMethod() - * } - */ - public static MethodHandle voidMethod$handle() { - return voidMethod.HANDLE; - } - - /** - * Address for: - * {@snippet lang=swift : - * public func voidMethod() - * } - */ - public static MemorySegment voidMethod$address() { - return voidMethod.ADDR; - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func voidMethod() - * } - */ - public static void voidMethod(java.lang.foreign.MemorySegment self$) { - var mh$ = voidMethod.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(self$); - } - mh$.invokeExact(self$); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func voidMethod() - * } - */ - public void voidMethod() { - voidMethod($memorySegment()); - } - - // ==== -------------------------------------------------- - // takeIntMethod - - private static class takeIntMethod { - public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - SWIFT_INT, - SWIFT_POINTER - ); - public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary02MyB5ClassC13takeIntMethod1iySi_tF"); - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 - /** - * Function descriptor for: - * {@snippet lang=swift : - * public func takeIntMethod(i: Swift.Int) - * } - */ - public static FunctionDescriptor takeIntMethod$descriptor() { - return takeIntMethod.DESC; - } - - /** - * Downcall method handle for: - * {@snippet lang=swift : - * public func takeIntMethod(i: Swift.Int) - * } - */ - public static MethodHandle takeIntMethod$handle() { - return takeIntMethod.HANDLE; - } - - /** - * Address for: - * {@snippet lang=swift : - * public func takeIntMethod(i: Swift.Int) - * } - */ - public static MemorySegment takeIntMethod$address() { - return takeIntMethod.ADDR; - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func takeIntMethod(i: Swift.Int) - * } - */ - public static void takeIntMethod(long i, java.lang.foreign.MemorySegment self$) { - var mh$ = takeIntMethod.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(i, self$); - } - mh$.invokeExact(i, self$); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func takeIntMethod(i: Swift.Int) - * } - */ - public void takeIntMethod(long i) { - takeIntMethod(i, $memorySegment()); - } - - // ==== -------------------------------------------------- - // echoIntMethod - - private static class echoIntMethod { - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /* -> */SWIFT_INT, - SWIFT_INT, - SWIFT_POINTER - ); - public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary02MyB5ClassC13echoIntMethod1iS2i_tF"); - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 - /** - * Function descriptor for: - * {@snippet lang=swift : - * public func echoIntMethod(i: Swift.Int) -> Swift.Int - * } - */ - public static FunctionDescriptor echoIntMethod$descriptor() { - return echoIntMethod.DESC; - } - - /** - * Downcall method handle for: - * {@snippet lang=swift : - * public func echoIntMethod(i: Swift.Int) -> Swift.Int - * } - */ - public static MethodHandle echoIntMethod$handle() { - return echoIntMethod.HANDLE; - } - - /** - * Address for: - * {@snippet lang=swift : - * public func echoIntMethod(i: Swift.Int) -> Swift.Int - * } - */ - public static MemorySegment echoIntMethod$address() { - return echoIntMethod.ADDR; - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func echoIntMethod(i: Swift.Int) -> Swift.Int - * } - */ - public static long echoIntMethod(long i, java.lang.foreign.MemorySegment self$) { - var mh$ = echoIntMethod.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(i, self$); - } - return (long) mh$.invokeExact(i, self$); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func echoIntMethod(i: Swift.Int) -> Swift.Int - * } - */ - public long echoIntMethod(long i) { - return (long) echoIntMethod(i, $memorySegment()); - } - - // ==== -------------------------------------------------- - // makeIntMethod - - private static class makeIntMethod { - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /* -> */SWIFT_INT, - SWIFT_POINTER - ); - public static final MemorySegment ADDR = ExampleSwiftLibrary.findOrThrow("$s19ExampleSwiftLibrary02MyB5ClassC13makeIntMethodSiyF"); - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } // printFunctionDowncallMethods(_:_:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:470 - /** - * Function descriptor for: - * {@snippet lang=swift : - * public func makeIntMethod() -> Swift.Int - * } - */ - public static FunctionDescriptor makeIntMethod$descriptor() { - return makeIntMethod.DESC; - } - - /** - * Downcall method handle for: - * {@snippet lang=swift : - * public func makeIntMethod() -> Swift.Int - * } - */ - public static MethodHandle makeIntMethod$handle() { - return makeIntMethod.HANDLE; - } - - /** - * Address for: - * {@snippet lang=swift : - * public func makeIntMethod() -> Swift.Int - * } - */ - public static MemorySegment makeIntMethod$address() { - return makeIntMethod.ADDR; - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func makeIntMethod() -> Swift.Int - * } - */ - public static long makeIntMethod(java.lang.foreign.MemorySegment self$) { - var mh$ = makeIntMethod.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(self$); - } - return (long) mh$.invokeExact(self$); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * Downcall to Swift: - * {@snippet lang=swift : - * public func makeIntMethod() -> Swift.Int - * } - */ - public long makeIntMethod() { - return (long) makeIntMethod($memorySegment()); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "(" + - SwiftKit.nameOfSwiftType($swiftType().$memorySegment(), true) + - ")@" + $memorySegment(); - } - - } // printClass(_:_:body:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:182 diff --git a/Samples/SwiftKitSampleApp/src/generated/java/org/swift/swiftkit/generated/SwiftKitSwift.java b/Samples/SwiftKitSampleApp/src/generated/java/org/swift/swiftkit/generated/SwiftKitSwift.java deleted file mode 100644 index fb1672a8..00000000 --- a/Samples/SwiftKitSampleApp/src/generated/java/org/swift/swiftkit/generated/SwiftKitSwift.java +++ /dev/null @@ -1,119 +0,0 @@ -// Generated by jextract-swift -// Swift module: SwiftKitSwift - -package org.swift.swiftkit.generated; - -import org.swift.swiftkit.*; -import org.swift.swiftkit.SwiftKit; -import org.swift.swiftkit.util.*; -import java.lang.foreign.*; -import java.lang.invoke.*; -import java.util.Arrays; -import java.util.stream.Collectors; -import java.nio.charset.StandardCharsets; - - public final class SwiftKitSwift { - private SwiftKitSwift() { - // Should not be called directly - } - - static final String LIB_NAME = "SwiftKitSwift"; - static final Arena LIBRARY_ARENA = Arena.ofAuto(); - - // TODO: rather than the C ones offer the Swift mappings - public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; - public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; - public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; - public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; - public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; - public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; - public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; - public static final AddressLayout C_POINTER = ValueLayout.ADDRESS - .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, ValueLayout.JAVA_BYTE)); - public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; - - - public static final ValueLayout.OfBoolean SWIFT_BOOL = ValueLayout.JAVA_BOOLEAN; - public static final ValueLayout.OfByte SWIFT_INT8 = ValueLayout.JAVA_BYTE; - public static final ValueLayout.OfChar SWIFT_UINT16 = ValueLayout.JAVA_CHAR; - public static final ValueLayout.OfShort SWIFT_INT16 = ValueLayout.JAVA_SHORT; - public static final ValueLayout.OfInt SWIFT_INT32 = ValueLayout.JAVA_INT; - public static final ValueLayout.OfLong SWIFT_INT64 = ValueLayout.JAVA_LONG; - public static final ValueLayout.OfFloat SWIFT_FLOAT = ValueLayout.JAVA_FLOAT; - public static final ValueLayout.OfDouble SWIFT_DOUBLE = ValueLayout.JAVA_DOUBLE; - public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS; - // On the platform this was generated on, Int was Int64 - public static final SequenceLayout SWIFT_BYTE_ARRAY = MemoryLayout.sequenceLayout(8, ValueLayout.JAVA_BYTE); - public static final ValueLayout.OfLong SWIFT_INT = SWIFT_INT64; - public static final ValueLayout.OfLong SWIFT_UINT = SWIFT_INT64; - public static final AddressLayout SWIFT_SELF = SWIFT_POINTER; - - static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); - static void traceDowncall(Object... args) { - var ex = new RuntimeException(); - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("[java][%s:%d] Downcall: %s(%s)\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - static void trace(Object... args) { - var ex = new RuntimeException(); - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("[java][%s:%d] %s: %s\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - - static MemorySegment findOrThrow(String symbol) { - return SYMBOL_LOOKUP.find(symbol) - .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); - } - - static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { - try { - return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); - } catch (ReflectiveOperationException ex) { - throw new AssertionError(ex); - } - } - - static MemoryLayout align(MemoryLayout layout, long align) { - return switch (layout) { - case PaddingLayout p -> p; - case ValueLayout v -> v.withByteAlignment(align); - case GroupLayout g -> { - MemoryLayout[] alignedMembers = g.memberLayouts().stream() - .map(m -> align(m, align)).toArray(MemoryLayout[]::new); - yield g instanceof StructLayout ? - MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); - } - case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); - }; - } - - static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); - private static SymbolLookup getSymbolLookup() { - if (PlatformUtils.isMacOS()) { - return SymbolLookup.libraryLookup(System.mapLibraryName(LIB_NAME), LIBRARY_ARENA) - .or(SymbolLookup.loaderLookup()) - .or(Linker.nativeLinker().defaultLookup()); - } else { - return SymbolLookup.loaderLookup() - .or(Linker.nativeLinker().defaultLookup()); - } - } - - static { - System.loadLibrary("swiftCore"); - System.loadLibrary(LIB_NAME); - } - - } // printModuleClass(_:body:) @ JExtractSwift/Swift2JavaTranslator+Printing.swift:201