From b393fcf3ae78521687fcd3f87dc5cca4db99e3f6 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Thu, 3 Jul 2025 14:24:10 +0200 Subject: [PATCH 1/3] separate swiftkit into two modules --- ...d-logic.java-common-conventions.gradle.kts | 6 +- Samples/JExtractJNISampleApp/build.gradle | 2 +- .../com/example/swift/HelloJava2SwiftJNI.java | 2 +- Samples/SwiftAndJavaJarSampleLib/build.gradle | 2 +- .../swift/swiftkit/JavaToSwiftBenchmark.java | 2 +- .../com/example/swift/HelloJava2Swift.java | 10 +- .../com/example/swift/MySwiftClassTest.java | 4 +- .../com/example/swift/MySwiftLibraryTest.java | 4 - .../MySwiftClassTest.java | 2 +- .../SwiftArenaTest.java | 12 +- Samples/SwiftKitSampleApp/build.gradle | 2 +- .../JavaToSwiftBenchmark.java | 2 +- .../StringPassingBenchmark.java | 4 +- .../com/example/swift/HelloJava2Swift.java | 4 +- .../com/example/swift/MySwiftClassTest.java | 4 +- .../com/example/swift/MySwiftLibraryTest.java | 5 - .../MySwiftClassTest.java | 2 +- .../MySwiftStructTest.java | 2 +- .../SwiftArenaTest.java | 12 +- SwiftKitCore/build.gradle | 73 +++ .../swiftkitcore}/AutoSwiftMemorySession.java | 20 +- .../swiftkitcore}/ClosableSwiftArena.java | 4 +- .../ConfinedSwiftMemorySession.java | 4 +- .../swift/swiftkitcore}/ManagedSwiftType.java | 4 +- .../swiftkitcore}/MemorySegmentUtils.java | 4 +- .../org/swift/swiftkitcore}/SwiftAnyType.java | 4 +- .../org/swift/swiftkitcore}/SwiftArena.java | 2 +- .../swift/swiftkitcore}/SwiftHeapObject.java | 4 +- .../swift/swiftkitcore}/SwiftInstance.java | 7 +- .../swiftkitcore}/SwiftInstanceCleanup.java | 4 +- .../org/swift/swiftkitcore}/SwiftKit.java | 11 +- .../org/swift/swiftkitcore}/SwiftValue.java | 2 +- .../swift/swiftkitcore}/SwiftValueLayout.java | 2 +- .../swiftkitcore}/SwiftValueWitnessTable.java | 4 +- .../swiftkitcore}/util/PlatformUtils.java | 2 +- .../swift/swiftkitcore}/util/StringUtils.java | 2 +- {SwiftKit => SwiftKitFFM}/build.gradle | 4 +- .../swiftkitffm/AutoSwiftMemorySession.java | 85 +++ .../swift/swiftkitffm/ClosableSwiftArena.java | 30 ++ .../ConfinedSwiftMemorySession.java | 100 ++++ .../swift/swiftkitffm/ManagedSwiftType.java | 27 + .../swift/swiftkitffm/MemorySegmentUtils.java | 39 ++ .../org/swift/swiftkitffm/SwiftAnyType.java | 62 +++ .../org/swift/swiftkitffm/SwiftArena.java | 62 +++ .../swift/swiftkitffm/SwiftHeapObject.java | 33 ++ .../org/swift/swiftkitffm/SwiftInstance.java | 94 ++++ .../swiftkitffm/SwiftInstanceCleanup.java | 38 ++ .../java/org/swift/swiftkitffm/SwiftKit.java | 500 ++++++++++++++++++ .../org/swift/swiftkitffm/SwiftValue.java | 21 + .../swift/swiftkitffm/SwiftValueLayout.java | 64 +++ .../swiftkitffm/SwiftValueWitnessTable.java | 290 ++++++++++ .../swift/swiftkitffm/util/PlatformUtils.java | 53 ++ .../swift/swiftkitffm/util/StringUtils.java | 36 ++ .../org/swift/swiftkitffm}/AutoArenaTest.java | 4 +- .../SwiftRuntimeMetadataTest.java | 6 +- settings.gradle | 3 +- 56 files changed, 1702 insertions(+), 85 deletions(-) rename Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/{swiftkit => swiftkitffm}/MySwiftClassTest.java (97%) rename Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/{swiftkit => swiftkitffm}/SwiftArenaTest.java (83%) rename Samples/SwiftKitSampleApp/src/jmh/java/org/swift/{swiftkit => swiftkitffm}/JavaToSwiftBenchmark.java (98%) rename Samples/SwiftKitSampleApp/src/jmh/java/org/swift/{swiftkit => swiftkitffm}/StringPassingBenchmark.java (96%) rename Samples/SwiftKitSampleApp/src/test/java/org/swift/{swiftkit => swiftkitffm}/MySwiftClassTest.java (97%) rename Samples/SwiftKitSampleApp/src/test/java/org/swift/{swiftkit => swiftkitffm}/MySwiftStructTest.java (96%) rename Samples/SwiftKitSampleApp/src/test/java/org/swift/{swiftkit => swiftkitffm}/SwiftArenaTest.java (91%) create mode 100644 SwiftKitCore/build.gradle rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/AutoSwiftMemorySession.java (88%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/ClosableSwiftArena.java (94%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/ConfinedSwiftMemorySession.java (96%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/ManagedSwiftType.java (94%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/MemorySegmentUtils.java (96%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/SwiftAnyType.java (95%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/SwiftArena.java (98%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/SwiftHeapObject.java (92%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/SwiftInstance.java (95%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/SwiftInstanceCleanup.java (97%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/SwiftKit.java (98%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/SwiftValue.java (95%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/SwiftValueLayout.java (98%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/SwiftValueWitnessTable.java (99%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/util/PlatformUtils.java (97%) rename {SwiftKit/src/main/java/org/swift/swiftkit => SwiftKitCore/src/main/java/org/swift/swiftkitcore}/util/StringUtils.java (96%) rename {SwiftKit => SwiftKitFFM}/build.gradle (95%) create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableSwiftArena.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ConfinedSwiftMemorySession.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ManagedSwiftType.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftArena.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftHeapObject.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstance.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstanceCleanup.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftKit.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValue.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueLayout.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueWitnessTable.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/PlatformUtils.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/StringUtils.java rename {SwiftKit/src/test/java/org/swift/swiftkit => SwiftKitFFM/src/test/java/org/swift/swiftkitffm}/AutoArenaTest.java (93%) rename {SwiftKit/src/test/java/org/swift/swiftkit => SwiftKitFFM/src/test/java/org/swift/swiftkitffm}/SwiftRuntimeMetadataTest.java (95%) diff --git a/BuildLogic/src/main/kotlin/build-logic.java-common-conventions.gradle.kts b/BuildLogic/src/main/kotlin/build-logic.java-common-conventions.gradle.kts index fb10bee0..7d03e68c 100644 --- a/BuildLogic/src/main/kotlin/build-logic.java-common-conventions.gradle.kts +++ b/BuildLogic/src/main/kotlin/build-logic.java-common-conventions.gradle.kts @@ -33,9 +33,9 @@ repositories { testing { suites { - val test by getting(JvmTestSuite::class) { - useJUnitJupiter("5.10.3") - } +// val test by getting(JvmTestSuite::class) { +// useJUnitJupiter("5.10.3") +// } } } diff --git a/Samples/JExtractJNISampleApp/build.gradle b/Samples/JExtractJNISampleApp/build.gradle index 03794ff7..54bd725a 100644 --- a/Samples/JExtractJNISampleApp/build.gradle +++ b/Samples/JExtractJNISampleApp/build.gradle @@ -148,7 +148,7 @@ tasks.clean { } dependencies { - implementation(project(':SwiftKit')) + implementation(project(':SwiftKitCore')) testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2SwiftJNI.java b/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2SwiftJNI.java index 7d0d77e1..15cb42b0 100644 --- a/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2SwiftJNI.java +++ b/Samples/JExtractJNISampleApp/src/main/java/com/example/swift/HelloJava2SwiftJNI.java @@ -18,7 +18,7 @@ // Import javakit/swiftkit support libraries -import org.swift.swiftkit.SwiftKit; +import org.swift.swiftkitffm.SwiftKit; public class HelloJava2SwiftJNI { diff --git a/Samples/SwiftAndJavaJarSampleLib/build.gradle b/Samples/SwiftAndJavaJarSampleLib/build.gradle index cc4c79d4..4528a27f 100644 --- a/Samples/SwiftAndJavaJarSampleLib/build.gradle +++ b/Samples/SwiftAndJavaJarSampleLib/build.gradle @@ -43,7 +43,7 @@ java { } dependencies { - implementation(project(':SwiftKit')) + implementation(project(':SwiftKitFFM')) testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/Samples/SwiftAndJavaJarSampleLib/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java b/Samples/SwiftAndJavaJarSampleLib/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java index 4e3edf03..73de6d59 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.util.concurrent.TimeUnit; diff --git a/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java index cd8af700..edfdcde3 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java @@ -16,15 +16,9 @@ // Import swift-extract generated sources -import com.example.swift.MySwiftLibrary; -import com.example.swift.MySwiftClass; - // Import javakit/swiftkit support libraries -import org.swift.swiftkit.SwiftArena; -import org.swift.swiftkit.SwiftKit; -import org.swift.swiftkit.SwiftValueWitnessTable; - -import java.util.Arrays; +import org.swift.swiftkitffm.SwiftArena; +import org.swift.swiftkitffm.SwiftKit; public class HelloJava2Swift { diff --git a/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java index bb46ef3d..e9425ec7 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java @@ -16,8 +16,8 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.swift.swiftkit.SwiftArena; -import org.swift.swiftkit.SwiftKit; +import org.swift.swiftkitffm.SwiftArena; +import org.swift.swiftkitffm.SwiftKit; import java.io.File; import java.util.stream.Stream; diff --git a/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftLibraryTest.java b/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftLibraryTest.java index 74df5da8..13ebb1b0 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftLibraryTest.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftLibraryTest.java @@ -14,14 +14,10 @@ package com.example.swift; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.swift.swiftkit.SwiftKit; -import java.util.Arrays; import java.util.concurrent.CountDownLatch; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; diff --git a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/MySwiftClassTest.java b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java similarity index 97% rename from Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/MySwiftClassTest.java rename to Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java index 78da5a64..169d750c 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/MySwiftClassTest.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/SwiftArenaTest.java b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java similarity index 83% rename from Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/SwiftArenaTest.java rename to Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java index e8b1ac04..97b9a99b 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/SwiftArenaTest.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java @@ -12,20 +12,16 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import com.example.swift.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 java.util.Arrays; -import java.util.stream.Collectors; +import org.swift.swiftkitffm.util.PlatformUtils; import static org.junit.jupiter.api.Assertions.*; -import static org.swift.swiftkit.SwiftKit.*; -import static org.swift.swiftkit.SwiftKit.retainCount; +import static org.swift.swiftkitffm.SwiftKit.*; +import static org.swift.swiftkitffm.SwiftKit.retainCount; public class SwiftArenaTest { diff --git a/Samples/SwiftKitSampleApp/build.gradle b/Samples/SwiftKitSampleApp/build.gradle index 647eaba9..75fa703c 100644 --- a/Samples/SwiftKitSampleApp/build.gradle +++ b/Samples/SwiftKitSampleApp/build.gradle @@ -148,7 +148,7 @@ tasks.clean { } dependencies { - implementation(project(':SwiftKit')) + implementation(project(':SwiftKitFFM')) testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java b/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkitffm/JavaToSwiftBenchmark.java similarity index 98% rename from Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java rename to Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkitffm/JavaToSwiftBenchmark.java index ff313fc6..01caa42a 100644 --- a/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java +++ b/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkitffm/JavaToSwiftBenchmark.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import com.example.swift.HelloJava2Swift; import com.example.swift.MySwiftLibrary; diff --git a/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/StringPassingBenchmark.java b/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkitffm/StringPassingBenchmark.java similarity index 96% rename from Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/StringPassingBenchmark.java rename to Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkitffm/StringPassingBenchmark.java index 0e686fb4..db6fc669 100644 --- a/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/StringPassingBenchmark.java +++ b/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkitffm/StringPassingBenchmark.java @@ -12,15 +12,13 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import com.example.swift.HelloJava2Swift; import com.example.swift.MySwiftClass; import com.example.swift.MySwiftLibrary; import org.openjdk.jmh.annotations.*; -import java.lang.foreign.Arena; -import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; @BenchmarkMode(Mode.AverageTime) 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 c12d82dd..271368ad 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -18,8 +18,8 @@ // Import javakit/swiftkit support libraries -import org.swift.swiftkit.SwiftArena; -import org.swift.swiftkit.SwiftKit; +import org.swift.swiftkitffm.SwiftArena; +import org.swift.swiftkitffm.SwiftKit; public class HelloJava2Swift { diff --git a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java index 6c0ceb1e..c272c35a 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java @@ -16,8 +16,8 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.swift.swiftkit.SwiftArena; -import org.swift.swiftkit.SwiftKit; +import org.swift.swiftkitffm.SwiftArena; +import org.swift.swiftkitffm.SwiftKit; import java.io.File; import java.util.stream.Stream; diff --git a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java index 2df53843..a6c34b57 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java @@ -14,15 +14,10 @@ package com.example.swift; -import com.example.swift.MySwiftLibrary; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.swift.swiftkit.SwiftKit; -import java.util.Arrays; import java.util.concurrent.CountDownLatch; -import java.util.stream.Collectors; import static org.junit.jupiter.api.Assertions.*; diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java similarity index 97% rename from Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java rename to Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java index 78da5a64..169d750c 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftStructTest.java similarity index 96% rename from Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java rename to Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftStructTest.java index 843551a5..a6e23941 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftStructTest.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import com.example.swift.MySwiftStruct; import org.junit.jupiter.api.Test; diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java similarity index 91% rename from Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java rename to Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java index 52d791a1..0b53eb9c 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java @@ -12,21 +12,17 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import com.example.swift.MySwiftClass; import com.example.swift.MySwiftStruct; -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 java.util.Arrays; -import java.util.stream.Collectors; +import org.swift.swiftkitffm.util.PlatformUtils; import static org.junit.jupiter.api.Assertions.*; -import static org.swift.swiftkit.SwiftKit.*; -import static org.swift.swiftkit.SwiftKit.retainCount; +import static org.swift.swiftkitffm.SwiftKit.*; +import static org.swift.swiftkitffm.SwiftKit.retainCount; public class SwiftArenaTest { diff --git a/SwiftKitCore/build.gradle b/SwiftKitCore/build.gradle new file mode 100644 index 00000000..bf533dbd --- /dev/null +++ b/SwiftKitCore/build.gradle @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +plugins { + id("build-logic.java-application-conventions") +} + +group = "org.swift.swiftkitcore" +version = "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(24)) + } + // Support Android 6+ (Java 7) + sourceCompatibility = JavaVersion.VERSION_1_7 +} + +dependencies { +// testImplementation(platform("org.junit:junit-bom:5.10.0")) +// testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.test { + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + } +} + +// SwiftKit depends on SwiftKitSwift (Swift library that this Java library calls into) + +def compileSwift = tasks.register("compileSwift", Exec) { + description "Compile the swift-java SwiftKitSwift dynamic library that SwiftKit (Java) calls into" + + inputs.file(new File(rootDir, "Package.swift")) + inputs.dir(new File(rootDir, "Sources/")) // a bit generous, but better safe than sorry, and include all Swift source changes + outputs.dir(new File(rootDir, ".build")) + + workingDir = rootDir + commandLine "swift" + args("build", "--target", "SwiftKitSwift") +} +tasks.build { + dependsOn("compileSwift") +} + + + +def cleanSwift = tasks.register("cleanSwift", Exec) { + workingDir = rootDir + commandLine "swift" + args("package", "clean") +} +tasks.clean { + dependsOn("cleanSwift") +} + diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/AutoSwiftMemorySession.java similarity index 88% rename from SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/AutoSwiftMemorySession.java index ecbe836e..7b61c2dd 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/AutoSwiftMemorySession.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; @@ -65,3 +65,21 @@ public MemorySegment allocate(long byteSize, long byteAlignment) { return arena.allocate(byteSize, byteAlignment); } } + +/** + * interface AllocatingSwiftArena implements SwiftArena extends SegmentAllocator {} + */ + +/** + * .init(long x, AllocatingSwiftArena arena) // FFM + */ + +/** + * Remain as a lifetime container. Remove `allocate`, so remove `SegmentAllocator` from `SwiftArena` + */ + +/** + * SwiftArena.ofConfined() { + * + * } + */ \ No newline at end of file diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/ClosableSwiftArena.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ClosableSwiftArena.java similarity index 94% rename from SwiftKit/src/main/java/org/swift/swiftkit/ClosableSwiftArena.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/ClosableSwiftArena.java index c257ae57..e75d38c2 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/ClosableSwiftArena.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ClosableSwiftArena.java @@ -12,7 +12,9 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; + +// Can be moved /** * Auto-closable version of {@link SwiftArena}. diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java similarity index 96% rename from SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java index 86725ae8..d6944aa7 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; @@ -20,6 +20,8 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +// FFMConfinedSwiftMemorySession implements Allocating extends Confined?? + final class ConfinedSwiftMemorySession implements ClosableSwiftArena { final static int CLOSED = 0; diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ManagedSwiftType.java similarity index 94% rename from SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/ManagedSwiftType.java index da25ae4d..a0b1bb84 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ManagedSwiftType.java @@ -12,10 +12,12 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.MemorySegment; +// delete? + public interface ManagedSwiftType { /** * The memory segment of `self` of the managed Swift object/value. diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/MemorySegmentUtils.java similarity index 96% rename from SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/MemorySegmentUtils.java index 660a5500..47f90ab8 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/MemorySegmentUtils.java @@ -12,11 +12,13 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; +// move to FFM + public class MemorySegmentUtils { /** * Set the value of `target` to the {@link MemorySegment#address()} of the `memorySegment`, diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftAnyType.java similarity index 95% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftAnyType.java index 4d13ecb7..028fa5ab 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftAnyType.java @@ -12,12 +12,14 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; +// move to FFM, if we don't abstract the type away. + public final class SwiftAnyType { private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftArena.java similarity index 98% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftArena.java index f50025cc..48b64628 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftArena.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.SegmentAllocator; import java.util.concurrent.ThreadFactory; diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftHeapObject.java similarity index 92% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftHeapObject.java index 89050fb5..e9e92863 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftHeapObject.java @@ -12,10 +12,12 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.MemorySegment; +// keep in core as a marker protocol + /** * Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}. */ diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java similarity index 95% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java index 5b8ff700..001fd0fb 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java @@ -12,13 +12,18 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemorySegment; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; +/** + * Maybe have the SwiftInstance require a `destroy` method that the cleanup calls. + * Interface instead of superclass + */ + public abstract class SwiftInstance { /// Pointer to the "self". private final MemorySegment selfMemorySegment; diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java similarity index 97% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java index a9fdd9c8..5837afad 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.MemorySegment; @@ -35,4 +35,4 @@ public void run() { SwiftValueWitnessTable.destroy(selfType, selfPointer); } } -} +} \ No newline at end of file diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftKit.java similarity index 98% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftKit.java index 9c04eded..cc199e6d 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftKit.java @@ -12,9 +12,9 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; -import org.swift.swiftkit.util.PlatformUtils; +import org.swift.swiftkitffm.util.PlatformUtils; import java.io.File; import java.io.IOException; @@ -30,8 +30,11 @@ import java.util.Optional; import java.util.stream.Collectors; -import static org.swift.swiftkit.util.StringUtils.stripPrefix; -import static org.swift.swiftkit.util.StringUtils.stripSuffix; +import static org.swift.swiftkitffm.util.StringUtils.stripPrefix; +import static org.swift.swiftkitffm.util.StringUtils.stripSuffix; + +// rename to SwiftLibraries that has all the library loading +// move all the other stuff to FFM, SwiftFFM class public class SwiftKit { diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValue.java similarity index 95% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValue.java index a364c012..0af529ce 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValue.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; /** * Represent a wrapper around a Swift value object. e.g. {@code struct} or {@code enum}. diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueLayout.java similarity index 98% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueLayout.java index 00215ef0..e1d3efb0 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueLayout.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.AddressLayout; import java.lang.foreign.MemoryLayout; diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueWitnessTable.java similarity index 99% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueWitnessTable.java index 8ddff1b6..4e566538 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueWitnessTable.java @@ -12,13 +12,13 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.*; import java.lang.invoke.*; import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static org.swift.swiftkit.SwiftKit.getSwiftInt; +import static org.swift.swiftkitffm.SwiftKit.getSwiftInt; public abstract class SwiftValueWitnessTable { diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/util/PlatformUtils.java similarity index 97% rename from SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/util/PlatformUtils.java index 6addb31d..7577dee4 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/util/PlatformUtils.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit.util; +package org.swift.swiftkitffm.util; public class PlatformUtils { public static boolean isLinux() { diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/util/StringUtils.java similarity index 96% rename from SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/util/StringUtils.java index 6753ea73..dee04820 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/util/StringUtils.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit.util; +package org.swift.swiftkitffm.util; public class StringUtils { public static String stripPrefix(String mangledName, String prefix) { diff --git a/SwiftKit/build.gradle b/SwiftKitFFM/build.gradle similarity index 95% rename from SwiftKit/build.gradle rename to SwiftKitFFM/build.gradle index 8f3b6c51..b571f171 100644 --- a/SwiftKit/build.gradle +++ b/SwiftKitFFM/build.gradle @@ -16,7 +16,7 @@ plugins { id("build-logic.java-application-conventions") } -group = "org.swift.swiftkit" +group = "org.swift.swiftkitffm" version = "1.0-SNAPSHOT" repositories { @@ -30,6 +30,8 @@ java { } dependencies { + implementation(project(':SwiftKitCore')) + testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation("org.junit.jupiter:junit-jupiter") } diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java new file mode 100644 index 00000000..7b61c2dd --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.lang.ref.Cleaner; +import java.util.Objects; +import java.util.concurrent.ThreadFactory; + +/** + * A memory session which manages registered objects via the Garbage Collector. + * + *

When registered Java wrapper classes around native Swift instances {@link SwiftInstance}, + * are eligible for collection, this will trigger the cleanup of the native resources as well. + * + *

This memory session is LESS reliable than using a {@link ConfinedSwiftMemorySession} because + * the timing of when the native resources are cleaned up is somewhat undefined, and rely on the + * system GC. Meaning, that if an object nas been promoted to an old generation, there may be a + * long time between the resource no longer being referenced "in Java" and its native memory being released, + * and also the deinit of the Swift type being run. + * + *

This can be problematic for Swift applications which rely on quick release of resources, and may expect + * the deinits to run in expected and "quick" succession. + * + *

Whenever possible, prefer using an explicitly managed {@link SwiftArena}, such as {@link SwiftArena#ofConfined()}. + */ +final class AutoSwiftMemorySession implements SwiftArena { + + private final Arena arena; + private final Cleaner cleaner; + + public AutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) { + this.cleaner = Cleaner.create(cleanerThreadFactory); + this.arena = Arena.ofAuto(); + } + + @Override + public void register(SwiftInstance instance) { + Objects.requireNonNull(instance, "value"); + + // We're doing this dance to avoid keeping a strong reference to the value itself + var statusDestroyedFlag = instance.$statusDestroyedFlag(); + Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); + + MemorySegment resource = instance.$memorySegment(); + var cleanupAction = new SwiftInstanceCleanup(resource, instance.$swiftType(), markAsDestroyed); + cleaner.register(instance, cleanupAction); + } + + @Override + public MemorySegment allocate(long byteSize, long byteAlignment) { + return arena.allocate(byteSize, byteAlignment); + } +} + +/** + * interface AllocatingSwiftArena implements SwiftArena extends SegmentAllocator {} + */ + +/** + * .init(long x, AllocatingSwiftArena arena) // FFM + */ + +/** + * Remain as a lifetime container. Remove `allocate`, so remove `SegmentAllocator` from `SwiftArena` + */ + +/** + * SwiftArena.ofConfined() { + * + * } + */ \ No newline at end of file diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableSwiftArena.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableSwiftArena.java new file mode 100644 index 00000000..e75d38c2 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableSwiftArena.java @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +// Can be moved + +/** + * Auto-closable version of {@link SwiftArena}. + */ +public interface ClosableSwiftArena extends SwiftArena, AutoCloseable { + + /** + * 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(); + +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ConfinedSwiftMemorySession.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ConfinedSwiftMemorySession.java new file mode 100644 index 00000000..d6944aa7 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ConfinedSwiftMemorySession.java @@ -0,0 +1,100 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +// FFMConfinedSwiftMemorySession implements Allocating extends Confined?? + +final class ConfinedSwiftMemorySession implements ClosableSwiftArena { + + final static int CLOSED = 0; + final static int ACTIVE = 1; + + final Thread owner; + final AtomicInteger state; + + final Arena arena; + final ConfinedResourceList resources; + + public ConfinedSwiftMemorySession(Thread owner) { + this.owner = owner; + this.state = new AtomicInteger(ACTIVE); + this.resources = new ConfinedResourceList(); + + this.arena = Arena.ofConfined(); + } + + 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("SwiftArena is already closed!"); + } + } + + @Override + public void close() { + checkValid(); + + // Cleanup all resources + if (this.state.compareAndExchange(ACTIVE, CLOSED) == ACTIVE) { + this.resources.runCleanup(); + } // else, was already closed; do nothing + + this.arena.close(); + } + + @Override + public void register(SwiftInstance instance) { + checkValid(); + + var statusDestroyedFlag = instance.$statusDestroyedFlag(); + Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); + + var cleanup = new SwiftInstanceCleanup( + instance.$memorySegment(), + instance.$swiftType(), + markAsDestroyed); + this.resources.add(cleanup); + } + + @Override + public MemorySegment allocate(long byteSize, long byteAlignment) { + return arena.allocate(byteSize, byteAlignment); + } + + static final class ConfinedResourceList implements SwiftResourceList { + // TODO: Could use intrusive linked list to avoid one indirection here + final List resourceCleanups = new LinkedList<>(); + + void add(SwiftInstanceCleanup cleanup) { + resourceCleanups.add(cleanup); + } + + @Override + public void runCleanup() { + for (SwiftInstanceCleanup cleanup : resourceCleanups) { + cleanup.run(); + } + resourceCleanups.clear(); + } + } +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ManagedSwiftType.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ManagedSwiftType.java new file mode 100644 index 00000000..a0b1bb84 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ManagedSwiftType.java @@ -0,0 +1,27 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import java.lang.foreign.MemorySegment; + +// delete? + +public interface ManagedSwiftType { + /** + * The memory segment of `self` of the managed Swift object/value. + */ + public MemorySegment $memorySegment(); + +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java new file mode 100644 index 00000000..47f90ab8 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; + +// move to FFM + +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/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.java new file mode 100644 index 00000000..028fa5ab --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.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.swiftkitffm; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; + +// move to FFM, if we don't abstract the type away. + +public final class SwiftAnyType { + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + SwiftValueLayout.SWIFT_POINTER + ); + + private final MemorySegment memorySegment; + + public SwiftAnyType(MemorySegment memorySegment) { +// if (SwiftKit.getSwiftInt(memorySegment, 0) > 0) { +// throw new IllegalArgumentException("A Swift Any.Type cannot be null!"); +// } + + this.memorySegment = memorySegment.asReadOnly(); + } + + public MemorySegment $memorySegment() { + return memorySegment; + } + + public GroupLayout $layout() { + return $LAYOUT; + } + + /** + * Get the human-readable Swift type name of this type. + */ + public String getSwiftName() { + return SwiftKit.nameOfSwiftType(memorySegment, true); + } + + @Override + public String toString() { + return "AnySwiftType{" + + "name=" + getSwiftName() + + ", memorySegment=" + memorySegment + + '}'; + } + +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftArena.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftArena.java new file mode 100644 index 00000000..48b64628 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftArena.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.swiftkitffm; + +import java.lang.foreign.SegmentAllocator; +import java.util.concurrent.ThreadFactory; + +/** + * 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 ClosableSwiftArena#close()}. + */ +public interface SwiftArena extends SegmentAllocator { + + static ClosableSwiftArena ofConfined() { + return new ConfinedSwiftMemorySession(Thread.currentThread()); + } + + static SwiftArena ofAuto() { + ThreadFactory cleanerThreadFactory = r -> new Thread(r, "AutoSwiftArenaCleanerThread"); + return new AutoSwiftMemorySession(cleanerThreadFactory); + } + + /** + * Register a Swift object. + * Its memory should be considered managed by this arena, and be destroyed when the arena is closed. + */ + void register(SwiftInstance instance); + +} + +/** + * Represents a list of resources that need a cleanup, e.g. allocated classes/structs. + */ +interface SwiftResourceList { + + void runCleanup(); + +} + + +final class UnexpectedRetainCountException extends RuntimeException { + 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/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftHeapObject.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftHeapObject.java new file mode 100644 index 00000000..e9e92863 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftHeapObject.java @@ -0,0 +1,33 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import java.lang.foreign.MemorySegment; + +// keep in core as a marker protocol + +/** + * Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}. + */ +public interface SwiftHeapObject { + MemorySegment $memorySegment(); + + /** + * Pointer to the instance. + */ + public default MemorySegment $instance() { + return this.$memorySegment().get(SwiftValueLayout.SWIFT_POINTER, 0); + } +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstance.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstance.java new file mode 100644 index 00000000..351c46c4 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstance.java @@ -0,0 +1,94 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemorySegment; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * Maybe have the SwiftInstance require a `destroy` method that the cleanup calls. + * Interface instead of superclass + */ + +public abstract class SwiftInstance { + /// Pointer to the "self". + private final MemorySegment selfMemorySegment; + + /** + * The pointer to the instance in memory. I.e. the {@code self} of the Swift object or value. + */ + public final MemorySegment $memorySegment() { + return this.selfMemorySegment; + } + + // TODO: make this a flagset integer and/or use a field updater + /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ + private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); + + /** + * Exposes a boolean value which can be used to indicate if the object was destroyed. + *

+ * This is exposing the object, rather than performing the action because we don't want to accidentally + * form a strong reference to the {@code SwiftInstance} which could prevent the cleanup from running, + * if using an GC managed instance (e.g. using an {@link AutoSwiftMemorySession}. + */ + public final AtomicBoolean $statusDestroyedFlag() { + return this.$state$destroyed; + } + + /** + * The in memory layout of an instance of this Swift type. + */ + public abstract GroupLayout $layout(); + + /** + * The Swift type metadata of this type. + */ + public abstract SwiftAnyType $swiftType(); + + /** + * The designated constructor of any imported Swift types. + * + * @param segment the memory segment. + * @param arena the arena this object belongs to. When the arena goes out of scope, this value is destroyed. + */ + protected SwiftInstance(MemorySegment segment, SwiftArena arena) { + this.selfMemorySegment = segment; + arena.register(this); + } + + /** + * Ensures that this instance has not been destroyed. + *

+ * If this object has been destroyed, calling this method will cause an {@link IllegalStateException} + * to be thrown. This check should be performed before accessing {@code $memorySegment} to prevent + * use-after-free errors. + */ + protected final void $ensureAlive() { + if (this.$state$destroyed.get()) { + throw new IllegalStateException("Attempted to call method on already destroyed instance of " + getClass().getSimpleName() + "!"); + } + } + + /** + * 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. + */ + public boolean isReferenceType() { + return this instanceof SwiftHeapObject; + } +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstanceCleanup.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstanceCleanup.java new file mode 100644 index 00000000..5837afad --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstanceCleanup.java @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import java.lang.foreign.MemorySegment; + +/** + * A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. + */ +record SwiftInstanceCleanup( + MemorySegment selfPointer, + SwiftAnyType selfType, + Runnable markAsDestroyed +) implements Runnable { + + @Override + public void run() { + markAsDestroyed.run(); + + // Allow null pointers just for AutoArena tests. + if (selfType != null && selfPointer != null) { + System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer); + SwiftValueWitnessTable.destroy(selfType, selfPointer); + } + } +} \ No newline at end of file diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftKit.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftKit.java new file mode 100644 index 00000000..302dabf8 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftKit.java @@ -0,0 +1,500 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import org.swift.swiftkitffm.util.PlatformUtils; + +import java.io.File; +import java.io.IOException; +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.swift.swiftkitffm.util.StringUtils.stripPrefix; +import static org.swift.swiftkitffm.util.StringUtils.stripSuffix; + +// rename to SwiftLibraries that has all the library loading +// move all the other stuff to FFM, SwiftFFM class + +public class SwiftKit { + + public static final String STDLIB_DYLIB_NAME = "swiftCore"; + public static final String SWIFTKITSWIFT_DYLIB_NAME = "SwiftKitSwift"; + public static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + private static final String STDLIB_MACOS_DYLIB_PATH = "/usr/lib/swift/libswiftCore.dylib"; + + private static final Arena LIBRARY_ARENA = Arena.ofAuto(); + + @SuppressWarnings("unused") + private static final boolean INITIALIZED_LIBS = loadLibraries(false); + + public static boolean loadLibraries(boolean loadSwiftKit) { + System.loadLibrary(STDLIB_DYLIB_NAME); + if (loadSwiftKit) { + System.loadLibrary(SWIFTKITSWIFT_DYLIB_NAME); + } + return true; + } + + static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); + + private static SymbolLookup getSymbolLookup() { + if (PlatformUtils.isMacOS()) { + // 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()); + } else { + return SymbolLookup.loaderLookup() + .or(Linker.nativeLinker().defaultLookup()); + } + } + + public SwiftKit() { + } + + public 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(%s)\n", + ex.getStackTrace()[1].getFileName(), + ex.getStackTrace()[1].getLineNumber(), + ex.getStackTrace()[1].getClassName(), + ex.getStackTrace()[1].getMethodName(), + traceArgs); + } + + public 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))); + } + + public static String getJavaLibraryPath() { + return System.getProperty("java.library.path"); + } + + public static boolean getJextractTraceDowncalls() { + return Boolean.getBoolean("jextract.trace.downcalls"); + } + + // ==== ------------------------------------------------------------------------------------------------------------ + // Loading libraries + + public static void loadLibrary(String libname) { + // TODO: avoid concurrent loadResource calls; one load is enough esp since we cause File IO when we do that + try { + // try to load a dylib from our classpath, e.g. when we included it in our jar + loadResourceLibrary(libname); + } catch (UnsatisfiedLinkError | RuntimeException e) { + // fallback to plain system path loading + System.loadLibrary(libname); + } + } + + public static void loadResourceLibrary(String libname) { + var resourceName = PlatformUtils.dynamicLibraryName(libname); + if (SwiftKit.TRACE_DOWNCALLS) { + System.out.println("[swift-java] Loading resource library: " + resourceName); + } + + try (var libInputStream = SwiftKit.class.getResourceAsStream("/" + resourceName)) { + if (libInputStream == null) { + throw new RuntimeException("Expected library '" + libname + "' ('" + resourceName + "') was not found as resource!"); + } + + // TODO: we could do an in memory file system here + var tempFile = File.createTempFile(libname, ""); + tempFile.deleteOnExit(); + Files.copy(libInputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + + System.load(tempFile.getAbsolutePath()); + } catch (IOException e) { + throw new RuntimeException("Failed to load dynamic library '" + libname + "' ('" + resourceName + "') as resource!", e); + } + } + + // ==== ------------------------------------------------------------------------------------------------------------ + // free + + static abstract class free { + /** + * Descriptor for the free C runtime function. + */ + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS + ); + + /** + * 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); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + // ==== ------------------------------------------------------------------------------------------------------------ + // swift_retainCount + + private static class swift_retainCount { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /*returns=*/ValueLayout.JAVA_LONG, + ValueLayout.ADDRESS + ); + + public static final MemorySegment ADDR = findOrThrow("swift_retainCount"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + + public static long retainCount(MemorySegment object) { + var mh$ = swift_retainCount.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("swift_retainCount", object); + } + return (long) mh$.invokeExact(object); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + public static long retainCount(SwiftHeapObject object) { + return retainCount(object.$instance()); + } + + // ==== ------------------------------------------------------------------------------------------------------------ + // swift_retain + + private static class swift_retain { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS + ); + + public static final MemorySegment ADDR = findOrThrow("swift_retain"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + + public static void retain(MemorySegment object) { + var mh$ = swift_retain.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("swift_retain", object); + } + mh$.invokeExact(object); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + public static void retain(SwiftHeapObject object) { + retain(object.$instance()); + } + + // ==== ------------------------------------------------------------------------------------------------------------ + // swift_release + + private static class swift_release { + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS + ); + + public static final MemorySegment ADDR = findOrThrow("swift_release"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + + public static void release(MemorySegment object) { + var mh$ = swift_release.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("swift_release", object); + } + mh$.invokeExact(object); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + public static void release(SwiftHeapObject object) { + release(object.$instance()); + } + + // ==== ------------------------------------------------------------------------------------------------------------ + // getTypeByName + + /** + * {@snippet lang = swift: + * func _typeByName(_: Swift.String) -> Any.Type? + *} + */ + private static class swift_getTypeByName { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /*returns=*/ValueLayout.ADDRESS, + ValueLayout.ADDRESS, + ValueLayout.JAVA_INT + ); + + public static final MemorySegment ADDR = findOrThrow("$ss11_typeByNameyypXpSgSSF"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + public static MemorySegment getTypeByName(String string) { + var mh$ = swift_getTypeByName.HANDLE; + try { + if (TRACE_DOWNCALLS) { + traceDowncall("_typeByName"); + } + // TODO: A bit annoying to generate, we need an arena for the conversion... + try (Arena arena = Arena.ofConfined()) { + MemorySegment stringMemorySegment = arena.allocateFrom(string); + + return (MemorySegment) mh$.invokeExact(stringMemorySegment, string.length()); + } + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + /** + * {@snippet lang = swift: + * func _swift_getTypeByMangledNameInEnvironment( + * _ name: UnsafePointer, + * _ nameLength: UInt, + * genericEnvironment: UnsafeRawPointer?, + * genericArguments: UnsafeRawPointer? + * ) -> Any.Type? + *} + */ + private static class swift_getTypeByMangledNameInEnvironment { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /*returns=*/SwiftValueLayout.SWIFT_POINTER, + ValueLayout.ADDRESS, + ValueLayout.JAVA_INT, + ValueLayout.ADDRESS, + ValueLayout.ADDRESS + ); + + public static final MemorySegment ADDR = findOrThrow("swift_getTypeByMangledNameInEnvironment"); + + 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 Optional getTypeByMangledNameInEnvironment(String mangledName) { + System.out.println("Get Any.Type for mangled name: " + mangledName); + + var mh$ = swift_getTypeByMangledNameInEnvironment.HANDLE; + try { + // 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("swift_getTypeByMangledNameInEnvironment", mangledName); + } + try (Arena arena = Arena.ofConfined()) { + MemorySegment stringMemorySegment = arena.allocateFrom(mangledName); + + var memorySegment = (MemorySegment) mh$.invokeExact(stringMemorySegment, mangledName.length(), MemorySegment.NULL, MemorySegment.NULL); + + 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$); + } + } + + /** + * Produce the name of the Swift type given its Swift type metadata. + *

+ * 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 swift_getTypeByMangledNameInEnvironment} call + */ + public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualified) { + 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$); + } + } + + /*** + * Namespace for calls down into swift-java generated thunks and accessors, such as {@code swiftjava_getType_...} etc. + *

Not intended to be used by end-user code directly, but used by swift-java generated Java code. + */ + @SuppressWarnings("unused") // used by source generated Java code + public static final class swiftjava { + private swiftjava() { /* just a namespace */ } + + private static class getType { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /* -> */ValueLayout.ADDRESS); + } + + public static MemorySegment getType(String moduleName, String nominalName) { + // We cannot cache this statically since it depends on the type names we're looking up + // TODO: we could cache the handles per type once we have them, to speed up subsequent calls + String symbol = "swiftjava_getType_" + moduleName + "_" + nominalName; + + try { + var addr = findOrThrow(symbol); + var mh$ = Linker.nativeLinker().downcallHandle(addr, getType.DESC); + return (MemorySegment) mh$.invokeExact(); + } catch (Throwable e) { + throw new AssertionError("Failed to call: " + symbol, e); + } + } + } + + // ==== ------------------------------------------------------------------------------------------------------------ + // Get Swift values out of native memory segments + + /** + * 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 long getSwiftInt(MemorySegment memorySegment, long offset) { + if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { + return memorySegment.get(ValueLayout.JAVA_LONG, offset); + } else { + return memorySegment.get(ValueLayout.JAVA_INT, offset); + } + } + + public static long getSwiftInt(MemorySegment memorySegment, VarHandle handle) { + if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { + return (long) handle.get(memorySegment, 0); + } else { + return (int) handle.get(memorySegment, 0); + } + } + + /** + * Get the method handle of a functional interface. + * + * @param fi functional interface. + * @param name name of the single abstraction method. + * @param fdesc function descriptor of the method. + * @return unbound method handle. + */ + public 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); + } + } + + /** + * Convert String to a MemorySegment filled with the C string. + */ + public static MemorySegment toCString(String str, Arena arena) { + return arena.allocateFrom(str); + } + + private static class swift_getTypeName { + + /** + * Descriptor for the swift_getTypeName runtime function. + */ + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /* -> */MemoryLayout.structLayout( + SwiftValueLayout.SWIFT_POINTER.withName("utf8Chars"), + SwiftValueLayout.SWIFT_INT.withName("length") + ), + ValueLayout.ADDRESS, + ValueLayout.JAVA_BOOLEAN + ); + + /** + * 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); + } + +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValue.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValue.java new file mode 100644 index 00000000..0af529ce --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValue.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.swiftkitffm; + +/** + * Represent a wrapper around a Swift value object. e.g. {@code struct} or {@code enum}. + */ +public interface SwiftValue { +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueLayout.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueLayout.java new file mode 100644 index 00000000..e1d3efb0 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueLayout.java @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import java.lang.foreign.AddressLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.SequenceLayout; +import java.lang.foreign.ValueLayout; + +import static java.lang.foreign.ValueLayout.*; + +/** + * 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 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; + + // FIXME: this sequence layout is a workaround, we must properly size pointers when we get them. + public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE)); + public static final SequenceLayout SWIFT_BYTE_ARRAY = MemoryLayout.sequenceLayout(8, ValueLayout.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) ? + SWIFT_INT32 : SWIFT_INT64; + + /** + * 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/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueWitnessTable.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueWitnessTable.java new file mode 100644 index 00000000..4e566538 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueWitnessTable.java @@ -0,0 +1,290 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import java.lang.foreign.*; +import java.lang.invoke.*; + +import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static org.swift.swiftkitffm.SwiftKit.getSwiftInt; + +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"); + + + /** + * 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 address + * 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. + */ + static final long $stride$offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("stride")); + + /** + * Variable handle for the "stride" field within the value witness table. + */ + static final VarHandle $stride$mh = + $LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("stride")); + + /** + * 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 + */ + 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 + */ + public static long alignmentOfSwiftType(MemorySegment typeMetadata) { + long flags = getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$flags$offset); + return (flags & 0xFF) + 1; + } + + /** + * 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 $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 = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("destroy")); + + static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS, // witness table functions expect a pointer to self pointer + ValueLayout.ADDRESS // pointer to the witness table + ); + + /** + * Function pointer for the destroy operation + */ + static MemorySegment addr(SwiftAnyType ty) { + // 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(SwiftAnyType ty) { + return Linker.nativeLinker().downcallHandle(addr(ty), DESC); + } + } + + + /** + * Destroy the value/object. + *

+ * This includes deallocating the Swift managed memory for the object. + */ + public static void destroy(SwiftAnyType type, MemorySegment object) { + var fullTypeMetadata = fullTypeMetadata(type.$memorySegment()); + var wtable = valueWitnessTable(fullTypeMetadata); + + var mh = destroy.handle(type); + + try { + mh.invokeExact(object, wtable); + } catch (Throwable th) { + throw new AssertionError("Failed to destroy '" + type + "' at " + object, th); + } + } + + /** + * {@snippet lang = C: + * /// T *(*initializeWithCopy)(T *dest, T *src, M *self); + * /// + * /// Given an invalid object of this type, initialize it as a copy of + * /// the source object. Returns the dest object. + * FUNCTION_VALUE_WITNESS(initializeWithCopy, + * InitializeWithCopy, + * MUTABLE_VALUE_TYPE, + * (MUTABLE_VALUE_TYPE, MUTABLE_VALUE_TYPE, TYPE_TYPE)) + *} + */ + private static class initializeWithCopy { + + static final long $offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("initializeWithCopy")); + + static final FunctionDescriptor DESC = FunctionDescriptor.of( + /* -> */ ValueLayout.ADDRESS, // returns the destination object + ValueLayout.ADDRESS, // destination + ValueLayout.ADDRESS, // source + ValueLayout.ADDRESS // pointer to the witness table + ); + + /** + * Function pointer for the initializeWithCopy operation + */ + static MemorySegment addr(SwiftAnyType ty) { + // Get the value witness table of the type + final var vwt = SwiftValueWitnessTable.valueWitnessTable(ty.$memorySegment()); + + // Get the address of the function stored at the offset of the witness table + long funcAddress = getSwiftInt(vwt, initializeWithCopy.$offset); + return MemorySegment.ofAddress(funcAddress); + } + + static MethodHandle handle(SwiftAnyType ty) { + return Linker.nativeLinker().downcallHandle(addr(ty), DESC); + } + } + + + /** + * Given an invalid object of this type, initialize it as a copy of + * the source object. + *

+ * Returns the dest object. + */ + public static MemorySegment initializeWithCopy(SwiftAnyType type, MemorySegment dest, MemorySegment src) { + var fullTypeMetadata = fullTypeMetadata(type.$memorySegment()); + var wtable = valueWitnessTable(fullTypeMetadata); + + var mh = initializeWithCopy.handle(type); + + try { + return (MemorySegment) mh.invokeExact(dest, src, wtable); + } catch (Throwable th) { + throw new AssertionError("Failed to initializeWithCopy '" + type + "' (" + dest + ", " + src + ")", th); + } + } + +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/PlatformUtils.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/PlatformUtils.java new file mode 100644 index 00000000..7577dee4 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/PlatformUtils.java @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm.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"); + } + + 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"); + } + + public static String dynamicLibraryName(String base) { + if (isLinux()) { + return "lib" + uppercaseFistLetter(base) + ".so"; + } else { + return "lib" + uppercaseFistLetter(base) + ".dylib"; + } + } + + static String uppercaseFistLetter(String base) { + if (base == null || base.isEmpty()) { + return base; + } + return base.substring(0, 1).toUpperCase() + base.substring(1); + } +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/StringUtils.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/StringUtils.java new file mode 100644 index 00000000..dee04820 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/StringUtils.java @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm.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; + } + + public static String hexString(long number) { + return String.format("0x%02x", number); + } + +} diff --git a/SwiftKit/src/test/java/org/swift/swiftkit/AutoArenaTest.java b/SwiftKitFFM/src/test/java/org/swift/swiftkitffm/AutoArenaTest.java similarity index 93% rename from SwiftKit/src/test/java/org/swift/swiftkit/AutoArenaTest.java rename to SwiftKitFFM/src/test/java/org/swift/swiftkitffm/AutoArenaTest.java index 23365de9..822d9d7f 100644 --- a/SwiftKit/src/test/java/org/swift/swiftkit/AutoArenaTest.java +++ b/SwiftKitFFM/src/test/java/org/swift/swiftkitffm/AutoArenaTest.java @@ -12,14 +12,12 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import org.junit.jupiter.api.Test; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemorySegment; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; public class AutoArenaTest { diff --git a/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java b/SwiftKitFFM/src/test/java/org/swift/swiftkitffm/SwiftRuntimeMetadataTest.java similarity index 95% rename from SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java rename to SwiftKitFFM/src/test/java/org/swift/swiftkitffm/SwiftRuntimeMetadataTest.java index 3419405e..15f3c4fd 100644 --- a/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java +++ b/SwiftKitFFM/src/test/java/org/swift/swiftkitffm/SwiftRuntimeMetadataTest.java @@ -12,11 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +package org.swift.swiftkitffm; public class SwiftRuntimeMetadataTest { diff --git a/settings.gradle b/settings.gradle index fa0fa5bd..9bf38ba5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,7 +18,8 @@ pluginManagement { rootProject.name = "swift-java" -include "SwiftKit" +include "SwiftKitCore" +include "SwiftKitFFM" // Include sample apps -- you can run them via `gradle Name:run` new File(rootDir, "Samples").listFiles().each { From 33617e4b1055230590a6471c2b9ea1f3a50960a9 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Thu, 3 Jul 2025 22:40:49 +0200 Subject: [PATCH 2/3] work on unifying modules --- .../com/example/swift/HelloJava2Swift.java | 8 +- .../com/example/swift/MySwiftClassTest.java | 4 +- .../swift/swiftkitffm/MySwiftClassTest.java | 10 +- .../org/swift/swiftkitffm/SwiftArenaTest.java | 3 +- .../com/example/swift/HelloJava2Swift.java | 28 +- .../com/example/swift/MySwiftClassTest.java | 4 +- .../swift/swiftkitffm/MySwiftClassTest.java | 10 +- .../org/swift/swiftkitffm/SwiftArenaTest.java | 3 +- .../swiftkitcore/AutoSwiftMemorySession.java | 57 +- .../swiftkitcore/ClosableSwiftArena.java | 5 +- .../ConfinedSwiftMemorySession.java | 29 +- .../swiftkitcore/MemorySegmentUtils.java | 39 -- .../org/swift/swiftkitcore/SwiftAnyType.java | 62 --- .../org/swift/swiftkitcore/SwiftArena.java | 14 +- .../swift/swiftkitcore/SwiftHeapObject.java | 13 +- .../org/swift/swiftkitcore/SwiftInstance.java | 37 +- .../swiftkitcore/SwiftInstanceCleanup.java | 25 +- .../java/org/swift/swiftkitcore/SwiftKit.java | 502 ------------------ .../swift/swiftkitcore/SwiftLibraries.java | 79 +++ .../org/swift/swiftkitcore/SwiftValue.java | 2 +- .../swift/swiftkitcore/SwiftValueLayout.java | 64 --- .../swiftkitcore/SwiftValueWitnessTable.java | 290 ---------- .../swiftkitcore/WrongThreadException.java | 10 +- .../swiftkitcore/util/PlatformUtils.java | 2 +- .../swiftkitffm/AllocatingSwiftArena.java | 21 + .../swiftkitffm/AutoSwiftMemorySession.java | 35 +- .../ClosableAllocatingSwiftArena.java | 15 +- .../swift/swiftkitffm/ClosableSwiftArena.java | 30 -- .../ConfinedSwiftMemorySession.java | 100 ---- .../FFMConfinedSwiftMemorySession.java | 40 ++ .../swift/swiftkitffm/FFMSwiftInstance.java | 54 ++ .../swift/swiftkitffm/ManagedSwiftType.java | 27 - .../swift/swiftkitffm/MemorySegmentUtils.java | 2 - .../org/swift/swiftkitffm/SwiftAnyType.java | 4 +- .../org/swift/swiftkitffm/SwiftArena.java | 62 --- .../{SwiftKit.java => SwiftFFM.java} | 49 +- .../swift/swiftkitffm/SwiftHeapObject.java | 33 -- .../org/swift/swiftkitffm/SwiftInstance.java | 94 ---- .../swiftkitffm/SwiftInstanceCleanup.java | 38 -- .../swiftkitffm/SwiftValueWitnessTable.java | 4 +- .../swift/swiftkitffm/util/PlatformUtils.java | 53 -- .../swift/swiftkitffm/util/StringUtils.java | 36 -- 42 files changed, 315 insertions(+), 1682 deletions(-) delete mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkitcore/MemorySegmentUtils.java delete mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftAnyType.java delete mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftKit.java create mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftLibraries.java delete mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueLayout.java delete mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueWitnessTable.java rename SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValue.java => SwiftKitCore/src/main/java/org/swift/swiftkitcore/WrongThreadException.java (74%) create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AllocatingSwiftArena.java rename SwiftKitCore/src/main/java/org/swift/swiftkitcore/ManagedSwiftType.java => SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableAllocatingSwiftArena.java (70%) delete mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableSwiftArena.java delete mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ConfinedSwiftMemorySession.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMConfinedSwiftMemorySession.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstance.java delete mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ManagedSwiftType.java delete mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftArena.java rename SwiftKitFFM/src/main/java/org/swift/swiftkitffm/{SwiftKit.java => SwiftFFM.java} (89%) delete mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftHeapObject.java delete mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstance.java delete mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstanceCleanup.java delete mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/PlatformUtils.java delete mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/StringUtils.java diff --git a/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java index edfdcde3..5f02a1f5 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java @@ -18,7 +18,7 @@ // Import javakit/swiftkit support libraries import org.swift.swiftkitffm.SwiftArena; -import org.swift.swiftkitffm.SwiftKit; +import org.swift.swiftkitffm.SwiftFFM; public class HelloJava2Swift { @@ -26,7 +26,7 @@ public static void main(String[] args) { boolean traceDowncalls = Boolean.getBoolean("jextract.trace.downcalls"); System.out.println("Property: jextract.trace.downcalls = " + traceDowncalls); - System.out.print("Property: java.library.path = " +SwiftKit.getJavaLibraryPath()); + System.out.print("Property: java.library.path = " + SwiftFFM.getJavaLibraryPath()); examples(); } @@ -41,8 +41,8 @@ static void examples() { MySwiftClass obj = MySwiftClass.init(2222, 7777, arena); // just checking retains/releases work - SwiftKit.retain(obj); - SwiftKit.release(obj); + SwiftFFM.retain(obj); + SwiftFFM.release(obj); obj.voidMethod(); obj.takeIntMethod(42); diff --git a/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java index e9425ec7..e7e53224 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java @@ -17,7 +17,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.swift.swiftkitffm.SwiftArena; -import org.swift.swiftkitffm.SwiftKit; +import org.swift.swiftkitffm.SwiftFFM; import java.io.File; import java.util.stream.Stream; @@ -27,7 +27,7 @@ public class MySwiftClassTest { void checkPaths(Throwable throwable) { - var paths = SwiftKit.getJavaLibraryPath().split(":"); + var paths = SwiftFFM.getJavaLibraryPath().split(":"); for (var path : paths) { System.out.println("CHECKING PATH: " + path); Stream.of(new File(path).listFiles()) diff --git a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java index 169d750c..d26d55a5 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java @@ -27,13 +27,13 @@ void call_retain_retainCount_release() { var arena = SwiftArena.ofConfined(); var obj = MySwiftClass.init(1, 2, arena); - assertEquals(1, SwiftKit.retainCount(obj)); + assertEquals(1, SwiftFFM.retainCount(obj)); // TODO: test directly on SwiftHeapObject inheriting obj - SwiftKit.retain(obj); - assertEquals(2, SwiftKit.retainCount(obj)); + SwiftFFM.retain(obj); + assertEquals(2, SwiftFFM.retainCount(obj)); - SwiftKit.release(obj); - assertEquals(1, SwiftKit.retainCount(obj)); + SwiftFFM.release(obj); + assertEquals(1, SwiftFFM.retainCount(obj)); } } diff --git a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java index 97b9a99b..8ce83fd5 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java @@ -20,8 +20,7 @@ import org.swift.swiftkitffm.util.PlatformUtils; import static org.junit.jupiter.api.Assertions.*; -import static org.swift.swiftkitffm.SwiftKit.*; -import static org.swift.swiftkitffm.SwiftKit.retainCount; +import static org.swift.swiftkitffm.SwiftFFM.retainCount; public class SwiftArenaTest { 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 271368ad..8454e5ee 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -19,7 +19,7 @@ // Import javakit/swiftkit support libraries import org.swift.swiftkitffm.SwiftArena; -import org.swift.swiftkitffm.SwiftKit; +import org.swift.swiftkitffm.SwiftFFM; public class HelloJava2Swift { @@ -27,7 +27,7 @@ public static void main(String[] args) { boolean traceDowncalls = Boolean.getBoolean("jextract.trace.downcalls"); System.out.println("Property: jextract.trace.downcalls = " + traceDowncalls); - System.out.print("Property: java.library.path = " + SwiftKit.getJavaLibraryPath()); + System.out.print("Property: java.library.path = " + SwiftFFM.getJavaLibraryPath()); examples(); } @@ -39,30 +39,30 @@ static void examples() { long cnt = MySwiftLibrary.globalWriteString("String from Java"); - SwiftKit.trace("count = " + cnt); + SwiftFFM.trace("count = " + cnt); MySwiftLibrary.globalCallMeRunnable(() -> { - SwiftKit.trace("running runnable"); + SwiftFFM.trace("running runnable"); }); - SwiftKit.trace("getGlobalBuffer().byteSize()=" + MySwiftLibrary.getGlobalBuffer().byteSize()); + SwiftFFM.trace("getGlobalBuffer().byteSize()=" + MySwiftLibrary.getGlobalBuffer().byteSize()); MySwiftLibrary.withBuffer((buf) -> { - SwiftKit.trace("withBuffer{$0.byteSize()}=" + buf.byteSize()); + SwiftFFM.trace("withBuffer{$0.byteSize()}=" + buf.byteSize()); }); // Example of using an arena; MyClass.deinit is run at end of scope try (var arena = SwiftArena.ofConfined()) { MySwiftClass obj = MySwiftClass.init(2222, 7777, arena); // just checking retains/releases work - SwiftKit.trace("retainCount = " + SwiftKit.retainCount(obj)); - SwiftKit.retain(obj); - SwiftKit.trace("retainCount = " + SwiftKit.retainCount(obj)); - SwiftKit.release(obj); - SwiftKit.trace("retainCount = " + SwiftKit.retainCount(obj)); + SwiftFFM.trace("retainCount = " + SwiftFFM.retainCount(obj)); + SwiftFFM.retain(obj); + SwiftFFM.trace("retainCount = " + SwiftFFM.retainCount(obj)); + SwiftFFM.release(obj); + SwiftFFM.trace("retainCount = " + SwiftFFM.retainCount(obj)); obj.setCounter(12); - SwiftKit.trace("obj.counter = " + obj.getCounter()); + SwiftFFM.trace("obj.counter = " + obj.getCounter()); obj.voidMethod(); obj.takeIntMethod(42); @@ -71,9 +71,9 @@ static void examples() { otherObj.voidMethod(); MySwiftStruct swiftValue = MySwiftStruct.init(2222, 1111, arena); - SwiftKit.trace("swiftValue.capacity = " + swiftValue.getCapacity()); + SwiftFFM.trace("swiftValue.capacity = " + swiftValue.getCapacity()); swiftValue.withCapLen((cap, len) -> { - SwiftKit.trace("withCapLenCallback: cap=" + cap + ", len=" + len); + SwiftFFM.trace("withCapLenCallback: cap=" + cap + ", len=" + len); }); } diff --git a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java index c272c35a..df7feff1 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java @@ -17,7 +17,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.swift.swiftkitffm.SwiftArena; -import org.swift.swiftkitffm.SwiftKit; +import org.swift.swiftkitffm.SwiftFFM; import java.io.File; import java.util.stream.Stream; @@ -27,7 +27,7 @@ public class MySwiftClassTest { void checkPaths(Throwable throwable) { - var paths = SwiftKit.getJavaLibraryPath().split(":"); + var paths = SwiftFFM.getJavaLibraryPath().split(":"); for (var path : paths) { Stream.of(new File(path).listFiles()) .filter(file -> !file.isDirectory()) diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java index 169d750c..d26d55a5 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftClassTest.java @@ -27,13 +27,13 @@ void call_retain_retainCount_release() { var arena = SwiftArena.ofConfined(); var obj = MySwiftClass.init(1, 2, arena); - assertEquals(1, SwiftKit.retainCount(obj)); + assertEquals(1, SwiftFFM.retainCount(obj)); // TODO: test directly on SwiftHeapObject inheriting obj - SwiftKit.retain(obj); - assertEquals(2, SwiftKit.retainCount(obj)); + SwiftFFM.retain(obj); + assertEquals(2, SwiftFFM.retainCount(obj)); - SwiftKit.release(obj); - assertEquals(1, SwiftKit.retainCount(obj)); + SwiftFFM.release(obj); + assertEquals(1, SwiftFFM.retainCount(obj)); } } diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java index 0b53eb9c..f96efe3b 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkitffm/SwiftArenaTest.java @@ -21,8 +21,7 @@ import org.swift.swiftkitffm.util.PlatformUtils; import static org.junit.jupiter.api.Assertions.*; -import static org.swift.swiftkitffm.SwiftKit.*; -import static org.swift.swiftkitffm.SwiftKit.retainCount; +import static org.swift.swiftkitffm.SwiftFFM.retainCount; public class SwiftArenaTest { diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/AutoSwiftMemorySession.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/AutoSwiftMemorySession.java index 7b61c2dd..cf9957ef 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/AutoSwiftMemorySession.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/AutoSwiftMemorySession.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkitffm; +package org.swift.swiftkitcore; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; @@ -37,34 +37,33 @@ * *

Whenever possible, prefer using an explicitly managed {@link SwiftArena}, such as {@link SwiftArena#ofConfined()}. */ -final class AutoSwiftMemorySession implements SwiftArena { - - private final Arena arena; - private final Cleaner cleaner; - - public AutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) { - this.cleaner = Cleaner.create(cleanerThreadFactory); - this.arena = Arena.ofAuto(); - } - - @Override - public void register(SwiftInstance instance) { - Objects.requireNonNull(instance, "value"); - - // We're doing this dance to avoid keeping a strong reference to the value itself - var statusDestroyedFlag = instance.$statusDestroyedFlag(); - Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); - - MemorySegment resource = instance.$memorySegment(); - var cleanupAction = new SwiftInstanceCleanup(resource, instance.$swiftType(), markAsDestroyed); - cleaner.register(instance, cleanupAction); - } - - @Override - public MemorySegment allocate(long byteSize, long byteAlignment) { - return arena.allocate(byteSize, byteAlignment); - } -} +//final class AutoSwiftMemorySession implements SwiftArena { +// +// private final Cleaner cleaner; +// +// public AutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) { +// this.cleaner = Cleaner.create(cleanerThreadFactory); +// this.arena = Arena.ofAuto(); +// } +// +// @Override +// public void register(SwiftInstance instance) { +// Objects.requireNonNull(instance, "value"); +// +// // We're doing this dance to avoid keeping a strong reference to the value itself +// var statusDestroyedFlag = instance.$statusDestroyedFlag(); +// Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); +// +// MemorySegment resource = instance.$memorySegment(); +// var cleanupAction = new SwiftInstanceCleanup(resource, instance.$swiftType(), markAsDestroyed); +// cleaner.register(instance, cleanupAction); +// } +// +// @Override +// public MemorySegment allocate(long byteSize, long byteAlignment) { +// return arena.allocate(byteSize, byteAlignment); +// } +//} /** * interface AllocatingSwiftArena implements SwiftArena extends SegmentAllocator {} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ClosableSwiftArena.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ClosableSwiftArena.java index e75d38c2..09a20b65 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ClosableSwiftArena.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ClosableSwiftArena.java @@ -12,9 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkitffm; - -// Can be moved +package org.swift.swiftkitcore; /** * Auto-closable version of {@link SwiftArena}. @@ -26,5 +24,4 @@ public interface ClosableSwiftArena extends SwiftArena, AutoCloseable { * Throws if unable to verify all resources have been release (e.g. over retained Swift classes) */ void close(); - } diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java index d6944aa7..16a142d1 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkitffm; +package org.swift.swiftkitcore; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; @@ -20,9 +20,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -// FFMConfinedSwiftMemorySession implements Allocating extends Confined?? - -final class ConfinedSwiftMemorySession implements ClosableSwiftArena { +public class ConfinedSwiftMemorySession implements ClosableSwiftArena { final static int CLOSED = 0; final static int ACTIVE = 1; @@ -30,21 +28,17 @@ final class ConfinedSwiftMemorySession implements ClosableSwiftArena { final Thread owner; final AtomicInteger state; - final Arena arena; final ConfinedResourceList resources; public ConfinedSwiftMemorySession(Thread owner) { this.owner = owner; this.state = new AtomicInteger(ACTIVE); this.resources = new ConfinedResourceList(); - - this.arena = Arena.ofConfined(); } 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())); + throw new WrongThreadException(String.format("ConfinedSwift arena is confined to %s but was closed from %s!", this.owner, Thread.currentThread())); } else if (this.state.get() < ACTIVE) { throw new RuntimeException("SwiftArena is already closed!"); } @@ -55,32 +49,19 @@ public void close() { checkValid(); // Cleanup all resources - if (this.state.compareAndExchange(ACTIVE, CLOSED) == ACTIVE) { + if (this.state.compareAndSet(ACTIVE, CLOSED)) { this.resources.runCleanup(); } // else, was already closed; do nothing - - this.arena.close(); } @Override public void register(SwiftInstance instance) { checkValid(); - var statusDestroyedFlag = instance.$statusDestroyedFlag(); - Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); - - var cleanup = new SwiftInstanceCleanup( - instance.$memorySegment(), - instance.$swiftType(), - markAsDestroyed); + SwiftInstanceCleanup cleanup = new SwiftInstanceCleanup(instance); this.resources.add(cleanup); } - @Override - public MemorySegment allocate(long byteSize, long byteAlignment) { - return arena.allocate(byteSize, byteAlignment); - } - static final class ConfinedResourceList implements SwiftResourceList { // TODO: Could use intrusive linked list to avoid one indirection here final List resourceCleanups = new LinkedList<>(); diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/MemorySegmentUtils.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/MemorySegmentUtils.java deleted file mode 100644 index 47f90ab8..00000000 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/MemorySegmentUtils.java +++ /dev/null @@ -1,39 +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.swiftkitffm; - -import java.lang.foreign.MemorySegment; -import java.lang.foreign.ValueLayout; - -// move to FFM - -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/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftAnyType.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftAnyType.java deleted file mode 100644 index 028fa5ab..00000000 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftAnyType.java +++ /dev/null @@ -1,62 +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.swiftkitffm; - -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.MemorySegment; - -// move to FFM, if we don't abstract the type away. - -public final class SwiftAnyType { - - private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( - SwiftValueLayout.SWIFT_POINTER - ); - - private final MemorySegment memorySegment; - - public SwiftAnyType(MemorySegment memorySegment) { -// if (SwiftKit.getSwiftInt(memorySegment, 0) > 0) { -// throw new IllegalArgumentException("A Swift Any.Type cannot be null!"); -// } - - this.memorySegment = memorySegment.asReadOnly(); - } - - public MemorySegment $memorySegment() { - return memorySegment; - } - - public GroupLayout $layout() { - return $LAYOUT; - } - - /** - * Get the human-readable Swift type name of this type. - */ - public String getSwiftName() { - return SwiftKit.nameOfSwiftType(memorySegment, true); - } - - @Override - public String toString() { - return "AnySwiftType{" + - "name=" + getSwiftName() + - ", memorySegment=" + memorySegment + - '}'; - } - -} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftArena.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftArena.java index 48b64628..e44ec365 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftArena.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftArena.java @@ -12,9 +12,8 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkitffm; +package org.swift.swiftkitcore; -import java.lang.foreign.SegmentAllocator; import java.util.concurrent.ThreadFactory; /** @@ -24,7 +23,7 @@ *

A confined arena has an associated owner thread that confines some operations to * associated owner thread such as {@link ClosableSwiftArena#close()}. */ -public interface SwiftArena extends SegmentAllocator { +public interface SwiftArena { static ClosableSwiftArena ofConfined() { return new ConfinedSwiftMemorySession(Thread.currentThread()); @@ -51,12 +50,3 @@ interface SwiftResourceList { void runCleanup(); } - - -final class UnexpectedRetainCountException extends RuntimeException { - 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/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftHeapObject.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftHeapObject.java index e9e92863..16e97b22 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftHeapObject.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftHeapObject.java @@ -12,22 +12,11 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkitffm; +package org.swift.swiftkitcore; -import java.lang.foreign.MemorySegment; - -// keep in core as a marker protocol /** * Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}. */ public interface SwiftHeapObject { - MemorySegment $memorySegment(); - - /** - * Pointer to the instance. - */ - public default MemorySegment $instance() { - return this.$memorySegment().get(SwiftValueLayout.SWIFT_POINTER, 0); - } } diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java index 001fd0fb..3c414215 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java @@ -12,29 +12,26 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkitffm; +package org.swift.swiftkitcore; -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemorySegment; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; - -/** - * Maybe have the SwiftInstance require a `destroy` method that the cleanup calls. - * Interface instead of superclass - */ public abstract class SwiftInstance { /// Pointer to the "self". - private final MemorySegment selfMemorySegment; + private final long selfPointer; /** * The pointer to the instance in memory. I.e. the {@code self} of the Swift object or value. */ - public final MemorySegment $memorySegment() { - return this.selfMemorySegment; + public final long pointer() { + return this.selfPointer; } + /** + * Called when the arena has decided the value should be destroyed. + */ + public abstract void destroy(); + // TODO: make this a flagset integer and/or use a field updater /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); @@ -50,24 +47,14 @@ public abstract class SwiftInstance { return this.$state$destroyed; } - /** - * The in memory layout of an instance of this Swift type. - */ - public abstract GroupLayout $layout(); - - /** - * The Swift type metadata of this type. - */ - public abstract SwiftAnyType $swiftType(); - /** * The designated constructor of any imported Swift types. * - * @param segment the memory segment. + * @param pointer a pointer to the memory containing the value * @param arena the arena this object belongs to. When the arena goes out of scope, this value is destroyed. */ - protected SwiftInstance(MemorySegment segment, SwiftArena arena) { - this.selfMemorySegment = segment; + protected SwiftInstance(long pointer, SwiftArena arena) { + this.selfPointer = pointer; arena.register(this); } diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java index 5837afad..b131a181 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java @@ -12,27 +12,24 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkitffm; - -import java.lang.foreign.MemorySegment; +package org.swift.swiftkitcore; /** * A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. */ -record SwiftInstanceCleanup( - MemorySegment selfPointer, - SwiftAnyType selfType, - Runnable markAsDestroyed -) implements Runnable { +public final class SwiftInstanceCleanup implements Runnable { + // TODO: Should this be a weak reference? + private final SwiftInstance swiftInstance; + + public SwiftInstanceCleanup(SwiftInstance swiftInstance) { + this.swiftInstance = swiftInstance; + } @Override public void run() { - markAsDestroyed.run(); + swiftInstance.$statusDestroyedFlag().set(true); - // Allow null pointers just for AutoArena tests. - if (selfType != null && selfPointer != null) { - System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer); - SwiftValueWitnessTable.destroy(selfType, selfPointer); - } + // System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer); + swiftInstance.destroy(); } } \ No newline at end of file diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftKit.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftKit.java deleted file mode 100644 index cc199e6d..00000000 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftKit.java +++ /dev/null @@ -1,502 +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.swiftkitffm; - -import org.swift.swiftkitffm.util.PlatformUtils; - -import java.io.File; -import java.io.IOException; -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.nio.file.CopyOption; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.Arrays; -import java.util.Optional; -import java.util.stream.Collectors; - -import static org.swift.swiftkitffm.util.StringUtils.stripPrefix; -import static org.swift.swiftkitffm.util.StringUtils.stripSuffix; - -// rename to SwiftLibraries that has all the library loading -// move all the other stuff to FFM, SwiftFFM class - -public class SwiftKit { - - public static final String STDLIB_DYLIB_NAME = "swiftCore"; - public static final String SWIFTKITSWIFT_DYLIB_NAME = "SwiftKitSwift"; - public static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); - - private static final String STDLIB_MACOS_DYLIB_PATH = "/usr/lib/swift/libswiftCore.dylib"; - - private static final Arena LIBRARY_ARENA = Arena.ofAuto(); - - @SuppressWarnings("unused") - private static final boolean INITIALIZED_LIBS = loadLibraries(false); - - public static boolean loadLibraries(boolean loadSwiftKit) { - System.loadLibrary(STDLIB_DYLIB_NAME); - if (loadSwiftKit) { - System.loadLibrary(SWIFTKITSWIFT_DYLIB_NAME); - } - return true; - } - - static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); - - private static SymbolLookup getSymbolLookup() { - if (PlatformUtils.isMacOS()) { - // 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()); - } else { - return SymbolLookup.loaderLookup() - .or(Linker.nativeLinker().defaultLookup()); - } - } - - public SwiftKit() { - } - - public 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(%s)\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getClassName(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - - public 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))); - } - - public static String getJavaLibraryPath() { - return System.getProperty("java.library.path"); - } - - public static boolean getJextractTraceDowncalls() { - return Boolean.getBoolean("jextract.trace.downcalls"); - } - - // ==== ------------------------------------------------------------------------------------------------------------ - // Loading libraries - - public static void loadLibrary(String libname) { - // TODO: avoid concurrent loadResource calls; one load is enough esp since we cause File IO when we do that - try { - // try to load a dylib from our classpath, e.g. when we included it in our jar - loadResourceLibrary(libname); - } catch (UnsatisfiedLinkError | RuntimeException e) { - // fallback to plain system path loading - System.loadLibrary(libname); - } - } - - public static void loadResourceLibrary(String libname) { - var resourceName = PlatformUtils.dynamicLibraryName(libname); - if (SwiftKit.TRACE_DOWNCALLS) { - System.out.println("[swift-java] Loading resource library: " + resourceName); - } - - try (var libInputStream = SwiftKit.class.getResourceAsStream("/" + resourceName)) { - if (libInputStream == null) { - throw new RuntimeException("Expected library '" + libname + "' ('" + resourceName + "') was not found as resource!"); - } - - // TODO: we could do an in memory file system here - var tempFile = File.createTempFile(libname, ""); - tempFile.deleteOnExit(); - Files.copy(libInputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - - System.load(tempFile.getAbsolutePath()); - } catch (IOException e) { - throw new RuntimeException("Failed to load dynamic library '" + libname + "' ('" + resourceName + "') as resource!", e); - } - } - - // ==== ------------------------------------------------------------------------------------------------------------ - // free - - static abstract class free { - /** - * Descriptor for the free C runtime function. - */ - public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - ValueLayout.ADDRESS - ); - - /** - * 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); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - // ==== ------------------------------------------------------------------------------------------------------------ - // swift_retainCount - - private static class swift_retainCount { - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /*returns=*/ValueLayout.JAVA_LONG, - ValueLayout.ADDRESS - ); - - public static final MemorySegment ADDR = findOrThrow("swift_retainCount"); - - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } - - - public static long retainCount(MemorySegment object) { - var mh$ = swift_retainCount.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall("swift_retainCount", object); - } - return (long) mh$.invokeExact(object); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - public static long retainCount(SwiftHeapObject object) { - return retainCount(object.$instance()); - } - - // ==== ------------------------------------------------------------------------------------------------------------ - // swift_retain - - private static class swift_retain { - public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - ValueLayout.ADDRESS - ); - - public static final MemorySegment ADDR = findOrThrow("swift_retain"); - - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } - - - public static void retain(MemorySegment object) { - var mh$ = swift_retain.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall("swift_retain", object); - } - mh$.invokeExact(object); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - public static void retain(SwiftHeapObject object) { - retain(object.$instance()); - } - - // ==== ------------------------------------------------------------------------------------------------------------ - // swift_release - - private static class swift_release { - public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - ValueLayout.ADDRESS - ); - - public static final MemorySegment ADDR = findOrThrow("swift_release"); - - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } - - - public static void release(MemorySegment object) { - var mh$ = swift_release.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall("swift_release", object); - } - mh$.invokeExact(object); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - public static void release(SwiftHeapObject object) { - release(object.$instance()); - } - - // ==== ------------------------------------------------------------------------------------------------------------ - // getTypeByName - - /** - * {@snippet lang = swift: - * func _typeByName(_: Swift.String) -> Any.Type? - *} - */ - private static class swift_getTypeByName { - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /*returns=*/ValueLayout.ADDRESS, - ValueLayout.ADDRESS, - ValueLayout.JAVA_INT - ); - - public static final MemorySegment ADDR = findOrThrow("$ss11_typeByNameyypXpSgSSF"); - - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } - - public static MemorySegment getTypeByName(String string) { - var mh$ = swift_getTypeByName.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall("_typeByName"); - } - // TODO: A bit annoying to generate, we need an arena for the conversion... - try (Arena arena = Arena.ofConfined()) { - MemorySegment stringMemorySegment = arena.allocateFrom(string); - - return (MemorySegment) mh$.invokeExact(stringMemorySegment, string.length()); - } - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - /** - * {@snippet lang = swift: - * func _swift_getTypeByMangledNameInEnvironment( - * _ name: UnsafePointer, - * _ nameLength: UInt, - * genericEnvironment: UnsafeRawPointer?, - * genericArguments: UnsafeRawPointer? - * ) -> Any.Type? - *} - */ - private static class swift_getTypeByMangledNameInEnvironment { - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /*returns=*/SwiftValueLayout.SWIFT_POINTER, - ValueLayout.ADDRESS, - ValueLayout.JAVA_INT, - ValueLayout.ADDRESS, - ValueLayout.ADDRESS - ); - - public static final MemorySegment ADDR = findOrThrow("swift_getTypeByMangledNameInEnvironment"); - - 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 Optional getTypeByMangledNameInEnvironment(String mangledName) { - System.out.println("Get Any.Type for mangled name: " + mangledName); - - var mh$ = swift_getTypeByMangledNameInEnvironment.HANDLE; - try { - // 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("swift_getTypeByMangledNameInEnvironment", mangledName); - } - try (Arena arena = Arena.ofConfined()) { - MemorySegment stringMemorySegment = arena.allocateFrom(mangledName); - - var memorySegment = (MemorySegment) mh$.invokeExact(stringMemorySegment, mangledName.length(), MemorySegment.NULL, MemorySegment.NULL); - - 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$); - } - } - - /** - * Produce the name of the Swift type given its Swift type metadata. - *

- * 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 swift_getTypeByMangledNameInEnvironment} call - */ - public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualified) { - 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$); - } - } - - /*** - * Namespace for calls down into swift-java generated thunks and accessors, such as {@code swiftjava_getType_...} etc. - *

Not intended to be used by end-user code directly, but used by swift-java generated Java code. - */ - @SuppressWarnings("unused") // used by source generated Java code - public static final class swiftjava { - private swiftjava() { /* just a namespace */ } - - private static class getType { - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /* -> */ValueLayout.ADDRESS); - } - - public static MemorySegment getType(String moduleName, String nominalName) { - // We cannot cache this statically since it depends on the type names we're looking up - // TODO: we could cache the handles per type once we have them, to speed up subsequent calls - String symbol = "swiftjava_getType_" + moduleName + "_" + nominalName; - - try { - var addr = findOrThrow(symbol); - var mh$ = Linker.nativeLinker().downcallHandle(addr, getType.DESC); - return (MemorySegment) mh$.invokeExact(); - } catch (Throwable e) { - throw new AssertionError("Failed to call: " + symbol, e); - } - } - } - - // ==== ------------------------------------------------------------------------------------------------------------ - // Get Swift values out of native memory segments - - /** - * 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 long getSwiftInt(MemorySegment memorySegment, long offset) { - if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { - return memorySegment.get(ValueLayout.JAVA_LONG, offset); - } else { - return memorySegment.get(ValueLayout.JAVA_INT, offset); - } - } - - public static long getSwiftInt(MemorySegment memorySegment, VarHandle handle) { - if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { - return (long) handle.get(memorySegment, 0); - } else { - return (int) handle.get(memorySegment, 0); - } - } - - /** - * Get the method handle of a functional interface. - * - * @param fi functional interface. - * @param name name of the single abstraction method. - * @param fdesc function descriptor of the method. - * @return unbound method handle. - */ - public 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); - } - } - - /** - * Convert String to a MemorySegment filled with the C string. - */ - public static MemorySegment toCString(String str, Arena arena) { - return arena.allocateFrom(str); - } - - private static class swift_getTypeName { - - /** - * Descriptor for the swift_getTypeName runtime function. - */ - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /* -> */MemoryLayout.structLayout( - SwiftValueLayout.SWIFT_POINTER.withName("utf8Chars"), - SwiftValueLayout.SWIFT_INT.withName("length") - ), - ValueLayout.ADDRESS, - ValueLayout.JAVA_BOOLEAN - ); - - /** - * 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); - } - -} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftLibraries.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftLibraries.java new file mode 100644 index 00000000..8babdd5a --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftLibraries.java @@ -0,0 +1,79 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitcore; + +import org.swift.swiftkitcore.util.PlatformUtils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; + +public class SwiftLibraries { + + public static final String STDLIB_DYLIB_NAME = "swiftCore"; + public static final String SWIFTKITSWIFT_DYLIB_NAME = "SwiftKitSwift"; + public static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); + + private static final String STDLIB_MACOS_DYLIB_PATH = "/usr/lib/swift/libswiftCore.dylib"; + + @SuppressWarnings("unused") + private static final boolean INITIALIZED_LIBS = loadLibraries(false); + + public static boolean loadLibraries(boolean loadSwiftKit) { + System.loadLibrary(STDLIB_DYLIB_NAME); + if (loadSwiftKit) { + System.loadLibrary(SWIFTKITSWIFT_DYLIB_NAME); + } + return true; + } + + // ==== ------------------------------------------------------------------------------------------------------------ + // Loading libraries + + public static void loadLibrary(String libname) { + // TODO: avoid concurrent loadResource calls; one load is enough esp since we cause File IO when we do that + try { + // try to load a dylib from our classpath, e.g. when we included it in our jar + loadResourceLibrary(libname); + } catch (UnsatisfiedLinkError | RuntimeException e) { + // fallback to plain system path loading + System.loadLibrary(libname); + } + } + + public static void loadResourceLibrary(String libname) { + String resourceName = PlatformUtils.dynamicLibraryName(libname); + if (SwiftLibraries.TRACE_DOWNCALLS) { + System.out.println("[swift-java] Loading resource library: " + resourceName); + } + + try (InputStream libInputStream = SwiftLibraries.class.getResourceAsStream("/" + resourceName)) { + if (libInputStream == null) { + throw new RuntimeException("Expected library '" + libname + "' ('" + resourceName + "') was not found as resource!"); + } + + // TODO: we could do an in memory file system here + File tempFile = File.createTempFile(libname, ""); + tempFile.deleteOnExit(); + Files.copy(libInputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + + System.load(tempFile.getAbsolutePath()); + } catch (IOException e) { + throw new RuntimeException("Failed to load dynamic library '" + libname + "' ('" + resourceName + "') as resource!", e); + } + } +} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValue.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValue.java index 0af529ce..707e998a 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValue.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValue.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkitffm; +package org.swift.swiftkitcore; /** * Represent a wrapper around a Swift value object. e.g. {@code struct} or {@code enum}. diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueLayout.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueLayout.java deleted file mode 100644 index e1d3efb0..00000000 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueLayout.java +++ /dev/null @@ -1,64 +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.swiftkitffm; - -import java.lang.foreign.AddressLayout; -import java.lang.foreign.MemoryLayout; -import java.lang.foreign.SequenceLayout; -import java.lang.foreign.ValueLayout; - -import static java.lang.foreign.ValueLayout.*; - -/** - * 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 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; - - // FIXME: this sequence layout is a workaround, we must properly size pointers when we get them. - public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS - .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE)); - public static final SequenceLayout SWIFT_BYTE_ARRAY = MemoryLayout.sequenceLayout(8, ValueLayout.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) ? - SWIFT_INT32 : SWIFT_INT64; - - /** - * 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/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueWitnessTable.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueWitnessTable.java deleted file mode 100644 index 4e566538..00000000 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftValueWitnessTable.java +++ /dev/null @@ -1,290 +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.swiftkitffm; - -import java.lang.foreign.*; -import java.lang.invoke.*; - -import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static org.swift.swiftkitffm.SwiftKit.getSwiftInt; - -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"); - - - /** - * 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 address - * 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. - */ - static final long $stride$offset = - $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("stride")); - - /** - * Variable handle for the "stride" field within the value witness table. - */ - static final VarHandle $stride$mh = - $LAYOUT.varHandle(MemoryLayout.PathElement.groupElement("stride")); - - /** - * 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 - */ - 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 - */ - public static long alignmentOfSwiftType(MemorySegment typeMetadata) { - long flags = getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$flags$offset); - return (flags & 0xFF) + 1; - } - - /** - * 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 $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 = - $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("destroy")); - - static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - ValueLayout.ADDRESS, // witness table functions expect a pointer to self pointer - ValueLayout.ADDRESS // pointer to the witness table - ); - - /** - * Function pointer for the destroy operation - */ - static MemorySegment addr(SwiftAnyType ty) { - // 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(SwiftAnyType ty) { - return Linker.nativeLinker().downcallHandle(addr(ty), DESC); - } - } - - - /** - * Destroy the value/object. - *

- * This includes deallocating the Swift managed memory for the object. - */ - public static void destroy(SwiftAnyType type, MemorySegment object) { - var fullTypeMetadata = fullTypeMetadata(type.$memorySegment()); - var wtable = valueWitnessTable(fullTypeMetadata); - - var mh = destroy.handle(type); - - try { - mh.invokeExact(object, wtable); - } catch (Throwable th) { - throw new AssertionError("Failed to destroy '" + type + "' at " + object, th); - } - } - - /** - * {@snippet lang = C: - * /// T *(*initializeWithCopy)(T *dest, T *src, M *self); - * /// - * /// Given an invalid object of this type, initialize it as a copy of - * /// the source object. Returns the dest object. - * FUNCTION_VALUE_WITNESS(initializeWithCopy, - * InitializeWithCopy, - * MUTABLE_VALUE_TYPE, - * (MUTABLE_VALUE_TYPE, MUTABLE_VALUE_TYPE, TYPE_TYPE)) - *} - */ - private static class initializeWithCopy { - - static final long $offset = - $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("initializeWithCopy")); - - static final FunctionDescriptor DESC = FunctionDescriptor.of( - /* -> */ ValueLayout.ADDRESS, // returns the destination object - ValueLayout.ADDRESS, // destination - ValueLayout.ADDRESS, // source - ValueLayout.ADDRESS // pointer to the witness table - ); - - /** - * Function pointer for the initializeWithCopy operation - */ - static MemorySegment addr(SwiftAnyType ty) { - // Get the value witness table of the type - final var vwt = SwiftValueWitnessTable.valueWitnessTable(ty.$memorySegment()); - - // Get the address of the function stored at the offset of the witness table - long funcAddress = getSwiftInt(vwt, initializeWithCopy.$offset); - return MemorySegment.ofAddress(funcAddress); - } - - static MethodHandle handle(SwiftAnyType ty) { - return Linker.nativeLinker().downcallHandle(addr(ty), DESC); - } - } - - - /** - * Given an invalid object of this type, initialize it as a copy of - * the source object. - *

- * Returns the dest object. - */ - public static MemorySegment initializeWithCopy(SwiftAnyType type, MemorySegment dest, MemorySegment src) { - var fullTypeMetadata = fullTypeMetadata(type.$memorySegment()); - var wtable = valueWitnessTable(fullTypeMetadata); - - var mh = initializeWithCopy.handle(type); - - try { - return (MemorySegment) mh.invokeExact(dest, src, wtable); - } catch (Throwable th) { - throw new AssertionError("Failed to initializeWithCopy '" + type + "' (" + dest + ", " + src + ")", th); - } - } - -} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValue.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/WrongThreadException.java similarity index 74% rename from SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValue.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/WrongThreadException.java index 0af529ce..bc217d66 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValue.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/WrongThreadException.java @@ -12,10 +12,10 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkitffm; +package org.swift.swiftkitcore; -/** - * Represent a wrapper around a Swift value object. e.g. {@code struct} or {@code enum}. - */ -public interface SwiftValue { +public class WrongThreadException extends RuntimeException { + public WrongThreadException(String message) { + super(message); + } } diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/util/PlatformUtils.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/util/PlatformUtils.java index 7577dee4..36714889 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/util/PlatformUtils.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/util/PlatformUtils.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkitffm.util; +package org.swift.swiftkitcore.util; public class PlatformUtils { public static boolean isLinux() { diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AllocatingSwiftArena.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AllocatingSwiftArena.java new file mode 100644 index 00000000..e42f82dc --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AllocatingSwiftArena.java @@ -0,0 +1,21 @@ +package org.swift.swiftkitffm; + +import org.swift.swiftkitcore.ClosableSwiftArena; +import org.swift.swiftkitcore.ConfinedSwiftMemorySession; +import org.swift.swiftkitcore.SwiftArena; + +import java.lang.foreign.MemorySegment; +import java.util.concurrent.ThreadFactory; + +public interface AllocatingSwiftArena extends SwiftArena { + MemorySegment allocate(long byteSize, long byteAlignment); + + static ClosableAllocatingSwiftArena ofConfined() { + return new FFMConfinedSwiftMemorySession(Thread.currentThread()); + } + +// static SwiftArena ofAuto() { +// ThreadFactory cleanerThreadFactory = r -> new Thread(r, "AutoSwiftArenaCleanerThread"); +// return new AutoSwiftMemorySession(cleanerThreadFactory); +// } +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java index 7b61c2dd..82a3cfc5 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java @@ -14,6 +14,9 @@ package org.swift.swiftkitffm; +import org.swift.swiftkitcore.SwiftInstance; +import org.swift.swiftkitcore.SwiftInstanceCleanup; + import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; import java.lang.ref.Cleaner; @@ -26,7 +29,7 @@ *

When registered Java wrapper classes around native Swift instances {@link SwiftInstance}, * are eligible for collection, this will trigger the cleanup of the native resources as well. * - *

This memory session is LESS reliable than using a {@link ConfinedSwiftMemorySession} because + *

This memory session is LESS reliable than using a {@link FFMConfinedSwiftMemorySession} because * the timing of when the native resources are cleaned up is somewhat undefined, and rely on the * system GC. Meaning, that if an object nas been promoted to an old generation, there may be a * long time between the resource no longer being referenced "in Java" and its native memory being released, @@ -37,7 +40,7 @@ * *

Whenever possible, prefer using an explicitly managed {@link SwiftArena}, such as {@link SwiftArena#ofConfined()}. */ -final class AutoSwiftMemorySession implements SwiftArena { +final class AutoSwiftMemorySession implements AllocatingSwiftArena { private final Arena arena; private final Cleaner cleaner; @@ -51,12 +54,8 @@ public AutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) { public void register(SwiftInstance instance) { Objects.requireNonNull(instance, "value"); - // We're doing this dance to avoid keeping a strong reference to the value itself - var statusDestroyedFlag = instance.$statusDestroyedFlag(); - Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); - - MemorySegment resource = instance.$memorySegment(); - var cleanupAction = new SwiftInstanceCleanup(resource, instance.$swiftType(), markAsDestroyed); + // TODO: Captures warning??? + var cleanupAction = new SwiftInstanceCleanup(instance); cleaner.register(instance, cleanupAction); } @@ -64,22 +63,4 @@ public void register(SwiftInstance instance) { public MemorySegment allocate(long byteSize, long byteAlignment) { return arena.allocate(byteSize, byteAlignment); } -} - -/** - * interface AllocatingSwiftArena implements SwiftArena extends SegmentAllocator {} - */ - -/** - * .init(long x, AllocatingSwiftArena arena) // FFM - */ - -/** - * Remain as a lifetime container. Remove `allocate`, so remove `SegmentAllocator` from `SwiftArena` - */ - -/** - * SwiftArena.ofConfined() { - * - * } - */ \ No newline at end of file +} \ No newline at end of file diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ManagedSwiftType.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableAllocatingSwiftArena.java similarity index 70% rename from SwiftKitCore/src/main/java/org/swift/swiftkitcore/ManagedSwiftType.java rename to SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableAllocatingSwiftArena.java index a0b1bb84..3070c991 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ManagedSwiftType.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableAllocatingSwiftArena.java @@ -14,14 +14,9 @@ package org.swift.swiftkitffm; -import java.lang.foreign.MemorySegment; +import org.swift.swiftkitcore.ClosableSwiftArena; -// delete? - -public interface ManagedSwiftType { - /** - * The memory segment of `self` of the managed Swift object/value. - */ - public MemorySegment $memorySegment(); - -} +/** + * Auto-closable version of {@link AllocatingSwiftArena}. + */ +public interface ClosableAllocatingSwiftArena extends ClosableSwiftArena, AllocatingSwiftArena {} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableSwiftArena.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableSwiftArena.java deleted file mode 100644 index e75d38c2..00000000 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableSwiftArena.java +++ /dev/null @@ -1,30 +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.swiftkitffm; - -// Can be moved - -/** - * Auto-closable version of {@link SwiftArena}. - */ -public interface ClosableSwiftArena extends SwiftArena, AutoCloseable { - - /** - * 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(); - -} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ConfinedSwiftMemorySession.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ConfinedSwiftMemorySession.java deleted file mode 100644 index d6944aa7..00000000 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ConfinedSwiftMemorySession.java +++ /dev/null @@ -1,100 +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.swiftkitffm; - -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -// FFMConfinedSwiftMemorySession implements Allocating extends Confined?? - -final class ConfinedSwiftMemorySession implements ClosableSwiftArena { - - final static int CLOSED = 0; - final static int ACTIVE = 1; - - final Thread owner; - final AtomicInteger state; - - final Arena arena; - final ConfinedResourceList resources; - - public ConfinedSwiftMemorySession(Thread owner) { - this.owner = owner; - this.state = new AtomicInteger(ACTIVE); - this.resources = new ConfinedResourceList(); - - this.arena = Arena.ofConfined(); - } - - 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("SwiftArena is already closed!"); - } - } - - @Override - public void close() { - checkValid(); - - // Cleanup all resources - if (this.state.compareAndExchange(ACTIVE, CLOSED) == ACTIVE) { - this.resources.runCleanup(); - } // else, was already closed; do nothing - - this.arena.close(); - } - - @Override - public void register(SwiftInstance instance) { - checkValid(); - - var statusDestroyedFlag = instance.$statusDestroyedFlag(); - Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); - - var cleanup = new SwiftInstanceCleanup( - instance.$memorySegment(), - instance.$swiftType(), - markAsDestroyed); - this.resources.add(cleanup); - } - - @Override - public MemorySegment allocate(long byteSize, long byteAlignment) { - return arena.allocate(byteSize, byteAlignment); - } - - static final class ConfinedResourceList implements SwiftResourceList { - // TODO: Could use intrusive linked list to avoid one indirection here - final List resourceCleanups = new LinkedList<>(); - - void add(SwiftInstanceCleanup cleanup) { - resourceCleanups.add(cleanup); - } - - @Override - public void runCleanup() { - for (SwiftInstanceCleanup cleanup : resourceCleanups) { - cleanup.run(); - } - resourceCleanups.clear(); - } - } -} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMConfinedSwiftMemorySession.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMConfinedSwiftMemorySession.java new file mode 100644 index 00000000..74d7eb4c --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMConfinedSwiftMemorySession.java @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import org.swift.swiftkitcore.ConfinedSwiftMemorySession; + +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + +final class FFMConfinedSwiftMemorySession extends ConfinedSwiftMemorySession implements AllocatingSwiftArena, ClosableAllocatingSwiftArena { + final Arena arena; + + public FFMConfinedSwiftMemorySession(Thread owner) { + super(owner); + this.arena = Arena.ofConfined(); + } + + @Override + public void close() { + super.close(); + this.arena.close(); + } + + @Override + public MemorySegment allocate(long byteSize, long byteAlignment) { + return arena.allocate(byteSize, byteAlignment); + } +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstance.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstance.java new file mode 100644 index 00000000..5a00daa9 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstance.java @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// +// 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.swiftkitffm; + +import org.swift.swiftkitcore.SwiftArena; +import org.swift.swiftkitcore.SwiftInstance; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemorySegment; +import java.util.concurrent.atomic.AtomicBoolean; + +public abstract class FFMSwiftInstance extends SwiftInstance { + /** + * The pointer to the instance in memory. I.e. the {@code self} of the Swift object or value. + */ + public final MemorySegment $memorySegment() { + return MemorySegment.ofAddress(this.pointer()); + } + + /** + * The Swift type metadata of this type. + */ + public abstract SwiftAnyType $swiftType(); + + /** + * The designated constructor of any imported Swift types. + * + * @param segment the memory segment. + * @param arena the arena this object belongs to. When the arena goes out of scope, this value is destroyed. + */ + protected FFMSwiftInstance(MemorySegment segment, AllocatingSwiftArena arena) { + super(segment.address(), arena); + } + + @Override + public void destroy() { + System.out.println("[debug] Destroy swift value [" + $swiftType().getSwiftName() + "]: " + $memorySegment()); + + // In FFM we allocate on the Java-side, so we can just call destroy. + SwiftValueWitnessTable.destroy(this.$swiftType(), this.$memorySegment()); + } +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ManagedSwiftType.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ManagedSwiftType.java deleted file mode 100644 index a0b1bb84..00000000 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ManagedSwiftType.java +++ /dev/null @@ -1,27 +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.swiftkitffm; - -import java.lang.foreign.MemorySegment; - -// delete? - -public interface ManagedSwiftType { - /** - * The memory segment of `self` of the managed Swift object/value. - */ - public MemorySegment $memorySegment(); - -} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java index 47f90ab8..4a270821 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java @@ -17,8 +17,6 @@ import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; -// move to FFM - public class MemorySegmentUtils { /** * Set the value of `target` to the {@link MemorySegment#address()} of the `memorySegment`, diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.java index 028fa5ab..0698c026 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.java @@ -18,8 +18,6 @@ import java.lang.foreign.MemoryLayout; import java.lang.foreign.MemorySegment; -// move to FFM, if we don't abstract the type away. - public final class SwiftAnyType { private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( @@ -48,7 +46,7 @@ public SwiftAnyType(MemorySegment memorySegment) { * Get the human-readable Swift type name of this type. */ public String getSwiftName() { - return SwiftKit.nameOfSwiftType(memorySegment, true); + return SwiftFFM.nameOfSwiftType(memorySegment, true); } @Override diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftArena.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftArena.java deleted file mode 100644 index 48b64628..00000000 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftArena.java +++ /dev/null @@ -1,62 +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.swiftkitffm; - -import java.lang.foreign.SegmentAllocator; -import java.util.concurrent.ThreadFactory; - -/** - * 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 ClosableSwiftArena#close()}. - */ -public interface SwiftArena extends SegmentAllocator { - - static ClosableSwiftArena ofConfined() { - return new ConfinedSwiftMemorySession(Thread.currentThread()); - } - - static SwiftArena ofAuto() { - ThreadFactory cleanerThreadFactory = r -> new Thread(r, "AutoSwiftArenaCleanerThread"); - return new AutoSwiftMemorySession(cleanerThreadFactory); - } - - /** - * Register a Swift object. - * Its memory should be considered managed by this arena, and be destroyed when the arena is closed. - */ - void register(SwiftInstance instance); - -} - -/** - * Represents a list of resources that need a cleanup, e.g. allocated classes/structs. - */ -interface SwiftResourceList { - - void runCleanup(); - -} - - -final class UnexpectedRetainCountException extends RuntimeException { - 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/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftKit.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java similarity index 89% rename from SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftKit.java rename to SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java index 302dabf8..c7b80ff8 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftKit.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java @@ -14,16 +14,14 @@ package org.swift.swiftkitffm; +import org.swift.swiftkitcore.SwiftHeapObject; +import org.swift.swiftkitcore.util.PlatformUtils; import org.swift.swiftkitffm.util.PlatformUtils; -import java.io.File; -import java.io.IOException; import java.lang.foreign.*; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.Arrays; import java.util.Optional; import java.util.stream.Collectors; @@ -31,10 +29,7 @@ import static org.swift.swiftkitffm.util.StringUtils.stripPrefix; import static org.swift.swiftkitffm.util.StringUtils.stripSuffix; -// rename to SwiftLibraries that has all the library loading -// move all the other stuff to FFM, SwiftFFM class - -public class SwiftKit { +public class SwiftFFM { public static final String STDLIB_DYLIB_NAME = "swiftCore"; public static final String SWIFTKITSWIFT_DYLIB_NAME = "SwiftKitSwift"; @@ -69,7 +64,7 @@ private static SymbolLookup getSymbolLookup() { } } - public SwiftKit() { + public SwiftFFM() { } public static void traceDowncall(Object... args) { @@ -112,42 +107,6 @@ public static boolean getJextractTraceDowncalls() { return Boolean.getBoolean("jextract.trace.downcalls"); } - // ==== ------------------------------------------------------------------------------------------------------------ - // Loading libraries - - public static void loadLibrary(String libname) { - // TODO: avoid concurrent loadResource calls; one load is enough esp since we cause File IO when we do that - try { - // try to load a dylib from our classpath, e.g. when we included it in our jar - loadResourceLibrary(libname); - } catch (UnsatisfiedLinkError | RuntimeException e) { - // fallback to plain system path loading - System.loadLibrary(libname); - } - } - - public static void loadResourceLibrary(String libname) { - var resourceName = PlatformUtils.dynamicLibraryName(libname); - if (SwiftKit.TRACE_DOWNCALLS) { - System.out.println("[swift-java] Loading resource library: " + resourceName); - } - - try (var libInputStream = SwiftKit.class.getResourceAsStream("/" + resourceName)) { - if (libInputStream == null) { - throw new RuntimeException("Expected library '" + libname + "' ('" + resourceName + "') was not found as resource!"); - } - - // TODO: we could do an in memory file system here - var tempFile = File.createTempFile(libname, ""); - tempFile.deleteOnExit(); - Files.copy(libInputStream, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - - System.load(tempFile.getAbsolutePath()); - } catch (IOException e) { - throw new RuntimeException("Failed to load dynamic library '" + libname + "' ('" + resourceName + "') as resource!", e); - } - } - // ==== ------------------------------------------------------------------------------------------------------------ // free diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftHeapObject.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftHeapObject.java deleted file mode 100644 index e9e92863..00000000 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftHeapObject.java +++ /dev/null @@ -1,33 +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.swiftkitffm; - -import java.lang.foreign.MemorySegment; - -// keep in core as a marker protocol - -/** - * Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}. - */ -public interface SwiftHeapObject { - MemorySegment $memorySegment(); - - /** - * Pointer to the instance. - */ - public default MemorySegment $instance() { - return this.$memorySegment().get(SwiftValueLayout.SWIFT_POINTER, 0); - } -} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstance.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstance.java deleted file mode 100644 index 351c46c4..00000000 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstance.java +++ /dev/null @@ -1,94 +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.swiftkitffm; - -import java.lang.foreign.GroupLayout; -import java.lang.foreign.MemorySegment; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Maybe have the SwiftInstance require a `destroy` method that the cleanup calls. - * Interface instead of superclass - */ - -public abstract class SwiftInstance { - /// Pointer to the "self". - private final MemorySegment selfMemorySegment; - - /** - * The pointer to the instance in memory. I.e. the {@code self} of the Swift object or value. - */ - public final MemorySegment $memorySegment() { - return this.selfMemorySegment; - } - - // TODO: make this a flagset integer and/or use a field updater - /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ - private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); - - /** - * Exposes a boolean value which can be used to indicate if the object was destroyed. - *

- * This is exposing the object, rather than performing the action because we don't want to accidentally - * form a strong reference to the {@code SwiftInstance} which could prevent the cleanup from running, - * if using an GC managed instance (e.g. using an {@link AutoSwiftMemorySession}. - */ - public final AtomicBoolean $statusDestroyedFlag() { - return this.$state$destroyed; - } - - /** - * The in memory layout of an instance of this Swift type. - */ - public abstract GroupLayout $layout(); - - /** - * The Swift type metadata of this type. - */ - public abstract SwiftAnyType $swiftType(); - - /** - * The designated constructor of any imported Swift types. - * - * @param segment the memory segment. - * @param arena the arena this object belongs to. When the arena goes out of scope, this value is destroyed. - */ - protected SwiftInstance(MemorySegment segment, SwiftArena arena) { - this.selfMemorySegment = segment; - arena.register(this); - } - - /** - * Ensures that this instance has not been destroyed. - *

- * If this object has been destroyed, calling this method will cause an {@link IllegalStateException} - * to be thrown. This check should be performed before accessing {@code $memorySegment} to prevent - * use-after-free errors. - */ - protected final void $ensureAlive() { - if (this.$state$destroyed.get()) { - throw new IllegalStateException("Attempted to call method on already destroyed instance of " + getClass().getSimpleName() + "!"); - } - } - - /** - * 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. - */ - public boolean isReferenceType() { - return this instanceof SwiftHeapObject; - } -} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstanceCleanup.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstanceCleanup.java deleted file mode 100644 index 5837afad..00000000 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftInstanceCleanup.java +++ /dev/null @@ -1,38 +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.swiftkitffm; - -import java.lang.foreign.MemorySegment; - -/** - * A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. - */ -record SwiftInstanceCleanup( - MemorySegment selfPointer, - SwiftAnyType selfType, - Runnable markAsDestroyed -) implements Runnable { - - @Override - public void run() { - markAsDestroyed.run(); - - // Allow null pointers just for AutoArena tests. - if (selfType != null && selfPointer != null) { - System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer); - SwiftValueWitnessTable.destroy(selfType, selfPointer); - } - } -} \ No newline at end of file diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueWitnessTable.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueWitnessTable.java index 4e566538..c5086569 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueWitnessTable.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueWitnessTable.java @@ -18,7 +18,7 @@ import java.lang.invoke.*; import static java.lang.foreign.ValueLayout.JAVA_BYTE; -import static org.swift.swiftkitffm.SwiftKit.getSwiftInt; +import static org.swift.swiftkitffm.SwiftFFM.getSwiftInt; public abstract class SwiftValueWitnessTable { @@ -156,7 +156,7 @@ public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { return MemoryLayout.structLayout( layouts - ).withName(SwiftKit.nameOfSwiftType(typeMetadata, true)); + ).withName(SwiftFFM.nameOfSwiftType(typeMetadata, true)); } diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/PlatformUtils.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/PlatformUtils.java deleted file mode 100644 index 7577dee4..00000000 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/PlatformUtils.java +++ /dev/null @@ -1,53 +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.swiftkitffm.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"); - } - - 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"); - } - - public static String dynamicLibraryName(String base) { - if (isLinux()) { - return "lib" + uppercaseFistLetter(base) + ".so"; - } else { - return "lib" + uppercaseFistLetter(base) + ".dylib"; - } - } - - static String uppercaseFistLetter(String base) { - if (base == null || base.isEmpty()) { - return base; - } - return base.substring(0, 1).toUpperCase() + base.substring(1); - } -} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/StringUtils.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/StringUtils.java deleted file mode 100644 index dee04820..00000000 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/util/StringUtils.java +++ /dev/null @@ -1,36 +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.swiftkitffm.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; - } - - public static String hexString(long number) { - return String.format("0x%02x", number); - } - -} From 3c12d2d373ab19283ee8a61afbb0edec570d3c78 Mon Sep 17 00:00:00 2001 From: Mads Odgaard Date: Fri, 4 Jul 2025 09:39:02 +0200 Subject: [PATCH 3/3] avoid capturing instance in cleanup actions --- .../ConfinedSwiftMemorySession.java | 2 +- .../swift/swiftkitcore/JNISwiftInstance.java | 28 +++++++++++++++++++ .../swiftkitcore/JNISwiftInstanceCleanup.java | 28 +++++++++++++++++++ .../org/swift/swiftkitcore/SwiftInstance.java | 4 ++- .../swiftkitcore/SwiftInstanceCleanup.java | 17 +---------- .../swiftkitffm/AllocatingSwiftArena.java | 8 +++--- .../swiftkitffm/AutoSwiftMemorySession.java | 9 +++--- .../swift/swiftkitffm/FFMSwiftInstance.java | 15 ++++++---- .../swiftkitffm/FFMSwiftInstanceCleanup.java | 28 +++++++++++++++++++ .../java/org/swift/swiftkitffm/SwiftFFM.java | 1 - 10 files changed, 108 insertions(+), 32 deletions(-) create mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkitcore/JNISwiftInstance.java create mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkitcore/JNISwiftInstanceCleanup.java create mode 100644 SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstanceCleanup.java diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java index 16a142d1..b3eb8f25 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java @@ -58,7 +58,7 @@ public void close() { public void register(SwiftInstance instance) { checkValid(); - SwiftInstanceCleanup cleanup = new SwiftInstanceCleanup(instance); + SwiftInstanceCleanup cleanup = instance.makeCleanupAction(); this.resources.add(cleanup); } diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/JNISwiftInstance.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/JNISwiftInstance.java new file mode 100644 index 00000000..47a93ad9 --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/JNISwiftInstance.java @@ -0,0 +1,28 @@ +package org.swift.swiftkitcore; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class JNISwiftInstance extends SwiftInstance { + /** + * The designated constructor of any imported Swift types. + * + * @param pointer a pointer to the memory containing the value + * @param arena the arena this object belongs to. When the arena goes out of scope, this value is destroyed. + */ + protected JNISwiftInstance(long pointer, SwiftArena arena) { + super(pointer, arena); + } + + @Override + public SwiftInstanceCleanup makeCleanupAction() { + final AtomicBoolean statusDestroyedFlag = $statusDestroyedFlag(); + Runnable markAsDestroyed = new Runnable() { + @Override + public void run() { + statusDestroyedFlag.set(true); + } + }; + + return new JNISwiftInstanceCleanup(this.getClass(), this.pointer(), markAsDestroyed); + } +} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/JNISwiftInstanceCleanup.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/JNISwiftInstanceCleanup.java new file mode 100644 index 00000000..96754350 --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/JNISwiftInstanceCleanup.java @@ -0,0 +1,28 @@ +package org.swift.swiftkitcore; + +import java.lang.reflect.Method; + +class JNISwiftInstanceCleanup implements SwiftInstanceCleanup { + private final Class clazz; + private final long selfPointer; + private final Runnable markAsDestroyed; + + public JNISwiftInstanceCleanup(Class clazz, long selfPointer, Runnable markAsDestroyed) { + this.clazz = clazz; + this.selfPointer = selfPointer; + this.markAsDestroyed = markAsDestroyed; + } + + @Override + public void run() { + markAsDestroyed.run(); + + try { + // Use reflection to look for the static destroy method on the wrapper. + Method method = clazz.getDeclaredMethod("destroy", long.class); + method.invoke(null, selfPointer); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java index 3c414215..eff862aa 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java @@ -29,8 +29,10 @@ public final long pointer() { /** * Called when the arena has decided the value should be destroyed. + *

+ * Warning: The cleanup action must not capture {@code this}. */ - public abstract void destroy(); + public abstract SwiftInstanceCleanup makeCleanupAction(); // TODO: make this a flagset integer and/or use a field updater /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java index b131a181..328f9699 100644 --- a/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java @@ -17,19 +17,4 @@ /** * A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. */ -public final class SwiftInstanceCleanup implements Runnable { - // TODO: Should this be a weak reference? - private final SwiftInstance swiftInstance; - - public SwiftInstanceCleanup(SwiftInstance swiftInstance) { - this.swiftInstance = swiftInstance; - } - - @Override - public void run() { - swiftInstance.$statusDestroyedFlag().set(true); - - // System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer); - swiftInstance.destroy(); - } -} \ No newline at end of file +public interface SwiftInstanceCleanup extends Runnable {} \ No newline at end of file diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AllocatingSwiftArena.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AllocatingSwiftArena.java index e42f82dc..9e600b9f 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AllocatingSwiftArena.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AllocatingSwiftArena.java @@ -14,8 +14,8 @@ static ClosableAllocatingSwiftArena ofConfined() { return new FFMConfinedSwiftMemorySession(Thread.currentThread()); } -// static SwiftArena ofAuto() { -// ThreadFactory cleanerThreadFactory = r -> new Thread(r, "AutoSwiftArenaCleanerThread"); -// return new AutoSwiftMemorySession(cleanerThreadFactory); -// } + static AllocatingSwiftArena ofAuto() { + ThreadFactory cleanerThreadFactory = r -> new Thread(r, "AutoSwiftArenaCleanerThread"); + return new AllocatingAutoSwiftMemorySession(cleanerThreadFactory); + } } diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java index 82a3cfc5..d711f2e9 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java @@ -40,12 +40,12 @@ * *

Whenever possible, prefer using an explicitly managed {@link SwiftArena}, such as {@link SwiftArena#ofConfined()}. */ -final class AutoSwiftMemorySession implements AllocatingSwiftArena { +final class AllocatingAutoSwiftMemorySession implements AllocatingSwiftArena { private final Arena arena; private final Cleaner cleaner; - public AutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) { + public AllocatingAutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) { this.cleaner = Cleaner.create(cleanerThreadFactory); this.arena = Arena.ofAuto(); } @@ -54,8 +54,9 @@ public AutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) { public void register(SwiftInstance instance) { Objects.requireNonNull(instance, "value"); - // TODO: Captures warning??? - var cleanupAction = new SwiftInstanceCleanup(instance); + // We make sure we don't capture `instance` in the + // cleanup action, so we can ignore the warning below. + var cleanupAction = instance.makeCleanupAction(); cleaner.register(instance, cleanupAction); } diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstance.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstance.java index 5a00daa9..8813461c 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstance.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstance.java @@ -16,6 +16,7 @@ import org.swift.swiftkitcore.SwiftArena; import org.swift.swiftkitcore.SwiftInstance; +import org.swift.swiftkitcore.SwiftInstanceCleanup; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemorySegment; @@ -45,10 +46,14 @@ protected FFMSwiftInstance(MemorySegment segment, AllocatingSwiftArena arena) { } @Override - public void destroy() { - System.out.println("[debug] Destroy swift value [" + $swiftType().getSwiftName() + "]: " + $memorySegment()); - - // In FFM we allocate on the Java-side, so we can just call destroy. - SwiftValueWitnessTable.destroy(this.$swiftType(), this.$memorySegment()); + public SwiftInstanceCleanup makeCleanupAction() { + var statusDestroyedFlag = $statusDestroyedFlag(); + Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); + + return new FFMSwiftInstanceCleanup( + $memorySegment(), + $swiftType(), + markAsDestroyed + ); } } diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstanceCleanup.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstanceCleanup.java new file mode 100644 index 00000000..4d7ad187 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstanceCleanup.java @@ -0,0 +1,28 @@ +package org.swift.swiftkitffm; + +import org.swift.swiftkitcore.SwiftInstanceCleanup; + +import java.lang.foreign.MemorySegment; + +public class FFMSwiftInstanceCleanup implements SwiftInstanceCleanup { + private final MemorySegment selfPointer; + private final SwiftAnyType selfType; + private final Runnable markAsDestroyed; + + public FFMSwiftInstanceCleanup(MemorySegment selfPointer, SwiftAnyType selfType, Runnable markAsDestroyed) { + this.selfPointer = selfPointer; + this.selfType = selfType; + this.markAsDestroyed = markAsDestroyed; + } + + @Override + public void run() { + markAsDestroyed.run(); + + // Allow null pointers just for AutoArena tests. + if (selfType != null && selfPointer != null) { + System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer); + SwiftValueWitnessTable.destroy(selfType, selfPointer); + } + } +} diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java index c7b80ff8..4e7ad14a 100644 --- a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java @@ -16,7 +16,6 @@ import org.swift.swiftkitcore.SwiftHeapObject; import org.swift.swiftkitcore.util.PlatformUtils; -import org.swift.swiftkitffm.util.PlatformUtils; import java.lang.foreign.*; import java.lang.invoke.MethodHandle;