From 49d8ffae0e891ad91fbd34f77aa6ab63414f4d77 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Wed, 15 Jan 2025 22:22:58 +0900 Subject: [PATCH 1/5] Prototyping Swift Array Accessor --- .../MySwiftLibrary+ManualThunks.swift | 68 ++++++++++ .../MySwiftLibrary/MySwiftLibrary.swift | 35 ++++- .../com/example/swift/HelloJava2Swift.java | 62 ++++++--- .../example/swift/ManualImportedMethods.java | 70 ++++++++++ .../org/swift/swiftkit/SwiftArrayTest.java | 62 +++++++++ .../Swift2JavaTranslator+Printing.swift | 15 ++- Sources/JExtractSwift/Swift2JavaVisitor.swift | 8 +- .../SwiftKitSwift/SwiftArray+Support.swift | 27 ++++ Sources/SwiftKitSwift/SwiftKit.swift | 19 ++- .../org/swift/swiftkit/SwiftArrayRef.java | 127 ++++++++++++++++++ .../java/org/swift/swiftkit/SwiftKit.java | 4 +- .../MethodImportTests.swift | 11 +- 12 files changed, 471 insertions(+), 37 deletions(-) create mode 100644 Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary+ManualThunks.swift create mode 100644 Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualImportedMethods.java create mode 100644 Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArrayTest.java create mode 100644 Sources/SwiftKitSwift/SwiftArray+Support.swift create mode 100644 SwiftKit/src/main/java/org/swift/swiftkit/SwiftArrayRef.java diff --git a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary+ManualThunks.swift b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary+ManualThunks.swift new file mode 100644 index 00000000..694ad729 --- /dev/null +++ b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary+ManualThunks.swift @@ -0,0 +1,68 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import SwiftKitSwift + +@_cdecl("swiftjava_manual_getArrayMySwiftClass") +public func swiftjava_manual_getArrayMySwiftClass() -> UnsafeMutableRawPointer /* [MySwiftClass] */ { + p("[thunk] swiftjava_manual_getArrayMySwiftClass") + var array: [MySwiftClass] = getArrayMySwiftClass() + p("[thunk] swiftjava_manual_getArrayMySwiftClass -> \(array)") + // TODO: we need to retain it I guess as we escape it into Java + var ptr: UnsafeRawBufferPointer! + array.withUnsafeBytes { + ptr = $0 + } + p("[thunk] swiftjava_manual_getArrayMySwiftClass -> \(ptr)") + + return UnsafeMutableRawPointer(mutating: ptr!.baseAddress)! +} + +@_cdecl("swiftjava_SwiftKitSwift_Array_count") // FIXME: hardcoded for MySwiftClass +public func swiftjava_SwiftKitSwift_Array____count( + rawPointer: UnsafeMutableRawPointer, // Array + elementType: UnsafeMutableRawPointer // Metadata of T +) -> Int { + print("[swift][\(#fileID):\(#line)](\(#function) passed in rawPointer = \(rawPointer)") + print("[swift][\(#fileID):\(#line)](\(#function) passed in metadata = \(elementType)") + + let array = rawPointer.assumingMemoryBound(to: [MySwiftClass].self) + .pointee + + print("[swift][\(#fileID):\(#line)](\(#function) ARRAY count = \(array.count)") + print("[swift][\(#fileID):\(#line)](\(#function) ARRAY[0] = \(unsafeBitCast(array[0], to: UInt64.self))") + return array.count +} + +@_cdecl("swiftjava_SwiftKitSwift_Array_get") // FIXME: hardcoded for MySwiftClass +public func swiftjava_SwiftKitSwift_Array____get( + rawPointer: UnsafeMutableRawPointer, // Array + index: Int, + elementType: UnsafeMutableRawPointer // Metadata of T +) -> UnsafeMutableRawPointer { + print("[swift][\(#fileID):\(#line)](\(#function) passed in rawPointer = \(rawPointer)") + print("[swift][\(#fileID):\(#line)](\(#function) passed in index = \(index)") + print("[swift][\(#fileID):\(#line)](\(#function) passed in metadata = \(elementType)") + + let array: UnsafeMutableBufferPointer = UnsafeMutableBufferPointer( + start: rawPointer.assumingMemoryBound(to: MySwiftClass.self), + count: 999 // FIXME: we need this to be passed in + ) + + print("[swift][\(#fileID):\(#line)](\(#function) ARRAY[\(index)] = \(unsafeBitCast(array[index], to: UInt64.self))") + let object = array[index] + + let objectPointer = unsafeBitCast(object, to: UnsafeMutableRawPointer.self) + return _swiftjava_swift_retain(object: objectPointer) +} diff --git a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift index ddb14569..a5be2cc8 100644 --- a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -35,6 +35,10 @@ public func globalMakeInt() -> Int { return 42 } +public func getMySwiftClassUntyped(as: T.Type) -> Any { + return MySwiftClass(len: 1, cap: 2) +} + public func globalWriteString(string: String) -> Int { return string.count } @@ -47,6 +51,20 @@ public func globalCallMeRunnable(run: () -> ()) { run() } +public func getArrayInt() -> [Int] { + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +} + +let DATA = [ + MySwiftClass(len: 1, cap: 11), + MySwiftClass(len: 2, cap: 22), + MySwiftClass(len: 3, cap: 33), + ] + +public func getArrayMySwiftClass() -> [MySwiftClass] { + DATA +} + public class MySwiftClass { public var len: Int @@ -64,7 +82,7 @@ public class MySwiftClass { deinit { let addr = unsafeBitCast(self, to: UInt64.self) - p("Deinit, self = 0x\(String(addr, radix: 16, uppercase: true))") + p("MySwiftClass.deinit, self = 0x\(String(addr, radix: 16, uppercase: true))") } public var counter: Int32 = 0 @@ -77,6 +95,15 @@ public class MySwiftClass { p("i:\(i)") } + // TODO: workaround until we expose properties again + public func getterForLen() -> Int { + len + } + // TODO: workaround until we expose properties again + public func getterForCap() -> Int { + cap + } + public func echoIntMethod(i: Int) -> Int { p("i:\(i)") return i @@ -99,9 +126,9 @@ public class MySwiftClass { // ==== Internal helpers -private func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) { -// print("[swift][\(file):\(line)](\(function)) \(msg)") -// fflush(stdout) +package func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) { + print("[swift][\(file):\(line)](\(function)) \(msg)") + fflush(stdout) } #if os(Linux) 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 1fafaf0c..908964ad 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/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.SwiftArrayRef; import org.swift.swiftkit.SwiftKit; -import org.swift.swiftkit.SwiftValueWitnessTable; - -import java.util.Arrays; public class HelloJava2Swift { @@ -38,26 +32,58 @@ public static void main(String[] args) { } static void examples() { - MySwiftLibrary.helloWorld(); +// MySwiftLibrary.helloWorld(); +// +// MySwiftLibrary.globalTakeInt(1337); +// +// // Example of using an arena; MyClass.deinit is run at end of scope +// try (var arena = SwiftArena.ofConfined()) { +// MySwiftClass obj = new MySwiftClass(arena, 2222, 7777); +// +// // just checking retains/releases work +// SwiftKit.retain(obj.$memorySegment()); +// SwiftKit.release(obj.$memorySegment()); +// +// obj.voidMethod(); +// obj.takeIntMethod(42); +// } - MySwiftLibrary.globalTakeInt(1337); + // public func getArrayMySwiftClass() -> [MySwiftClass] + SwiftArrayRef arr = ManualImportedMethods.getArrayMySwiftClass(); - // Example of using an arena; MyClass.deinit is run at end of scope - try (var arena = SwiftArena.ofConfined()) { - MySwiftClass obj = new MySwiftClass(arena, 2222, 7777); + MySwiftClass first = arr.get(0, MySwiftClass::new); + System.out.println("[java] first = " + first); - // just checking retains/releases work - SwiftKit.retain(obj.$memorySegment()); - SwiftKit.release(obj.$memorySegment()); + // FIXME: properties don't work yet, need the thunks! +// System.out.println("[java] first.getLen() = " + first.getLen()); +// assert(first.getLen() == 1); +// System.out.println("[java] first.getCap() = " + first.getCap()); +// assert(first.getCap() == 2); + + System.out.println("[java] first.getterForLen() = " + first.getterForLen()); + System.out.println("[java] first.getForCap() = " + first.getterForCap()); + precondition(1, first.getterForLen()); + precondition(11, first.getterForCap()); + + MySwiftClass second = arr.get(1, MySwiftClass::new); + System.out.println("[java] second = " + second); + System.out.println("[java] second.getterForLen() = " + second.getterForLen()); + System.out.println("[java] second.getForCap() = " + second.getterForCap()); + precondition(2, second.getterForLen()); + precondition(22, second.getterForCap()); - obj.voidMethod(); - obj.takeIntMethod(42); - } System.out.println("DONE."); } + private static void precondition(long expected, long got) { + if (expected != got) { + throw new AssertionError("Expected '" + expected + "', but got '" + got + "'!"); + } + } + public static native long jniWriteString(String str); public static native long jniGetInt(); } + diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualImportedMethods.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualImportedMethods.java new file mode 100644 index 00000000..8c4bf853 --- /dev/null +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualImportedMethods.java @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// 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 com.example.swift; + +import org.swift.swiftkit.SwiftArrayRef; +import org.swift.swiftkit.SwiftKit; + +import java.lang.foreign.Arena; +import java.lang.foreign.FunctionDescriptor; +import java.lang.foreign.Linker; +import java.lang.foreign.MemorySegment; +import java.lang.invoke.MethodHandle; + +import static org.swift.swiftkit.SwiftValueLayout.SWIFT_POINTER; + +public final class ManualImportedMethods { + static final String LIB_NAME = "MySwiftLibrary"; + + @SuppressWarnings("unused") + private static final boolean INITIALIZED_LIBS = initializeLibs(); + static boolean initializeLibs() { + System.loadLibrary(SwiftKit.STDLIB_DYLIB_NAME); + System.loadLibrary("SwiftKitSwift"); + System.loadLibrary(LIB_NAME); + return true; + } + + private static class getArrayMySwiftClass { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /* -> */SWIFT_POINTER + ); + public static final MemorySegment ADDR = + SwiftKit.findOrThrow("swiftjava_manual_getArrayMySwiftClass"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + + public static SwiftArrayRef getArrayMySwiftClass() { + MethodHandle mh = getArrayMySwiftClass.HANDLE; + + Arena arena = Arena.ofAuto(); + try { + if (SwiftKit.TRACE_DOWNCALLS) { + SwiftKit.traceDowncall(); + } + + MemorySegment arrayPointer = (MemorySegment) mh.invokeExact(); + return new SwiftArrayRef<>( + arena, + arrayPointer, + /* element type = */MySwiftClass.TYPE_METADATA + ); + } catch (Throwable e) { + throw new RuntimeException("Failed to invoke Swift method", e); + } + } +} diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArrayTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArrayTest.java new file mode 100644 index 00000000..093ba62d --- /dev/null +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArrayTest.java @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import com.example.swift.ManualImportedMethods; +import com.example.swift.MySwiftClass; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class SwiftArrayTest { + + @BeforeAll + public static void setUp() { + SwiftKit.loadLibrary("swiftCore"); + SwiftKit.loadLibrary("SwiftKitSwift"); + SwiftKit.loadLibrary("MySwiftLibrary"); + } + + @Test + public void array_of_MySwiftClass_get() { + try (var arena = SwiftArena.ofConfined()) { + SwiftArrayRef arr = ManualImportedMethods.getArrayMySwiftClass(); + + MySwiftClass first = arr.get(0, MySwiftClass::new); + System.out.println("[java] first = " + first); + + // FIXME: properties don't work yet, need the thunks! +// System.out.println("[java] first.getLen() = " + first.getLen()); +// assert(first.getLen() == 1); +// System.out.println("[java] first.getCap() = " + first.getCap()); +// assert(first.getCap() == 2); + + System.out.println("[java] first.getterForLen() = " + first.getterForLen()); + System.out.println("[java] first.getForCap() = " + first.getterForCap()); + assertEquals(1, first.getterForLen()); + assertEquals(11, first.getterForCap()); + + MySwiftClass second = arr.get(1, MySwiftClass::new); + System.out.println("[java] second = " + second); + System.out.println("[java] second.getterForLen() = " + second.getterForLen()); + System.out.println("[java] second.getForCap() = " + second.getterForCap()); + assertEquals(2, second.getterForLen()); + assertEquals(22, second.getterForCap()); + + } + } +} + diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 61d7e981..46818556 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -303,7 +303,7 @@ extension Swift2JavaTranslator { printer.print( """ - static MemorySegment findOrThrow(String symbol) { + public static MemorySegment findOrThrow(String symbol) { return SYMBOL_LOOKUP.find(symbol) .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); } @@ -344,7 +344,7 @@ extension Swift2JavaTranslator { // https://bugs.openjdk.org/browse/JDK-8311090 printer.print( """ - static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); + public static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); private static SymbolLookup getSymbolLookup() { // Ensure Swift and our Lib are loaded during static initialization of the class. SwiftKit.loadLibrary("swiftCore"); @@ -479,6 +479,17 @@ extension Swift2JavaTranslator { ) { let descClassIdentifier = renderDescClassName(decl) + printer.print( + """ + /** + * Wrap a memory segment which is pointing to an instance of {@code \(parentName.unqualifiedJavaTypeName)}. + */ + public \(parentName.unqualifiedJavaTypeName)(MemorySegment self) { + this.selfMemorySegment = self; + } + """ + ) + printer.print( """ /** diff --git a/Sources/JExtractSwift/Swift2JavaVisitor.swift b/Sources/JExtractSwift/Swift2JavaVisitor.swift index 44138018..66f3d795 100644 --- a/Sources/JExtractSwift/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwift/Swift2JavaVisitor.swift @@ -108,6 +108,11 @@ final class Swift2JavaVisitor: SyntaxVisitor { let fullName = "\(node.name.text)" + guard !fullName.hasPrefix("swiftjava_") else { + self.log.debug("Skip swiftjava_ thunk method during importing: \(fullName)") + return .skipChildren + } + let funcDecl = ImportedFunc( module: self.translator.swiftModuleName, decl: node.trimmed, @@ -170,7 +175,8 @@ final class Swift2JavaVisitor: SyntaxVisitor { log.debug("Record variable in \(currentTypeName)") translator.importedTypes[currentTypeName]!.variables.append(varDecl) } else { - fatalError("Global variables are not supported yet: \(node.debugDescription)") + log.warning("Global variables are not supported yet: \(node.debugDescription)") + return .skipChildren } return .skipChildren diff --git a/Sources/SwiftKitSwift/SwiftArray+Support.swift b/Sources/SwiftKitSwift/SwiftArray+Support.swift new file mode 100644 index 00000000..46a39aa5 --- /dev/null +++ b/Sources/SwiftKitSwift/SwiftArray+Support.swift @@ -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 +// +//===----------------------------------------------------------------------===// + +import SwiftKitSwift + +//@_cdecl("swiftjava_SwiftKitSwift_Array_count") +//public func swiftjava_SwiftKitSwift_Array_Int_count( +// array: UnsafeMutableRawPointer, // Array +// elementType: UnsafeMutableRawPointer // Metadata of T +//) -> Int { +// print("[swift][\(#fileID):\(#line)](\(#function) passed in array = \(array)") +// print("[swift][\(#fileID):\(#line)](\(#function) passed in metadata = \(elementType)") +// let array = unsafeBitCast(array, to: Array.self) +// print("[swift][\(#fileID):\(#line)](\(#function) ARRAY count = \(array.count)") +// return array.count +//} diff --git a/Sources/SwiftKitSwift/SwiftKit.swift b/Sources/SwiftKitSwift/SwiftKit.swift index 38b3c1c8..e2e767a3 100644 --- a/Sources/SwiftKitSwift/SwiftKit.swift +++ b/Sources/SwiftKitSwift/SwiftKit.swift @@ -32,13 +32,12 @@ public func _swiftjava_swift_retainCount(object: UnsafeMutableRawPointer) -> Int @_silgen_name("swift_isUniquelyReferenced") public func _swiftjava_swift_isUniquelyReferenced(object: UnsafeMutableRawPointer) -> Bool - - @_alwaysEmitIntoClient @_transparent - internal func _swiftjava_withHeapObject( - of object: AnyObject, - _ body: (UnsafeMutableRawPointer) -> R - ) -> R { - defer { _fixLifetime(object) } - let unmanaged = Unmanaged.passUnretained(object) - return body(unmanaged.toOpaque()) - } +@_alwaysEmitIntoClient @_transparent +internal func _swiftjava_withHeapObject( + of object: AnyObject, + _ body: (UnsafeMutableRawPointer) -> R +) -> R { + defer { _fixLifetime(object) } + let unmanaged = Unmanaged.passUnretained(object) + return body(unmanaged.toOpaque()) +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArrayRef.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArrayRef.java new file mode 100644 index 00000000..c9cd04a1 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArrayRef.java @@ -0,0 +1,127 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.util.function.Function; + +import static org.swift.swiftkit.SwiftValueLayout.SWIFT_INT; +import static org.swift.swiftkit.SwiftValueLayout.SWIFT_POINTER; + +public class SwiftArrayRef { + + private final MemorySegment self$; + private final SwiftAnyType wrappedType; + + public SwiftArrayRef(Arena arena, + MemorySegment selfMemorySegment, + SwiftAnyType wrappedSwiftTypeMetadata) { + this.self$ = selfMemorySegment; + this.wrappedType = wrappedSwiftTypeMetadata; + } + + public final MemorySegment $memorySegment() { + return this.self$; + } + + @SuppressWarnings("unused") + private static final boolean INITIALIZED_LIBS = initializeLibs(); + + static boolean initializeLibs() { + System.loadLibrary(SwiftKit.STDLIB_DYLIB_NAME); + System.loadLibrary("SwiftKitSwift"); + return true; + } + + // ==== ------------------------------------------------------------------------ + // count + + private static class count { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /* -> */SWIFT_INT, + SWIFT_POINTER, // Array + SWIFT_POINTER // metadata pointer: Element + ); + public static final MemorySegment ADDR = + SwiftKit.findOrThrow("swiftjava_SwiftKitSwift_Array_count"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + + public int size() { + var mh$ = count.HANDLE; + try { + if (SwiftKit.TRACE_DOWNCALLS) { + SwiftKit.traceDowncall(self$); + } + // A Swift array has `Int` length which is a Java long potentially, + // however it won't be so large so we will to-int-convert it... + var count = (long) mh$.invokeExact( + self$, // the array + wrappedType.$memorySegment() // the T metadata + ); + + return Math.toIntExact(count); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + // ==== ------------------------------------------------------------------------ + // get + + private static class get { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /* -> */SWIFT_POINTER, + SWIFT_POINTER, // Array + SWIFT_INT, // index: Int + SWIFT_POINTER // metadata pointer: Element + ); + public static final MemorySegment ADDR = + SwiftKit.findOrThrow("swiftjava_SwiftKitSwift_Array_get"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + + public Wrapped get(long index, Function wrap) { + var mh$ = get.HANDLE; + try { + if (SwiftKit.TRACE_DOWNCALLS) { + SwiftKit.traceDowncall(self$); + } + // A Swift array has `Int` length which is a Java long potentially, + // however it won't be so large so we will to-int-convert it... + var pointer = (MemorySegment) mh$.invokeExact( + self$, // the array + index, + wrappedType.$memorySegment() // the T metadata + ); + + return wrap.apply(pointer); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + // ==== ------------------------------------------------------------------------ + // swap + + public Wrapped swap(int i, int j) { + throw new RuntimeException("Not implemented"); + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index 858f5500..34ac70b9 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -95,7 +95,7 @@ public static void trace(Object... args) { traceArgs); } - static MemorySegment findOrThrow(String symbol) { + public static MemorySegment findOrThrow(String symbol) { return SYMBOL_LOOKUP.find(symbol) .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); } @@ -112,6 +112,7 @@ public static boolean getJextractTraceDowncalls() { // Loading libraries public static void loadLibrary(String libname) { + System.out.println("[swiftjava] Loading library: " + 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 @@ -289,6 +290,7 @@ private static class swift_getTypeByName { } public static MemorySegment getTypeByName(String string) { + System.out.println("[java] get type by name: " + string); var mh$ = swift_getTypeByName.HANDLE; try { if (TRACE_DOWNCALLS) { diff --git a/Tests/JExtractSwiftTests/MethodImportTests.swift b/Tests/JExtractSwiftTests/MethodImportTests.swift index 67c7c210..ff55efd9 100644 --- a/Tests/JExtractSwiftTests/MethodImportTests.swift +++ b/Tests/JExtractSwiftTests/MethodImportTests.swift @@ -34,6 +34,8 @@ final class MethodImportTests { // MANGLED NAME: $s14MySwiftLibrary13globalTakeInt1iySi_tF public func globalTakeInt(i: Int) + public func getSwiftIntArray() -> [Int] + // MANGLED NAME: $s14MySwiftLibrary23globalTakeLongIntString1l3i321sys5Int64V_s5Int32VSStF public func globalTakeIntLongString(i32: Int32, l: Int64, s: String) @@ -379,7 +381,7 @@ final class MethodImportTests { return (long) makeInt($memorySegment()); } """ - ) + )al } @Test @@ -404,6 +406,12 @@ final class MethodImportTests { output, expected: """ + /** + * Wrap a memory segment which is pointing to an instance of {@code MySwiftClass}. + */ + public MySwiftClass(MemorySegment self) { + this.selfMemorySegment = self; + } /** * Create an instance of {@code MySwiftClass}. * @@ -439,4 +447,5 @@ final class MethodImportTests { """ ) } + } From 807155567d8d74783c83d186869f392c56d433a1 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Fri, 17 Jan 2025 11:14:49 +0900 Subject: [PATCH 2/5] jextract visitor: allow importing struct decl --- Sources/JExtractSwift/Swift2JavaVisitor.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Sources/JExtractSwift/Swift2JavaVisitor.swift b/Sources/JExtractSwift/Swift2JavaVisitor.swift index 66f3d795..0b1ff105 100644 --- a/Sources/JExtractSwift/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwift/Swift2JavaVisitor.swift @@ -55,6 +55,22 @@ final class Swift2JavaVisitor: SyntaxVisitor { } } + override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind { + guard let importedNominalType = translator.importedNominalType(node) else { + return .skipChildren + } + + currentTypeName = importedNominalType.swiftTypeName + return .visitChildren + } + + override func visitPost(_ node: StructDeclSyntax) { + if currentTypeName != nil { + log.debug("Completed import: \(node.kind) \(node.name)") + currentTypeName = nil + } + } + override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind { // Resolve the extended type of the extension as an imported nominal, and // recurse if we found it. From e54d00e5b4bdc035a23a82883a3b966289b76d19 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Sat, 18 Jan 2025 15:09:24 +0900 Subject: [PATCH 3/5] Import and allow init, returning, destroying Swift `struct`s --- .../MySwiftLibrary/MySwiftLibrary.swift | 12 +++++ .../com/example/swift/HelloJava2Swift.java | 54 +++++++++++-------- .../Convenience/SwiftSyntax+Extensions.swift | 2 +- Sources/JExtractSwift/ImportedDecls.swift | 12 ++++- .../Swift2JavaTranslator+Printing.swift | 31 +++++++++-- .../JExtractSwift/SwiftThunkTranslator.swift | 17 ++++-- Sources/JExtractSwift/TranslatedType.swift | 5 ++ .../swiftkit/AutoSwiftMemorySession.java | 5 +- .../swiftkit/ConfinedSwiftMemorySession.java | 5 +- .../swift/swiftkit/SwiftInstanceCleanup.java | 4 +- .../java/org/swift/swiftkit/SwiftKit.java | 1 + .../java/org/swift/swiftkit/SwiftValue.java | 20 +++++++ .../swiftkit/SwiftValueWitnessTable.java | 50 ++++++++++++++++- 13 files changed, 177 insertions(+), 41 deletions(-) diff --git a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift index a5be2cc8..f419da2f 100644 --- a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -124,6 +124,18 @@ public class MySwiftClass { } } +public struct MySwiftStruct { + public var number: Int + + public init(number: Int) { + self.number = number + } + + public func getTheNumber() -> Int { + number + } +} + // ==== Internal helpers package func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) { 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 908964ad..bf4aac8d 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -17,6 +17,7 @@ // Import swift-extract generated sources // Import javakit/swiftkit support libraries +import org.swift.swiftkit.SwiftArena; import org.swift.swiftkit.SwiftArrayRef; import org.swift.swiftkit.SwiftKit; @@ -48,29 +49,36 @@ static void examples() { // obj.takeIntMethod(42); // } - // public func getArrayMySwiftClass() -> [MySwiftClass] - SwiftArrayRef arr = ManualImportedMethods.getArrayMySwiftClass(); - - MySwiftClass first = arr.get(0, MySwiftClass::new); - System.out.println("[java] first = " + first); - - // FIXME: properties don't work yet, need the thunks! -// System.out.println("[java] first.getLen() = " + first.getLen()); -// assert(first.getLen() == 1); -// System.out.println("[java] first.getCap() = " + first.getCap()); -// assert(first.getCap() == 2); - - System.out.println("[java] first.getterForLen() = " + first.getterForLen()); - System.out.println("[java] first.getForCap() = " + first.getterForCap()); - precondition(1, first.getterForLen()); - precondition(11, first.getterForCap()); - - MySwiftClass second = arr.get(1, MySwiftClass::new); - System.out.println("[java] second = " + second); - System.out.println("[java] second.getterForLen() = " + second.getterForLen()); - System.out.println("[java] second.getForCap() = " + second.getterForCap()); - precondition(2, second.getterForLen()); - precondition(22, second.getterForCap()); +// // public func getArrayMySwiftClass() -> [MySwiftClass] +// SwiftArrayRef arr = ManualImportedMethods.getArrayMySwiftClass(); +// +// MySwiftClass first = arr.get(0, MySwiftClass::new); +// System.out.println("[java] first = " + first); +// +// // FIXME: properties don't work yet, need the thunks! +//// System.out.println("[java] first.getLen() = " + first.getLen()); +//// assert(first.getLen() == 1); +//// System.out.println("[java] first.getCap() = " + first.getCap()); +//// assert(first.getCap() == 2); +// +// System.out.println("[java] first.getterForLen() = " + first.getterForLen()); +// System.out.println("[java] first.getForCap() = " + first.getterForCap()); +// precondition(1, first.getterForLen()); +// precondition(11, first.getterForCap()); +// +// MySwiftClass second = arr.get(1, MySwiftClass::new); +// System.out.println("[java] second = " + second); +// System.out.println("[java] second.getterForLen() = " + second.getterForLen()); +// System.out.println("[java] second.getForCap() = " + second.getterForCap()); +// precondition(2, second.getterForLen()); +// precondition(22, second.getterForCap()); + + try (var arena = SwiftArena.ofConfined()) { + MySwiftStruct struct = new MySwiftStruct(arena, 44); +// long theNumber = struct.getTheNumber(); +// precondition(44, theNumber); + + } System.out.println("DONE."); diff --git a/Sources/JExtractSwift/Convenience/SwiftSyntax+Extensions.swift b/Sources/JExtractSwift/Convenience/SwiftSyntax+Extensions.swift index 0a8f5533..2cfde1c8 100644 --- a/Sources/JExtractSwift/Convenience/SwiftSyntax+Extensions.swift +++ b/Sources/JExtractSwift/Convenience/SwiftSyntax+Extensions.swift @@ -58,7 +58,7 @@ extension TypeSyntax { } } -extension DeclSyntaxProtocol { +extension SyntaxProtocol { var isClass: Bool { return self.is(ClassDeclSyntax.self) } diff --git a/Sources/JExtractSwift/ImportedDecls.swift b/Sources/JExtractSwift/ImportedDecls.swift index 794753aa..f81a9e77 100644 --- a/Sources/JExtractSwift/ImportedDecls.swift +++ b/Sources/JExtractSwift/ImportedDecls.swift @@ -64,6 +64,16 @@ public enum NominalTypeKind { case `class` case `enum` case `struct` + + var isReferenceType: Bool { + switch self { + case .actor, .class: true + case .enum, .struct: false + } + } + var isValueType: Bool { + !isReferenceType + } } public struct ImportedParam { @@ -90,7 +100,7 @@ public struct ImportedParam { var effectiveName: String? { firstName ?? secondName } - + var effectiveValueName: String { secondName ?? firstName ?? "_" } diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 46818556..54ee669a 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -194,7 +194,7 @@ extension Swift2JavaTranslator { printPackage(&printer) printImports(&printer) - printClass(&printer, decl) { printer in + printNominal(&printer, decl) { printer in // Prepare type metadata, we're going to need these when invoking e.g. initializers so cache them in a static. // We call into source swift-java source generated accessors which give us the type of the Swift object: // TODO: seems we no longer need the mangled name per se, so avoiding such constant and downcall @@ -277,11 +277,17 @@ extension Swift2JavaTranslator { printer.print("") } - public func printClass( + public func printNominal( _ printer: inout CodePrinter, _ decl: ImportedNominalType, body: (inout CodePrinter) -> Void ) { - printer.printTypeDecl("public final class \(decl.javaClassName) implements SwiftHeapObject") { - printer in + let baseType: String = + if decl.kind.isReferenceType { + "SwiftHeapObject" + } else { + "SwiftValue" + } + + printer.printTypeDecl("public final class \(decl.javaClassName) implements \(baseType)") { printer in // ==== Storage of the class printClassSelfProperty(&printer, decl) @@ -289,6 +295,11 @@ extension Swift2JavaTranslator { printClassConstants(printer: &printer) printTypeMappingDecls(&printer) + // struct specific funcs + if decl.kind.isValueType { + printValueTypeFuncs(printer: &printer, decl) + } + body(&printer) } } @@ -376,6 +387,18 @@ extension Swift2JavaTranslator { ) } + private func printValueTypeFuncs(printer: inout CodePrinter, _ decl: ImportedNominalType) { + let typeName = decl.javaClassName + printer.print( + """ + public \(typeName) wrap(MemorySegment self) { + return new \(typeName)(self); + } + """ + ) + } + + private func printPrivateConstructor(_ printer: inout CodePrinter, _ typeName: String) { printer.print( """ diff --git a/Sources/JExtractSwift/SwiftThunkTranslator.swift b/Sources/JExtractSwift/SwiftThunkTranslator.swift index c530be3b..7ae0281a 100644 --- a/Sources/JExtractSwift/SwiftThunkTranslator.swift +++ b/Sources/JExtractSwift/SwiftThunkTranslator.swift @@ -86,6 +86,13 @@ struct SwiftThunkTranslator { let thunkName = self.st.thunkNameRegistry.functionThunkName( module: st.swiftModuleName, decl: function) + let maybeRetainedSelf: String = + if parent.isReferenceType { + "_swiftjava_swift_retain(object: self$)" + } else { + "self$" + } + return [ """ @@ -93,7 +100,7 @@ struct SwiftThunkTranslator { public func \(raw: thunkName)(\(raw: st.renderSwiftParamDecls(function, paramPassingStyle: nil))) -> UnsafeMutableRawPointer /* \(raw: parent.swiftTypeName) */ { let _self = \(raw: parent.swiftTypeName)(\(raw: st.renderForwardSwiftParams(function, paramPassingStyle: nil))) let self$ = unsafeBitCast(_self, to: UnsafeMutableRawPointer.self) - return _swiftjava_swift_retain(object: self$) + return \(raw: maybeRetainedSelf) } """ ] @@ -109,7 +116,7 @@ struct SwiftThunkTranslator { } else { "-> \(decl.returnType.cCompatibleSwiftType) /* \(decl.returnType.swiftTypeName) */" } - + // Do we need to pass a self parameter? let paramPassingStyle: SelfParameterVariant? let callBase: String @@ -155,7 +162,7 @@ struct SwiftThunkTranslator { """ ] } - + func adaptArgumentsInThunk(_ decl: ImportedFunc) -> String { var lines: [String] = [] for p in decl.parameters { @@ -165,11 +172,11 @@ struct SwiftThunkTranslator { """ let \(p.effectiveValueName) = String(cString: \(p.effectiveValueName)) """ - + lines += [adaptedType] } } - + return lines.joined(separator: "\n") } } diff --git a/Sources/JExtractSwift/TranslatedType.swift b/Sources/JExtractSwift/TranslatedType.swift index eef4140d..d93b39a9 100644 --- a/Sources/JExtractSwift/TranslatedType.swift +++ b/Sources/JExtractSwift/TranslatedType.swift @@ -236,6 +236,11 @@ public struct TranslatedType { originalSwiftType.trimmedDescription } + var isReferenceType: Bool { + originalSwiftType.isClass || + originalSwiftType.isActor + } + /// Produce the "unqualified" Java type name. var unqualifiedJavaTypeName: String { switch javaType { diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java b/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java index 9d25ad12..e220c27e 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java @@ -62,8 +62,9 @@ void register(SwiftHeapObject object, SwiftHeapObjectCleanup cleanupAction) { @Override public void register(SwiftValue value) { Objects.requireNonNull(value, "value"); - MemorySegment resource = value.$memorySegment(); - var cleanupAction = new SwiftValueCleanup(resource); + MemorySegment selfPointer = value.$memorySegment(); + SwiftAnyType selfType = value.$swiftType(); + var cleanupAction = new SwiftValueCleanup(selfPointer, selfType); cleaner.register(value, cleanupAction); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java b/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java index e727f5db..4b597d13 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java @@ -14,6 +14,7 @@ package org.swift.swiftkit; +import java.lang.foreign.MemorySegment; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -64,7 +65,9 @@ public void register(SwiftHeapObject object) { public void register(SwiftValue value) { checkValid(); - var cleanup = new SwiftValueCleanup(value.$memorySegment()); + MemorySegment selfPointer = value.$memorySegment(); + SwiftAnyType selfType = value.$swiftType(); + var cleanup = new SwiftValueCleanup(selfPointer, selfType); this.resources.add(cleanup); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java index 8ac62793..b0bdaee6 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java @@ -62,9 +62,9 @@ public void run() throws UnexpectedRetainCountException { } } -record SwiftValueCleanup(MemorySegment resource) implements SwiftInstanceCleanup { +record SwiftValueCleanup(MemorySegment selfPointer, SwiftAnyType selfType) implements SwiftInstanceCleanup { @Override public void run() { - throw new RuntimeException("not implemented yet"); + SwiftValueWitnessTable.destroy(selfType, selfPointer); } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index 34ac70b9..b86c0a69 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -198,6 +198,7 @@ public static long retainCount(MemorySegment object) { try { if (TRACE_DOWNCALLS) { traceDowncall("swift_retainCount", object); + throw new RuntimeException("DONT DO THIS"); } return (long) mh$.invokeExact(object); } catch (Throwable ex$) { diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java index 9387fa85..5f3d8c48 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java @@ -14,6 +14,26 @@ package org.swift.swiftkit; +import java.lang.foreign.Arena; +import java.lang.foreign.MemorySegment; + public interface SwiftValue extends SwiftInstance { SwiftAnyType $swiftType(); + + + /** + * Create a copy of the Swift array but keeping the memory managed in Swift native memory. + */ + default SwiftValue copy(Arena arena) { + var layout = SwiftValueWitnessTable.layoutOfSwiftType($swiftType().$memorySegment()); + System.out.println("layout = " + layout); + + MemorySegment target = arena.allocate(layout.byteSize()); + + SwiftValueWitnessTable.initializeWithCopy($swiftType(), $memorySegment(), target); + + return wrap(target); + } + + SwiftValue wrap(MemorySegment self); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java index 826a4fa2..e03c2cc2 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -161,9 +161,12 @@ public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { static final long $flags$offset = $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("flags")); + // ==== ------------------------------------------------------------------------------------------------------------ + // destroy + /** * {@snippet lang = C: - * ///void(*destroy)(T *object, witness_t *self); + * /// 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 @@ -202,7 +205,6 @@ static MethodHandle handle(SwiftAnyType ty) { } } - /** * Destroy the value/object. *

@@ -225,4 +227,48 @@ public static void destroy(SwiftAnyType type, MemorySegment object) { } } + // ==== ------------------------------------------------------------------------------------------------------------ + // initializeWithCopy + + /** + * {@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("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); + } + } + + public static void initializeWithCopy(SwiftAnyType type, MemorySegment from, MemorySegment target) { + + } } From c0dac2cc179aa86ee2baf3417cf7259f6252a264 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 21 Jan 2025 20:13:05 +0900 Subject: [PATCH 4/5] wip --- .../MySwiftLibrary/MySwiftLibrary.swift | 7 ++- .../MySwiftLibrary+ManualThunks.swift | 4 +- .../MySwiftLibrary/MySwiftLibrary.swift | 18 ++++-- .../com/example/swift/HelloJava2Swift.java | 59 +++++++++++-------- .../example/swift/ManualImportedMethods.java | 36 ++++++++++- .../com/example/swift/MySwiftClassTest.java | 26 ++++---- .../org/swift/swiftkit/MySwiftStructTest.java | 35 +++++++++++ ...tArrayTest.java => SwiftArrayRefTest.java} | 6 +- .../java/org/swift/swiftkit/SwiftAnyType.java | 24 ++++++-- .../org/swift/swiftkit/SwiftArrayRef.java | 44 ++++++++++++-- .../swift/swiftkit/SwiftInstanceCleanup.java | 5 +- .../java/org/swift/swiftkit/SwiftKit.java | 13 ++++ .../swiftkit/SwiftValueWitnessTable.java | 25 +++++--- .../MethodImportTests.swift | 4 +- 14 files changed, 234 insertions(+), 72 deletions(-) create mode 100644 Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java rename Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/{SwiftArrayTest.java => SwiftArrayRefTest.java} (93%) diff --git a/Samples/SwiftAndJavaJarSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftAndJavaJarSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift index 84e4618f..42c608e8 100644 --- a/Samples/SwiftAndJavaJarSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftAndJavaJarSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -59,7 +59,12 @@ public class MySwiftClass { p("Deinit, self = 0x\(String(addr, radix: 16, uppercase: true))") } - public var counter: Int32 = 0 + public var counter: Int = 0 + + public func increment(by num: Int) -> Int { + counter += num + return counter + } public func voidMethod() { p("") diff --git a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary+ManualThunks.swift b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary+ManualThunks.swift index 694ad729..57e7ab33 100644 --- a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary+ManualThunks.swift +++ b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary+ManualThunks.swift @@ -30,7 +30,7 @@ public func swiftjava_manual_getArrayMySwiftClass() -> UnsafeMutableRawPointer / } @_cdecl("swiftjava_SwiftKitSwift_Array_count") // FIXME: hardcoded for MySwiftClass -public func swiftjava_SwiftKitSwift_Array____count( +public func swiftjava_SwiftKitSwift_Array_count( rawPointer: UnsafeMutableRawPointer, // Array elementType: UnsafeMutableRawPointer // Metadata of T ) -> Int { @@ -46,7 +46,7 @@ public func swiftjava_SwiftKitSwift_Array____count( } @_cdecl("swiftjava_SwiftKitSwift_Array_get") // FIXME: hardcoded for MySwiftClass -public func swiftjava_SwiftKitSwift_Array____get( +public func swiftjava_SwiftKitSwift_Array_get( rawPointer: UnsafeMutableRawPointer, // Array index: Int, elementType: UnsafeMutableRawPointer // Metadata of T diff --git a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift index f419da2f..449a0e72 100644 --- a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -56,15 +56,25 @@ public func getArrayInt() -> [Int] { } let DATA = [ - MySwiftClass(len: 1, cap: 11), - MySwiftClass(len: 2, cap: 22), - MySwiftClass(len: 3, cap: 33), - ] + MySwiftClass(len: 1, cap: 11), + MySwiftClass(len: 2, cap: 22), + MySwiftClass(len: 3, cap: 33), +] public func getArrayMySwiftClass() -> [MySwiftClass] { DATA } + +let BYTES_DATA: [UInt8] = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, +] + + +public func getByteArray() -> [UInt8] { + BYTES_DATA +} + public class MySwiftClass { public var len: Int 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 bf4aac8d..be596ada 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -17,10 +17,15 @@ // Import swift-extract generated sources // Import javakit/swiftkit support libraries +import org.swift.swiftkit.SwiftAnyType; import org.swift.swiftkit.SwiftArena; import org.swift.swiftkit.SwiftArrayRef; import org.swift.swiftkit.SwiftKit; +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.SequenceLayout; + public class HelloJava2Swift { public static void main(String[] args) { @@ -49,38 +54,40 @@ static void examples() { // obj.takeIntMethod(42); // } -// // public func getArrayMySwiftClass() -> [MySwiftClass] -// SwiftArrayRef arr = ManualImportedMethods.getArrayMySwiftClass(); -// -// MySwiftClass first = arr.get(0, MySwiftClass::new); -// System.out.println("[java] first = " + first); -// -// // FIXME: properties don't work yet, need the thunks! -//// System.out.println("[java] first.getLen() = " + first.getLen()); -//// assert(first.getLen() == 1); -//// System.out.println("[java] first.getCap() = " + first.getCap()); -//// assert(first.getCap() == 2); -// -// System.out.println("[java] first.getterForLen() = " + first.getterForLen()); -// System.out.println("[java] first.getForCap() = " + first.getterForCap()); -// precondition(1, first.getterForLen()); -// precondition(11, first.getterForCap()); -// -// MySwiftClass second = arr.get(1, MySwiftClass::new); -// System.out.println("[java] second = " + second); -// System.out.println("[java] second.getterForLen() = " + second.getterForLen()); -// System.out.println("[java] second.getForCap() = " + second.getterForCap()); -// precondition(2, second.getterForLen()); -// precondition(22, second.getterForCap()); + // public func getArrayMySwiftClass() -> [MySwiftClass] + SwiftArrayRef arr = ManualImportedMethods.getArrayMySwiftClass(); + + precondition(3, arr.count()); + + MySwiftClass first = arr.get(0); + System.out.println("[java] first = " + first); + + // FIXME: properties don't work yet, need the thunks! +// System.out.println("[java] first.getLen() = " + first.getLen()); +// assert(first.getLen() == 1); +// System.out.println("[java] first.getCap() = " + first.getCap()); +// assert(first.getCap() == 2); + + System.out.println("[java] first.getterForLen() = " + first.getterForLen()); + System.out.println("[java] first.getForCap() = " + first.getterForCap()); + precondition(1, first.getterForLen()); + precondition(11, first.getterForCap()); + + MySwiftClass second = arr.get(1); + precondition(2, second.getterForLen()); + precondition(22, second.getterForCap()); try (var arena = SwiftArena.ofConfined()) { MySwiftStruct struct = new MySwiftStruct(arena, 44); -// long theNumber = struct.getTheNumber(); -// precondition(44, theNumber); - + System.out.println("struct.getTheNumber() = " + struct.getTheNumber()); + long theNumber = struct.getTheNumber(); + precondition(44, theNumber); } + arr.get(0); + + System.out.println("DONE."); } diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualImportedMethods.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualImportedMethods.java index 8c4bf853..0db8373a 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualImportedMethods.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualImportedMethods.java @@ -23,6 +23,7 @@ import java.lang.foreign.MemorySegment; import java.lang.invoke.MethodHandle; +import static org.swift.swiftkit.SwiftValueLayout.SWIFT_INT; import static org.swift.swiftkit.SwiftValueLayout.SWIFT_POINTER; public final class ManualImportedMethods { @@ -61,10 +62,43 @@ public static SwiftArrayRef getArrayMySwiftClass() { return new SwiftArrayRef<>( arena, arrayPointer, - /* element type = */MySwiftClass.TYPE_METADATA + /* element type = */MySwiftClass.TYPE_METADATA, + MySwiftClass::new ); } catch (Throwable e) { throw new RuntimeException("Failed to invoke Swift method", e); } } + +// private static class getByteArray { +// public static final FunctionDescriptor DESC = FunctionDescriptor.of( +// /* -> */SWIFT_POINTER, +// /* size */SWIFT_INT +// ); +// public static final MemorySegment ADDR = +// SwiftKit.findOrThrow("swiftjava_manual_getByteArray"); +// +// public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); +// } +// +// public static SwiftPrimiviteArrayRef getByteArray() { +// MethodHandle mh = getByteArray.HANDLE; +// +// Arena arena = Arena.ofAuto(); +// try { +// if (SwiftKit.TRACE_DOWNCALLS) { +// SwiftKit.traceDowncall(); +// } +// +// MemorySegment arrayPointer = (MemorySegment) mh.invokeExact(); +// return new SwiftArrayRef<>( +// arena, +// arrayPointer, +// /* element type = */, +// MySwiftClass::new +// ); +// } catch (Throwable e) { +// throw new RuntimeException("Failed to invoke Swift method", e); +// } +// } } 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 fa17ef1a..4032aa4f 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java @@ -41,12 +41,12 @@ void checkPaths(Throwable throwable) { @Test void test_MySwiftClass_voidMethod() { - try { - MySwiftClass o = new MySwiftClass(12, 42); - o.voidMethod(); - } catch (Throwable throwable) { - checkPaths(throwable); - } +// try { +// MySwiftClass o = new MySwiftClass(12, 42); +// o.voidMethod(); +// } catch (Throwable throwable) { +// checkPaths(throwable); +// } } @Test @@ -56,12 +56,12 @@ void test_MySwiftClass_makeIntMethod() { assertEquals(12, got); } - @Test - @Disabled // TODO: Need var mangled names in interfaces - void test_MySwiftClass_property_len() { - MySwiftClass o = new MySwiftClass(12, 42); - var got = o.getLen(); - assertEquals(12, got); - } +// @Test +// @Disabled // TODO: Implement properties again +// void test_MySwiftClass_property_len() { +// MySwiftClass o = new MySwiftClass(12, 42); +// var got = o.getLen(); +// assertEquals(12, got); +// } } diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java new file mode 100644 index 00000000..8ee398e8 --- /dev/null +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import com.example.swift.MySwiftClass; +import com.example.swift.MySwiftStruct; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MySwiftStructTest { + + @Test + void create_struct() { + try (var arena = SwiftArena.ofConfined()) { + long expectedNumber = 128; + var struct = new MySwiftStruct(arena, expectedNumber); + + long theNumber = struct.getTheNumber(); + assertEquals(expectedNumber, theNumber); + } + } +} diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArrayTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArrayRefTest.java similarity index 93% rename from Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArrayTest.java rename to Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArrayRefTest.java index 093ba62d..99337b04 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArrayTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArrayRefTest.java @@ -21,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class SwiftArrayTest { +public class SwiftArrayRefTest { @BeforeAll public static void setUp() { @@ -35,7 +35,7 @@ public void array_of_MySwiftClass_get() { try (var arena = SwiftArena.ofConfined()) { SwiftArrayRef arr = ManualImportedMethods.getArrayMySwiftClass(); - MySwiftClass first = arr.get(0, MySwiftClass::new); + MySwiftClass first = arr.get(0); System.out.println("[java] first = " + first); // FIXME: properties don't work yet, need the thunks! @@ -49,7 +49,7 @@ public void array_of_MySwiftClass_get() { assertEquals(1, first.getterForLen()); assertEquals(11, first.getterForCap()); - MySwiftClass second = arr.get(1, MySwiftClass::new); + MySwiftClass second = arr.get(1); System.out.println("[java] second = " + second); System.out.println("[java] second.getterForLen() = " + second.getterForLen()); System.out.println("[java] second.getForCap() = " + second.getterForCap()); diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java index cf7cc238..16aa9161 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java @@ -26,11 +26,17 @@ public final class SwiftAnyType { private final MemorySegment memorySegment; - public SwiftAnyType(MemorySegment memorySegment) { -// if (SwiftKit.getSwiftInt(memorySegment, 0) > 0) { -// throw new IllegalArgumentException("A Swift Any.Type cannot be null!"); -// } + public static SwiftAnyType SWIFT_INT = SwiftKit.getTypeByMangledNameInEnvironment("SiSg").get(); + public static SwiftAnyType SWIFT_UINT = SwiftKit.getTypeByMangledNameInEnvironment("SuSg").get(); + public static SwiftAnyType SWIFT_LONG = SwiftKit.getTypeByMangledNameInEnvironment("SiSg").get(); + public static SwiftAnyType SWIFT_BOOL = SwiftKit.getTypeByMangledNameInEnvironment("SbSg").get(); + public static SwiftAnyType SWIFT_DOUBLE = SwiftKit.getTypeByMangledNameInEnvironment("SdSg").get(); + public static SwiftAnyType SWIFT_FLOAT = SwiftKit.getTypeByMangledNameInEnvironment("SfSg").get(); + public static SwiftAnyType SWIFT_UNSAFE_RAW_POINTER = SwiftKit.getTypeByMangledNameInEnvironment("SVSg").get(); + public static SwiftAnyType SWIFT_UNSAFE_MUTABLE_RAW_POINTER = SwiftKit.getTypeByMangledNameInEnvironment("SvSg").get(); + public static SwiftAnyType SWIFT_string = SwiftKit.getTypeByMangledNameInEnvironment("SSg").get(); + public SwiftAnyType(MemorySegment memorySegment) { this.memorySegment = memorySegment.asReadOnly(); } @@ -56,11 +62,19 @@ public SwiftAnyType(SwiftHeapObject object) { 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=" + SwiftKit.nameOfSwiftType(memorySegment, true) + + "name=" + getSwiftName() + ", memorySegment=" + memorySegment + '}'; } + } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArrayRef.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArrayRef.java index c9cd04a1..86410c2f 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArrayRef.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArrayRef.java @@ -25,12 +25,21 @@ public class SwiftArrayRef { private final MemorySegment self$; private final SwiftAnyType wrappedType; + private final Function wrapAsWrapped; + + private GroupLayout ELEMENT_LAYOUT = null; + private SequenceLayout ARRAY_OF_ELEMENT_LAYOUT = null; public SwiftArrayRef(Arena arena, MemorySegment selfMemorySegment, - SwiftAnyType wrappedSwiftTypeMetadata) { + SwiftAnyType wrappedSwiftTypeMetadata, + Function memorySegmentAsWrapped) { this.self$ = selfMemorySegment; this.wrappedType = wrappedSwiftTypeMetadata; + this.wrapAsWrapped = memorySegmentAsWrapped; + + this.ELEMENT_LAYOUT = (GroupLayout) SwiftValueWitnessTable.layoutOfSwiftType(wrappedType.$memorySegment()); + this.ARRAY_OF_ELEMENT_LAYOUT = MemoryLayout.sequenceLayout(this.count(), ELEMENT_LAYOUT); } public final MemorySegment $memorySegment() { @@ -46,6 +55,8 @@ static boolean initializeLibs() { return true; } + + // ==== ------------------------------------------------------------------------ // count @@ -62,7 +73,7 @@ private static class count { } - public int size() { + public int count() { var mh$ = count.HANDLE; try { if (SwiftKit.TRACE_DOWNCALLS) { @@ -75,6 +86,8 @@ public int size() { wrappedType.$memorySegment() // the T metadata ); + System.out.println("got array count = " + count); + return Math.toIntExact(count); } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); @@ -98,21 +111,40 @@ private static class get { } - public Wrapped get(long index, Function wrap) { + public Wrapped get(long index) { var mh$ = get.HANDLE; try { if (SwiftKit.TRACE_DOWNCALLS) { SwiftKit.traceDowncall(self$); } - // A Swift array has `Int` length which is a Java long potentially, - // however it won't be so large so we will to-int-convert it... + + var pointer = (MemorySegment) mh$.invokeExact( + self$, // the array + index, + wrappedType.$memorySegment() // the T metadata + ); + + return this.wrapAsWrapped.apply(pointer); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + + public Wrapped getWithVarHandle(long index) { + var mh$ = get.HANDLE; + try { + if (SwiftKit.TRACE_DOWNCALLS) { + SwiftKit.traceDowncall(self$); + } + + var pointer = (MemorySegment) mh$.invokeExact( self$, // the array index, wrappedType.$memorySegment() // the T metadata ); - return wrap.apply(pointer); + return this.wrapAsWrapped.apply(pointer); } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java index b0bdaee6..461457cf 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java @@ -37,12 +37,12 @@ class SwiftHeapObjectCleanup implements SwiftInstanceCleanup { /** * This constructor on purpose does not just take a {@link SwiftHeapObject} in order to make it very * clear that it does not take ownership of it, but we ONLY manage the native resource here. - * + *

* This is important for {@link AutoSwiftMemorySession} which relies on the wrapper type to be GC-able, * when no longer "in use" on the Java side. */ SwiftHeapObjectCleanup(MemorySegment selfPointer, SwiftAnyType selfType) { - this.selfPointer = selfPointer; + this.selfPointer = selfPointer; this.selfType = selfType; } @@ -65,6 +65,7 @@ public void run() throws UnexpectedRetainCountException { record SwiftValueCleanup(MemorySegment selfPointer, SwiftAnyType selfType) implements SwiftInstanceCleanup { @Override public void run() { + System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer); SwiftValueWitnessTable.destroy(selfType, selfPointer); } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index b86c0a69..e5d8eac7 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.lang.foreign.*; import java.lang.invoke.MethodHandle; +import java.lang.invoke.VarHandle; import java.nio.file.CopyOption; import java.nio.file.FileSystems; import java.nio.file.Files; @@ -440,6 +441,18 @@ public static long getSwiftInt(MemorySegment memorySegment, long offset) { } } + /** + * 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, VarHandle handle) { + if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { + return (long) handle.get(memorySegment, 0); + } else { + return (int) handle.get(memorySegment, 0); + } + } private static class swift_getTypeName { diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java index e03c2cc2..8a092bc2 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -16,6 +16,7 @@ import java.lang.foreign.*; import java.lang.invoke.MethodHandle; +import java.lang.invoke.VarHandle; import static java.lang.foreign.ValueLayout.JAVA_BYTE; import static org.swift.swiftkit.SwiftKit.getSwiftInt; @@ -70,8 +71,7 @@ public static MemorySegment fullTypeMetadata(MemorySegment typeMetadata) { */ public static MemorySegment valueWitnessTable(MemorySegment typeMetadata) { return fullTypeMetadata(typeMetadata) - .get(SwiftValueLayout.SWIFT_POINTER, SwiftValueWitnessTable.fullTypeMetadata$vwt$offset); -// .get(ValueLayout.ADDRESS, SwiftValueWitnessTable.fullTypeMetadata$vwt$offset); + .get(SwiftValueLayout.SWIFT_POINTER, SwiftValueWitnessTable.fullTypeMetadata$vwt$offset); } @@ -81,22 +81,33 @@ public static MemorySegment valueWitnessTable(MemorySegment typeMetadata) { static final long $size$offset = $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("size")); + /** + * Variable handle for the "stride" field within the value witness table. + */ + static final VarHandle $size$mh = + $LAYOUT.varHandle(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); + return getSwiftInt(valueWitnessTable(typeMetadata), $size$mh); } - /** * 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("size")); + /** * 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 @@ -107,7 +118,7 @@ public static long sizeOfSwiftType(MemorySegment typeMetadata) { * @param typeMetadata the memory segment must point to a Swift metadata */ public static long strideOfSwiftType(MemorySegment typeMetadata) { - return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$stride$offset); + return getSwiftInt(valueWitnessTable(typeMetadata), $stride$mh); } @@ -117,7 +128,7 @@ public static long strideOfSwiftType(MemorySegment typeMetadata) { * @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); + long flags = getSwiftInt(valueWitnessTable(typeMetadata), $flags$offset); return (flags & 0xFF) + 1; } @@ -240,7 +251,7 @@ public static void destroy(SwiftAnyType type, MemorySegment object) { * InitializeWithCopy, * MUTABLE_VALUE_TYPE, * (MUTABLE_VALUE_TYPE, MUTABLE_VALUE_TYPE, TYPE_TYPE)) - * } + *} */ private static class initializeWithCopy { static final long $offset = diff --git a/Tests/JExtractSwiftTests/MethodImportTests.swift b/Tests/JExtractSwiftTests/MethodImportTests.swift index ff55efd9..769ae224 100644 --- a/Tests/JExtractSwiftTests/MethodImportTests.swift +++ b/Tests/JExtractSwiftTests/MethodImportTests.swift @@ -121,7 +121,7 @@ final class MethodImportTests { expected: """ /** - * Downcall to Swift: + * Downcall to Swift: * {@snippet lang=swift : * public func globalTakeInt(i: Int) * } @@ -381,7 +381,7 @@ final class MethodImportTests { return (long) makeInt($memorySegment()); } """ - )al + ) } @Test From 2afc1d9a62455a8aa9a48e919c3a2c1140d6f085 Mon Sep 17 00:00:00 2001 From: Konrad `ktoso` Malawski Date: Tue, 21 Jan 2025 21:38:05 +0900 Subject: [PATCH 5/5] correct how we get the unsafe self pointer of a struct --- .../MySwiftLibrary/MySwiftLibrary.swift | 12 ---- .../MySwiftLibrary/MySwiftStruct.swift | 60 +++++++++++++++++++ .../com/example/swift/HelloJava2Swift.java | 17 ++++-- .../JExtractSwift/SwiftThunkTranslator.swift | 25 +++++--- 4 files changed, 88 insertions(+), 26 deletions(-) create mode 100644 Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftStruct.swift diff --git a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift index 449a0e72..1ba305e8 100644 --- a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -134,18 +134,6 @@ public class MySwiftClass { } } -public struct MySwiftStruct { - public var number: Int - - public init(number: Int) { - self.number = number - } - - public func getTheNumber() -> Int { - number - } -} - // ==== Internal helpers package func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) { diff --git a/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftStruct.swift b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftStruct.swift new file mode 100644 index 00000000..e25d332d --- /dev/null +++ b/Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftStruct.swift @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +#if os(Linux) +import Glibc +#else +import Darwin.C +#endif + +public struct MySwiftStruct { + private var numberA: Int + private var numberB: Int + private var numberC: Int + private var numberD: Int + + public init(number: Int) { + self.numberA = number + self.numberB = number + self.numberC = number + self.numberD = number + } + + public func getTheNumber() -> Int { + numberA + } +} + +public struct MyHugeSwiftStruct { + private var numberA: Int + private var numberB: Int + private var numberC: Int + private var numberD: Int + + private var numberA2: Int = 0 + private var numberB2: Int = 0 + private var numberC2: Int = 0 + private var numberD2: Int = 0 + + public init(number: Int) { + self.numberA = number + self.numberB = number + self.numberC = number + self.numberD = number + } + + public func getTheNumber() -> Int { + numberA + } +} 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 be596ada..94055e9c 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -57,7 +57,7 @@ static void examples() { // public func getArrayMySwiftClass() -> [MySwiftClass] SwiftArrayRef arr = ManualImportedMethods.getArrayMySwiftClass(); - precondition(3, arr.count()); + // precondition(3, arr.count()); MySwiftClass first = arr.get(0); System.out.println("[java] first = " + first); @@ -78,10 +78,17 @@ static void examples() { precondition(22, second.getterForCap()); try (var arena = SwiftArena.ofConfined()) { - MySwiftStruct struct = new MySwiftStruct(arena, 44); - System.out.println("struct.getTheNumber() = " + struct.getTheNumber()); - long theNumber = struct.getTheNumber(); - precondition(44, theNumber); + var struct = new MySwiftStruct(arena, 44); +// System.out.println("struct.getTheNumber() = " + struct.getTheNumber()); +// long theNumber = struct.getTheNumber(); +// precondition(44, theNumber); + + System.out.println("struct.$layout() = " + struct.$layout()); + System.out.println("struct.$layout() = " + struct.$layout().byteSize()); + + var huge = new MyHugeSwiftStruct(arena, 44); + System.out.println("huge.$layout() = " + huge.$layout()); + System.out.println("huge.$layout() = " + huge.$layout().byteSize()); } diff --git a/Sources/JExtractSwift/SwiftThunkTranslator.swift b/Sources/JExtractSwift/SwiftThunkTranslator.swift index 7ae0281a..69c39809 100644 --- a/Sources/JExtractSwift/SwiftThunkTranslator.swift +++ b/Sources/JExtractSwift/SwiftThunkTranslator.swift @@ -86,21 +86,27 @@ struct SwiftThunkTranslator { let thunkName = self.st.thunkNameRegistry.functionThunkName( module: st.swiftModuleName, decl: function) - let maybeRetainedSelf: String = - if parent.isReferenceType { - "_swiftjava_swift_retain(object: self$)" - } else { - "self$" - } + let returnSelf: String = + if parent.isReferenceType { + """ + let self$ = unsafeBitCast(_self, to: UnsafeMutableRawPointer.self) + _swiftjava_swift_retain(object: self$) + """ + } else { + """ + // FIXME: Reconsider how else we want to deal with value types being returned to JVM + let self$ = withUnsafePointer(to: &_self) { $0 } + return UnsafeMutableRawPointer(mutating: self$) + """ + } return [ """ @_cdecl("\(raw: thunkName)") public func \(raw: thunkName)(\(raw: st.renderSwiftParamDecls(function, paramPassingStyle: nil))) -> UnsafeMutableRawPointer /* \(raw: parent.swiftTypeName) */ { - let _self = \(raw: parent.swiftTypeName)(\(raw: st.renderForwardSwiftParams(function, paramPassingStyle: nil))) - let self$ = unsafeBitCast(_self, to: UnsafeMutableRawPointer.self) - return \(raw: maybeRetainedSelf) + var _self = \(raw: parent.swiftTypeName)(\(raw: st.renderForwardSwiftParams(function, paramPassingStyle: nil))) + \(raw: returnSelf) } """ ] @@ -123,6 +129,7 @@ struct SwiftThunkTranslator { let callBaseDot: String if let parent = decl.parent { paramPassingStyle = .swiftThunkSelf + // FIXME: for structs callBase = "let self$ = unsafeBitCast(_self, to: \(parent.originalSwiftType).self)" callBaseDot = "self$." } else {