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..5f02a1f5 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.SwiftFFM; public class HelloJava2Swift { @@ -32,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(); } @@ -47,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 bb46ef3d..e7e53224 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.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/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 78% 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..d26d55a5 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; @@ -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/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..8ce83fd5 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,15 @@ // //===----------------------------------------------------------------------===// -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.SwiftFFM.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..8454e5ee 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.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 6c0ceb1e..df7feff1 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.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/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 78% 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..d26d55a5 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; @@ -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/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..f96efe3b 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,16 @@ // //===----------------------------------------------------------------------===// -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.SwiftFFM.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/SwiftKitCore/src/main/java/org/swift/swiftkitcore/AutoSwiftMemorySession.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/AutoSwiftMemorySession.java new file mode 100644 index 00000000..cf9957ef --- /dev/null +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/AutoSwiftMemorySession.java @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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 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 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/SwiftKit/src/main/java/org/swift/swiftkit/ClosableSwiftArena.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ClosableSwiftArena.java similarity index 96% rename from SwiftKit/src/main/java/org/swift/swiftkit/ClosableSwiftArena.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/ClosableSwiftArena.java index c257ae57..09a20b65 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/ClosableSwiftArena.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ClosableSwiftArena.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitcore; /** * Auto-closable version of {@link SwiftArena}. @@ -24,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/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java similarity index 70% rename from SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/ConfinedSwiftMemorySession.java index 86725ae8..b3eb8f25 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.swiftkitcore; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; @@ -20,7 +20,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -final class ConfinedSwiftMemorySession implements ClosableSwiftArena { +public class ConfinedSwiftMemorySession implements ClosableSwiftArena { final static int CLOSED = 0; final static int ACTIVE = 1; @@ -28,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!"); } @@ -53,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 = instance.makeCleanupAction(); 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/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/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftArena.java similarity index 71% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftArena.java index f50025cc..e44ec365 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftArena.java @@ -12,9 +12,8 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +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/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftHeapObject.java similarity index 69% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftHeapObject.java index 89050fb5..16e97b22 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftHeapObject.java @@ -12,20 +12,11 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitcore; -import java.lang.foreign.MemorySegment; /** * Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}. */ public interface SwiftHeapObject { - MemorySegment $memorySegment(); - - /** - * Pointer to the instance. - */ - public default MemorySegment $instance() { - return this.$memorySegment().get(SwiftValueLayout.SWIFT_POINTER, 0); - } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java similarity index 80% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java index 5b8ff700..eff862aa 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstance.java @@ -12,24 +12,28 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +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; 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. + *

+ * Warning: The cleanup action must not capture {@code this}. + */ + 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. */ private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); @@ -45,24 +49,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/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java similarity index 50% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java index a9fdd9c8..328f9699 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/SwiftInstanceCleanup.java @@ -12,27 +12,9 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; - -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 { - - @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); - } - } -} +public interface SwiftInstanceCleanup extends Runnable {} \ No newline at end of file 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/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..707e998a 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.swiftkitcore; /** * 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/ManagedSwiftType.java b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/WrongThreadException.java similarity index 67% rename from SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java rename to SwiftKitCore/src/main/java/org/swift/swiftkitcore/WrongThreadException.java index da25ae4d..bc217d66 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java +++ b/SwiftKitCore/src/main/java/org/swift/swiftkitcore/WrongThreadException.java @@ -12,14 +12,10 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; - -import java.lang.foreign.MemorySegment; - -public interface ManagedSwiftType { - /** - * The memory segment of `self` of the managed Swift object/value. - */ - public MemorySegment $memorySegment(); +package org.swift.swiftkitcore; +public class WrongThreadException extends RuntimeException { + public WrongThreadException(String message) { + super(message); + } } 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..36714889 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.swiftkitcore.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/AllocatingSwiftArena.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AllocatingSwiftArena.java new file mode 100644 index 00000000..9e600b9f --- /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 AllocatingSwiftArena ofAuto() { + ThreadFactory cleanerThreadFactory = r -> new Thread(r, "AutoSwiftArenaCleanerThread"); + return new AllocatingAutoSwiftMemorySession(cleanerThreadFactory); + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java similarity index 78% rename from SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java rename to SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java index ecbe836e..d711f2e9 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/AutoSwiftMemorySession.java @@ -12,7 +12,10 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; + +import org.swift.swiftkitcore.SwiftInstance; +import org.swift.swiftkitcore.SwiftInstanceCleanup; import java.lang.foreign.Arena; import java.lang.foreign.MemorySegment; @@ -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,12 +40,12 @@ * *

Whenever possible, prefer using an explicitly managed {@link SwiftArena}, such as {@link SwiftArena#ofConfined()}. */ -final class AutoSwiftMemorySession implements SwiftArena { +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(); } @@ -51,12 +54,9 @@ 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); + // 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); } @@ -64,4 +64,4 @@ public void register(SwiftInstance instance) { public MemorySegment allocate(long byteSize, long byteAlignment) { return arena.allocate(byteSize, byteAlignment); } -} +} \ No newline at end of file diff --git a/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableAllocatingSwiftArena.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableAllocatingSwiftArena.java new file mode 100644 index 00000000..3070c991 --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/ClosableAllocatingSwiftArena.java @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// 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.ClosableSwiftArena; + +/** + * Auto-closable version of {@link AllocatingSwiftArena}. + */ +public interface ClosableAllocatingSwiftArena extends ClosableSwiftArena, AllocatingSwiftArena {} 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..8813461c --- /dev/null +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/FFMSwiftInstance.java @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// 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 org.swift.swiftkitcore.SwiftInstanceCleanup; + +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 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/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java similarity index 97% rename from SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java rename to SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java index 660a5500..4a270821 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/MemorySegmentUtils.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.MemorySegment; import java.lang.foreign.ValueLayout; diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.java similarity index 94% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java rename to SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.java index 4d13ecb7..0698c026 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftAnyType.java @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; import java.lang.foreign.GroupLayout; import java.lang.foreign.MemoryLayout; @@ -46,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/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java similarity index 88% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java rename to SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java index 9c04eded..4e7ad14a 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftFFM.java @@ -12,28 +12,23 @@ // //===----------------------------------------------------------------------===// -package org.swift.swiftkit; +package org.swift.swiftkitffm; -import org.swift.swiftkit.util.PlatformUtils; +import org.swift.swiftkitcore.SwiftHeapObject; +import org.swift.swiftkitcore.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.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; -public class SwiftKit { +public class SwiftFFM { public static final String STDLIB_DYLIB_NAME = "swiftCore"; public static final String SWIFTKITSWIFT_DYLIB_NAME = "SwiftKitSwift"; @@ -68,7 +63,7 @@ private static SymbolLookup getSymbolLookup() { } } - public SwiftKit() { + public SwiftFFM() { } public static void traceDowncall(Object... args) { @@ -111,42 +106,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/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueLayout.java similarity index 98% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java rename to SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueLayout.java index 00215ef0..e1d3efb0 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/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/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueWitnessTable.java similarity index 98% rename from SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java rename to SwiftKitFFM/src/main/java/org/swift/swiftkitffm/SwiftValueWitnessTable.java index 8ddff1b6..c5086569 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKitFFM/src/main/java/org/swift/swiftkitffm/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.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/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 {