diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 251ba1b3..08ebc7b6 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -57,7 +57,10 @@ jobs: ${{ runner.os }}-swiftpm- # run the actual build - name: Gradle build - run: ./gradlew build --info --no-daemon + run: | + ./gradlew build -x test --no-daemon # skip tests + find . + ./gradlew build --info --no-daemon test-swift: name: Swift tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }}) diff --git a/.gitignore b/.gitignore index f56abda5..43b51002 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,9 @@ BuildLogic/out/ **/build/ lib/ +# Ignore JVM crash logs +**/*.log + # Ignore package resolved Package.resolved @@ -27,3 +30,6 @@ Package.resolved # Cache of project .gradletasknamecache + +# Ignore files generated by jextract, we always can re-generate them +*/**/*.java diff --git a/.licenseignore b/.licenseignore index 5d0e3172..02866b01 100644 --- a/.licenseignore +++ b/.licenseignore @@ -27,12 +27,12 @@ docker/* **/docker-compose.yaml **/docker/* **/.dockerignore -JavaSwiftKitDemo/src/main/java/com/example/swift/generated/* Makefile **/Makefile **/*.html **/CMakeLists.txt **/*.jar +**/generated/*.java **/generated/*.swift gradle/wrapper/gradle-wrapper.properties gradlew diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index 28054047..51dd48b1 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -19,12 +19,11 @@ import com.example.swift.generated.MySwiftClass; // Import javakit/swiftkit support libraries +import org.swift.swiftkit.SwiftArena; import org.swift.swiftkit.SwiftKit; - +import org.swift.swiftkit.SwiftValueWitnessTable; import java.lang.foreign.*; -import java.util.List; - public class HelloJava2Swift { @@ -32,41 +31,24 @@ public static void main(String[] args) { boolean traceDowncalls = Boolean.getBoolean("jextract.trace.downcalls"); System.out.println("Property: jextract.trace.downcalls = " + traceDowncalls); - final var dylibNames = List.of( - "swiftCore", - "JavaKitExample" - ); - - - System.out.println("Loading libraries..."); - - for (var lib : dylibNames) { - System.out.printf("Loading: %s... ", lib); - System.loadLibrary(lib); - System.out.println("ok."); - } + System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path")); examples(); } static void examples() { - ExampleSwiftLibrary.helloWorld(); + ExampleSwiftLibrary.helloWorld(); - ExampleSwiftLibrary.globalTakeInt(1337); + ExampleSwiftLibrary.globalTakeInt(1337); - MySwiftClass obj = new MySwiftClass(2222, 7777); + MySwiftClass obj = new MySwiftClass(2222, 7777); - SwiftKit.retain(obj.$memorySegment()); - System.out.println("[java] obj ref count = " + SwiftKit.retainCount(obj.$memorySegment())); + SwiftKit.retain(obj.$memorySegment()); + System.out.println("[java] obj ref count = " + SwiftKit.retainCount(obj.$memorySegment())); - obj.voidMethod(); - obj.takeIntMethod(42); + obj.voidMethod(); + obj.takeIntMethod(42); - MemorySegment swiftType = SwiftKit.getTypeByMangledNameInEnvironment("SiSg"); - System.out.println("Memory layout for Swift.Int?:"); - System.out.println(" size = " + SwiftKit.sizeOfSwiftType(swiftType)); - System.out.println(" stride = " + SwiftKit.strideOfSwiftType(swiftType)); - System.out.println(" alignment = " + SwiftKit.alignmentOfSwiftType(swiftType)); - System.out.println(" Java layout = " + SwiftKit.layoutOfSwiftType(swiftType)); + System.out.println("DONE."); } } diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java index 0a40f293..9aa63f22 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java @@ -27,7 +27,7 @@ public final class ManualMySwiftClass extends Manual_MySwiftClass implements Man // 000000000003f4a8 S type metadata for JavaKitExample.MySwiftSlice // strip the _$s // drop the N - public static final String TYPE_METADATA_NAME = "14JavaKitExample12MySwiftClassC"; + public static final String TYPE_MANGLED_NAME = "14JavaKitExample12MySwiftClassC"; private final MemorySegment self; @@ -167,13 +167,13 @@ static MemorySegment __allocating_init_len_cap(long len, long cap) { if (ManualJavaKitExample.TRACE_DOWNCALLS) { ManualJavaKitExample.traceDowncall("MySwiftClass.__allocating_init(len:cap:)", len, cap); } - ManualJavaKitExample.trace("type name = " + TYPE_METADATA_NAME); + ManualJavaKitExample.trace("type name = " + TYPE_MANGLED_NAME); // FIXME: problems with _getTypeByName because of the String memory repr - // final MemorySegment type = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_METADATA_NAME); + // final MemorySegment type = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_MANGLED_NAME); // we must get a method we can call like this into SwiftKit: - MemorySegment type = ManualJavaKitExample.swiftkit_getTypeByStringByteArray(TYPE_METADATA_NAME); + MemorySegment type = ManualJavaKitExample.swiftkit_getTypeByStringByteArray(TYPE_MANGLED_NAME); ManualJavaKitExample.trace("type = " + type); var address = (MemorySegment) mh$.invokeExact(len, cap, type); diff --git a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java index 0f20c172..7495ff19 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java @@ -15,6 +15,7 @@ package com.example.swift.generated; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; @@ -48,9 +49,10 @@ void test_MySwiftClass_makeIntMethod() { } @Test + @Disabled // TODO: Need var mangled names in interfaces void test_MySwiftClass_property_len() { MySwiftClass o = new MySwiftClass(12, 42); - var got = o.makeIntMethod(); + var got = o.getLen(); assertEquals(12, got); } diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java index b04da744..1b98d1e0 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java @@ -27,17 +27,14 @@ public class MySwiftClassTest { @BeforeAll static void beforeAll() { - System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path")); - - System.loadLibrary("swiftCore"); - System.loadLibrary("ExampleSwiftLibrary"); - - System.setProperty("jextract.trace.downcalls", "true"); + System.out.printf("java.library.path = %s\n", SwiftKit.getJavaLibraryPath()); + System.out.printf("jextract.trace.downcalls = %s\n", SwiftKit.getJextractTraceDowncalls()); } @Test void call_retain_retainCount_release() { - var obj = new MySwiftClass(1, 2); + var arena = SwiftArena.ofConfined(); + var obj = new MySwiftClass(arena, 1, 2); assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); // TODO: test directly on SwiftHeapObject inheriting obj diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java new file mode 100644 index 00000000..4f4ab9df --- /dev/null +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import com.example.swift.generated.MySwiftClass; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.DisabledIf; +import org.swift.swiftkit.util.PlatformUtils; + +import static org.junit.jupiter.api.Assertions.*; +import static org.swift.swiftkit.SwiftKit.*; +import static org.swift.swiftkit.SwiftKit.retainCount; + +public class SwiftArenaTest { + + @BeforeAll + static void beforeAll() { + System.out.printf("java.library.path = %s\n", SwiftKit.getJavaLibraryPath()); + System.out.printf("jextract.trace.downcalls = %s\n", SwiftKit.getJextractTraceDowncalls()); + } + + static boolean isAmd64() { + return PlatformUtils.isAmd64(); + } + + // FIXME: The destroy witness table call hangs on x86_64 platforms during the destroy witness table call + // See: https://github.com/swiftlang/swift-java/issues/97 + @Test + @DisabledIf("isAmd64") + public void arena_releaseClassOnClose_class_ok() { + try (var arena = SwiftArena.ofConfined()) { + var obj = new MySwiftClass(arena,1, 2); + + retain(obj.$memorySegment()); + assertEquals(2, retainCount(obj.$memorySegment())); + + release(obj.$memorySegment()); + assertEquals(1, retainCount(obj.$memorySegment())); + } + + // TODO: should we zero out the $memorySegment perhaps? + } + + @Test + public void arena_releaseClassOnClose_class_leaked() { + String memorySegmentDescription = ""; + + try { + try (var arena = SwiftArena.ofConfined()) { + var obj = new MySwiftClass(arena,1, 2); + memorySegmentDescription = obj.$memorySegment().toString(); + + // Pretend that we "leaked" the class, something still holds a reference to it while we try to destroy it + retain(obj.$memorySegment()); + assertEquals(2, retainCount(obj.$memorySegment())); + } + + fail("Expected exception to be thrown while the arena is closed!"); + } catch (Exception ex) { + // The message should point out which objects "leaked": + assertTrue(ex.getMessage().contains(memorySegmentDescription)); + } + + } +} diff --git a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift index 4928233e..5ad8e6fb 100644 --- a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift +++ b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift @@ -76,6 +76,15 @@ public class MySwiftClass { } } +@_silgen_name("swift_getTypeByMangledNameInEnvironment") +public func _getTypeByMangledNameInEnvironment( + _ name: UnsafePointer, + _ nameLength: UInt, + genericEnvironment: UnsafeRawPointer?, + genericArguments: UnsafeRawPointer?) + -> Any.Type? + + // ==== Internal helpers private func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) { diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 46d89dae..1d20114a 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -100,6 +100,9 @@ extension Swift2JavaTranslator { printImports(&printer) printModuleClass(&printer) { printer in + + printStaticLibraryLoad(&printer) + // TODO: print all "static" methods for decl in importedGlobalFuncs { printFunctionDowncallMethods(&printer, decl) @@ -110,22 +113,27 @@ extension Swift2JavaTranslator { public func printImportedClass(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { printHeader(&printer) printPackage(&printer) - printImports(&printer) // TODO print any imports the file may need, it we talk to other Swift modules + printImports(&printer) printClass(&printer, decl) { printer in - // Metadata + // Ensure we have loaded the library where the Swift type was declared before we attempt to resolve types in Swift + printStaticLibraryLoad(&printer) + + // Prepare type metadata, we're going to need these when invoking e.g. initializers so cache them in a static printer.print( """ - // FIXME: this detecting is somewhat off - public static final String TYPE_METADATA_NAME = "\(decl.swiftMangledName ?? "")"; - static final MemorySegment TYPE_METADATA = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_METADATA_NAME); + public static final String TYPE_MANGLED_NAME = "\(decl.swiftMangledName ?? "")"; + public static final SwiftAnyType TYPE_METADATA = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_MANGLED_NAME).get(); + public final SwiftAnyType $swiftType() { + return TYPE_METADATA; + } """ ) printer.print("") // Initializers for initDecl in decl.initializers { - printClassInitializer(&printer, initDecl) + printClassConstructors(&printer, initDecl) } // Properties @@ -137,6 +145,9 @@ extension Swift2JavaTranslator { for funcDecl in decl.methods { printFunctionDowncallMethods(&printer, funcDecl) } + + // Helper methods and default implementations + printHeapObjectToStringMethod(&printer, decl) } } @@ -168,20 +179,15 @@ extension Swift2JavaTranslator { } public func printClass(_ printer: inout CodePrinter, _ decl: ImportedNominalType, body: (inout CodePrinter) -> Void) { - printer.printTypeDecl("public final class \(decl.javaClassName)") { printer in + printer.printTypeDecl("public final class \(decl.javaClassName) implements SwiftHeapObject") { printer in // ==== Storage of the class - // FIXME: implement the self storage for the memory address and accessors printClassSelfProperty(&printer, decl) - // Constructor - printClassMemorySegmentConstructor(&printer, decl) - // Constants printClassConstants(printer: &printer) printTypeMappingDecls(&printer) // Layout of the class - // TODO: this is missing information until we can get @layout from Swift printClassMemoryLayout(&printer, decl) // Render the 'trace' functions etc @@ -247,8 +253,8 @@ extension Swift2JavaTranslator { """ static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); private static SymbolLookup getSymbolLookup() { - if (SwiftKit.isMacOS()) { - return SymbolLookup.libraryLookup(System.mapLibraryName(DYLIB_NAME), LIBRARY_ARENA) + if (PlatformUtils.isMacOS()) { + return SymbolLookup.libraryLookup(System.mapLibraryName(LIB_NAME), LIBRARY_ARENA) .or(SymbolLookup.loaderLookup()) .or(Linker.nativeLinker().defaultLookup()); } else { @@ -266,7 +272,7 @@ extension Swift2JavaTranslator { private func printClassConstants(printer: inout CodePrinter) { printer.print( """ - static final String DYLIB_NAME = "\(swiftModuleName)"; + static final String LIB_NAME = "\(swiftModuleName)"; static final Arena LIBRARY_ARENA = Arena.ofAuto(); """ ) @@ -282,17 +288,6 @@ extension Swift2JavaTranslator { ) } - private func printClassMemorySegmentConstructor(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - printer.print( - """ - /** Instances are created using static {@code init} methods rather than through the constructor directly. */ - private \(decl.javaClassName)(MemorySegment selfMemorySegment) { - this.selfMemorySegment = selfMemorySegment; - } - """ - ) - } - /// Print a property where we can store the "self" pointer of a class. private func printClassSelfProperty(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { printer.print( @@ -308,31 +303,16 @@ extension Swift2JavaTranslator { } private func printClassMemoryLayout(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { + // TODO: make use of the swift runtime to get the layout printer.print( """ private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( SWIFT_POINTER - ); - - // TODO: depends on @layout in Swift, so we can't do these yet - private static final GroupLayout $CLASS_LAYOUT = MemoryLayout.structLayout( - // SWIFT_INT.withName("heapObject"), - // ... - // SWIFT_INT.withName("cap") - ).withName("\(decl.javaType)"); // TODO: is the name right? + ).withName("\(decl.swiftMangledName ?? decl.swiftTypeName)"); - /** - * When other types refer to this type, they refer to by a pointer, - * so the layout of a class is just a pointer - */ - public static final GroupLayout $layout() { + public final GroupLayout $layout() { return $LAYOUT; } - - /** The in-memory layout of this type */ - public static final GroupLayout $instanceLayout() { - return $CLASS_LAYOUT; - } """ ) } @@ -372,7 +352,6 @@ extension Swift2JavaTranslator { public static final ValueLayout.OfLong SWIFT_UINT = SWIFT_INT64; public static final AddressLayout SWIFT_SELF = SWIFT_POINTER; - public static final AddressLayout SWIFT_TYPE_METADATA_PTR = SWIFT_POINTER; """ ) } @@ -411,7 +390,7 @@ extension Swift2JavaTranslator { ) } - public func printClassInitializer(_ printer: inout CodePrinter, _ decl: ImportedFunc) { + public func printClassConstructors(_ printer: inout CodePrinter, _ decl: ImportedFunc) { guard let parentName = decl.parentName else { fatalError("init must be inside a parent type! Was: \(decl)") } @@ -424,32 +403,48 @@ extension Swift2JavaTranslator { printMethodDowncallHandleForAddrDesc(&printer) } - printClassInitializerConstructor(&printer, decl, parentName: parentName) + printClassInitializerConstructors(&printer, decl, parentName: parentName) } - public func printClassInitializerConstructor( + public func printClassInitializerConstructors( _ printer: inout CodePrinter, _ decl: ImportedFunc, parentName: TranslatedType ) { let descClassIdentifier = renderDescClassName(decl) + printer.print( """ /** * Create an instance of {@code \(parentName.unqualifiedJavaTypeName)}. * - * {@snippet lang=swift : - * \(decl.syntax ?? "") - * } + \(decl.renderCommentSnippet ?? " *") */ public \(parentName.unqualifiedJavaTypeName)(\(renderJavaParamDecls(decl, selfVariant: .wrapper))) { + this(/*arena=*/null, \(renderForwardParams(decl, selfVariant: .wrapper))); + } + """ + ) + + printer.print( + """ + /** + * Create an instance of {@code \(parentName.unqualifiedJavaTypeName)}. + * This instance is managed by the passed in {@link SwiftArena} and may not outlive the arena's lifetime. + * + \(decl.renderCommentSnippet ?? " *") + */ + public \(parentName.unqualifiedJavaTypeName)(SwiftArena arena, \(renderJavaParamDecls(decl, selfVariant: .wrapper))) { var mh$ = \(descClassIdentifier).HANDLE; try { if (TRACE_DOWNCALLS) { traceDowncall(\(renderForwardParams(decl, selfVariant: nil))); } - this.selfMemorySegment = (MemorySegment) mh$.invokeExact(\(renderForwardParams(decl, selfVariant: nil)), TYPE_METADATA); + this.selfMemorySegment = (MemorySegment) mh$.invokeExact(\(renderForwardParams(decl, selfVariant: nil)), TYPE_METADATA.$memorySegment()); + if (arena != null) { + arena.register(this); + } } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } @@ -458,6 +453,17 @@ extension Swift2JavaTranslator { ) } + public func printStaticLibraryLoad(_ printer: inout CodePrinter) { + printer.print( + """ + static { + System.loadLibrary("swiftCore"); + System.loadLibrary(LIB_NAME); + } + """ + ) + } + public func printFunctionDowncallMethods(_ printer: inout CodePrinter, _ decl: ImportedFunc) { printer.printSeparator(decl.identifier) @@ -821,4 +827,16 @@ extension Swift2JavaTranslator { printer.print(");"); } + public func printHeapObjectToStringMethod(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { + printer.print( + """ + @Override + public String toString() { + return getClass().getSimpleName() + "(" + + SwiftKit.nameOfSwiftType($swiftType().$memorySegment(), true) + + ")@" + $memorySegment(); + } + """) + } + } diff --git a/Sources/JExtractSwift/Swift2JavaTranslator.swift b/Sources/JExtractSwift/Swift2JavaTranslator.swift index b57c19af..a954620b 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator.swift @@ -118,8 +118,9 @@ extension Swift2JavaTranslator { /// Default set Java imports for every generated file static let defaultJavaImports: Array = [ - // Support library in Java + "org.swift.swiftkit.*", "org.swift.swiftkit.SwiftKit", + "org.swift.swiftkit.util.*", // Necessary for native calls and type mapping "java.lang.foreign.*", diff --git a/Sources/SwiftKitSwift/SwiftKit.swift b/Sources/SwiftKitSwift/SwiftKit.swift index e034f6d0..9fdcdd80 100644 --- a/Sources/SwiftKitSwift/SwiftKit.swift +++ b/Sources/SwiftKitSwift/SwiftKit.swift @@ -18,6 +18,7 @@ // No annotations are necessary on the Swift side to perform the export. // FIXME: this is a workaround until we can pass String to Swift directly +@_silgen_name("getTypeByStringByteArray") public func getTypeByStringByteArray(_ name: UnsafePointer) -> Any.Type? { let string = String(cString: name) let type = _typeByName(string) diff --git a/SwiftKit/build.gradle b/SwiftKit/build.gradle index 5a6d15c0..3479e6c8 100644 --- a/SwiftKit/build.gradle +++ b/SwiftKit/build.gradle @@ -36,4 +36,7 @@ dependencies { tasks.test { useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java b/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java index 2abaf85a..da25ae4d 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/ManagedSwiftType.java @@ -19,7 +19,6 @@ public interface ManagedSwiftType { /** * The memory segment of `self` of the managed Swift object/value. - * @return */ public MemorySegment $memorySegment(); diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java new file mode 100644 index 00000000..c1ab8995 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/MemorySegmentUtils.java @@ -0,0 +1,39 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.MemorySegment; +import java.lang.foreign.ValueLayout; + +public class MemorySegmentUtils { + /** + * Set the value of `target` to the {@link MemorySegment#address()} of the `memorySegment`, + * adjusting for the fact that Swift pointer may be 32 or 64 bit, depending on runtime. + * + * @param target the target to set a value on + * @param memorySegment the origin of the address value to write into `target` + */ + static void setSwiftPointerAddress(MemorySegment target, MemorySegment memorySegment) { + // Write the address of as the value of the newly created pointer. + // We need to type-safely set the pointer value which may be 64 or 32-bit. + if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { + System.out.println("[setSwiftPointerAddress] address is long = " + memorySegment.address()); + target.set(ValueLayout.JAVA_LONG, /*offset=*/0, memorySegment.address()); + } else { + System.out.println("[setSwiftPointerAddress] address is int = " + memorySegment.address()); + target.set(ValueLayout.JAVA_INT, /*offset=*/0, (int) memorySegment.address()); + } + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java new file mode 100644 index 00000000..6dcd1f6b --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.MemorySegment; + +public final class SwiftAnyType { + + private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( + SwiftValueLayout.SWIFT_POINTER + ); + + private final MemorySegment memorySegment; + + public SwiftAnyType(MemorySegment memorySegment) { + if (memorySegment.byteSize() == 0) { + throw new IllegalArgumentException("A Swift Any.Type cannot be null!"); + } + + this.memorySegment = memorySegment; + } + + public SwiftAnyType(SwiftHeapObject object) { + if (object.$layout().name().isEmpty()) { + throw new IllegalArgumentException("SwiftHeapObject must have a mangled name in order to obtain its SwiftType."); + } + + String mangledName = object.$layout().name().get(); + var type = SwiftKit.getTypeByMangledNameInEnvironment(mangledName); + if (type.isEmpty()) { + throw new IllegalArgumentException("A Swift Any.Type cannot be null!"); + } + this.memorySegment = type.get().memorySegment; + } + + + public MemorySegment $memorySegment() { + return memorySegment; + } + + public GroupLayout $layout() { + return $LAYOUT; + } + + @Override + public String toString() { + return "AnySwiftType{" + + "name=" + SwiftKit.nameOfSwiftType(memorySegment, true) + + ", memorySegment=" + memorySegment + + '}'; + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java index 54c801b4..98bfd5ef 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java @@ -14,52 +14,124 @@ package org.swift.swiftkit; -import java.lang.foreign.Arena; -import java.lang.foreign.MemorySegment; -import java.util.concurrent.ConcurrentSkipListSet; - -public interface SwiftArena extends Arena { +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A Swift arena manages Swift allocated memory for classes, structs, enums etc. + * When an arena is closed, it will destroy all managed swift objects in a way appropriate to their type. + *

+ * A confined arena has an associated owner thread that confines some operations to + * associated owner thread such as {@link #close()}. + */ +public interface SwiftArena extends AutoCloseable { + + static SwiftArena ofConfined() { + return new ConfinedSwiftMemorySession(Thread.currentThread()); + } - void release(MemorySegment segment); + /** + * Register a Swift reference counted heap object with this arena (such as a {@code class} or {@code actor}). + * Its memory should be considered managed by this arena, and be destroyed when the arena is closed. + */ + void register(SwiftHeapObject object); - void retain(MemorySegment segment); + /** + * Register a struct, enum or other non-reference counted Swift object. + * Its memory should be considered managed by this arena, and be destroyed when the arena is closed. + */ + void register(SwiftValue value); - long retainCount(MemorySegment segment); + /** + * Close the arena and make sure all objects it managed are released. + * Throws if unable to verify all resources have been release (e.g. over retained Swift classes) + */ + void close(); } -final class AutoSwiftArena implements SwiftArena { - Arena underlying; +final class ConfinedSwiftMemorySession implements SwiftArena { + +// final Arena underlying; + final Thread owner; + final SwiftResourceList resources; + + final int CLOSED = 0; + final int ACTIVE = 1; + final AtomicInteger state; - ConcurrentSkipListSet managedMemorySegments; + public ConfinedSwiftMemorySession(Thread owner) { + this.owner = owner; + resources = new ConfinedResourceList(); + state = new AtomicInteger(ACTIVE); + } + + public void checkValid() throws RuntimeException { + if (this.owner != null && this.owner != Thread.currentThread()) { + throw new WrongThreadException("ConfinedSwift arena is confined to %s but was closed from %s!".formatted(this.owner, Thread.currentThread())); + } else if (this.state.get() < ACTIVE) { + throw new RuntimeException("Arena is already closed!"); + } + } @Override - public MemorySegment allocate(long byteSize, long byteAlignment) { - return null; + public void register(SwiftHeapObject object) { + this.resources.add(new SwiftHeapObjectCleanup(object)); } @Override - public MemorySegment.Scope scope() { - return null; + public void register(SwiftValue value) { + this.resources.add(new SwiftValueCleanup(value.$memorySegment())); } @Override public void close() { + checkValid(); + // Cleanup all resources + if (this.state.compareAndExchange(ACTIVE, CLOSED) == ACTIVE) { + this.resources.cleanup(); + } // else, was already closed; do nothing } - @Override - public void release(MemorySegment segment) { - SwiftKit.release(segment); + /** + * Represents a list of resources that need a cleanup, e.g. allocated classes/structs. + */ + static abstract class SwiftResourceList implements Runnable { + // TODO: Could use intrusive linked list to avoid one indirection here + final List resourceCleanups = new LinkedList<>(); + + abstract void add(SwiftInstanceCleanup cleanup); + + public abstract void cleanup(); + + public final void run() { + cleanup(); // cleaner interop + } } - @Override - public void retain(MemorySegment segment) { - SwiftKit.retain(segment); + static final class ConfinedResourceList extends SwiftResourceList { + @Override + void add(SwiftInstanceCleanup cleanup) { + resourceCleanups.add(cleanup); + } + + @Override + public void cleanup() { + for (SwiftInstanceCleanup cleanup : resourceCleanups) { + cleanup.run(); + } + } } - @Override - public long retainCount(MemorySegment segment) { - return SwiftKit.retainCount(segment); + +} + +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/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java index 5d1945dd..a7add138 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java @@ -14,8 +14,9 @@ package org.swift.swiftkit; -import java.lang.foreign.MemorySegment; - -public interface SwiftHeapObject { - MemorySegment $self(); +/** + * Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}. + */ +public interface SwiftHeapObject extends SwiftInstance { + SwiftAnyType $swiftType(); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java new file mode 100644 index 00000000..08cce158 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java @@ -0,0 +1,42 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.GroupLayout; +import java.lang.foreign.MemorySegment; + +public interface SwiftInstance { + + /** + * The pointer to the instance in memory. I.e. the {@code self} of the Swift object or value. + */ + MemorySegment $memorySegment(); + + /** + * The in memory layout of an instance of this Swift type. + */ + GroupLayout $layout(); + + SwiftAnyType $swiftType(); + + /** + * Returns `true` if this swift instance is a reference type, i.e. a `class` or (`distributed`) `actor`. + * + * @return `true` if this instance is a reference type, `false` otherwise. + */ + default boolean isReferenceType() { + return this instanceof SwiftHeapObject; + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java new file mode 100644 index 00000000..e28e0dd7 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.MemorySegment; + +/** + * A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. + */ +sealed interface SwiftInstanceCleanup extends Runnable { +} + +record SwiftHeapObjectCleanup(SwiftHeapObject instance) implements SwiftInstanceCleanup { + + @Override + public void run() throws UnexpectedRetainCountException { + // Verify we're only destroying an object that's indeed not retained by anyone else: + long retainedCount = SwiftKit.retainCount(this.instance); + if (retainedCount > 1) { + throw new UnexpectedRetainCountException(this.instance, retainedCount, 1); + } + + // Destroy (and deinit) the object: + var ty = this.instance.$swiftType(); + System.out.println("x destroy instance = " + this.instance); + System.out.println("x destroy ty = " + ty); + + SwiftValueWitnessTable.destroy(ty, this.instance.$memorySegment()); + + // Invalidate the Java wrapper class, in order to prevent effectively use-after-free issues. + // FIXME: some trouble with setting the pointer to null, need to figure out an appropriate way to do this + } +} + +record SwiftValueCleanup(MemorySegment resource) implements SwiftInstanceCleanup { + @Override + public void run() { + throw new RuntimeException("not implemented yet"); + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index 821169b9..064be4ec 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -14,13 +14,16 @@ package org.swift.swiftkit; +import org.swift.swiftkit.util.PlatformUtils; + import java.lang.foreign.*; -import java.lang.foreign.MemoryLayout.PathElement; import java.lang.invoke.MethodHandle; import java.util.Arrays; +import java.util.Optional; import java.util.stream.Collectors; -import static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static org.swift.swiftkit.util.StringUtils.stripPrefix; +import static org.swift.swiftkit.util.StringUtils.stripSuffix; public class SwiftKit { @@ -32,17 +35,14 @@ public class SwiftKit { static { System.loadLibrary(STDLIB_DYLIB_NAME); + System.loadLibrary("SwiftKitSwift"); } - public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS - .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); - static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup(); private static SymbolLookup getSymbolLookup() { - if (isMacOS()) { - // FIXME: why does this not find just by name on macOS? - // SymbolLookup.libraryLookup(System.mapLibraryName(STDLIB_DYLIB_NAME), LIBRARY_ARENA) + if (PlatformUtils.isMacOS()) { + // On Apple platforms we need to lookup using the complete path return SymbolLookup.libraryLookup(STDLIB_MACOS_DYLIB_PATH, LIBRARY_ARENA) .or(SymbolLookup.loaderLookup()) .or(Linker.nativeLinker().defaultLookup()); @@ -51,26 +51,15 @@ private static SymbolLookup getSymbolLookup() { .or(Linker.nativeLinker().defaultLookup()); } } - public SwiftKit() { - } - public static boolean isLinux() { - return System.getProperty("os.name").toLowerCase().contains("linux"); - } - - public static boolean isMacOS() { - return System.getProperty("os.name").toLowerCase().contains("mac"); - } - - public static boolean isWindows() { - return System.getProperty("os.name").toLowerCase().contains("windows"); + public SwiftKit() { } static void traceDowncall(String name, Object... args) { String traceArgs = Arrays.stream(args) .map(Object::toString) .collect(Collectors.joining(", ")); - System.out.printf("%s(%s)\n", name, traceArgs); + System.out.printf("[java] Downcall: %s(%s)\n", name, traceArgs); } static MemorySegment findOrThrow(String symbol) { @@ -78,31 +67,42 @@ static MemorySegment findOrThrow(String symbol) { .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); } + public static String getJavaLibraryPath() { + return System.getProperty("java.library.path"); + } + + public static boolean getJextractTraceDowncalls() { + return Boolean.getBoolean("jextract.trace.downcalls"); + } + // ==== ------------------------------------------------------------------------------------------------------------ // free - /** - * Descriptor for the free C runtime function. - */ - public static final FunctionDescriptor free$descriptor = FunctionDescriptor.ofVoid( - ValueLayout.ADDRESS - ); - /** - * Address of the free C runtime function. - */ - public static final MemorySegment free$addr = findOrThrow("free"); + static abstract class free { + /** + * Descriptor for the free C runtime function. + */ + public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS + ); - /** - * Handle for the free C runtime function. - */ - public static final MethodHandle free$handle = Linker.nativeLinker().downcallHandle(free$addr, free$descriptor); + /** + * Address of the free C runtime function. + */ + public static final MemorySegment ADDR = findOrThrow("free"); + + /** + * Handle for the free C runtime function. + */ + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } /** * free the given pointer */ public static void cFree(MemorySegment pointer) { try { - free$handle.invokeExact(pointer); + free.HANDLE.invokeExact(pointer); } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } @@ -136,7 +136,7 @@ public static long retainCount(MemorySegment object) { } public static long retainCount(SwiftHeapObject object) { - return retainCount(object.$self()); + return retainCount(object.$memorySegment()); } // ==== ------------------------------------------------------------------------------------------------------------ @@ -165,8 +165,8 @@ public static void retain(MemorySegment object) { } } - public static long retain(SwiftHeapObject object) { - return retainCount(object.$self()); + public static void retain(SwiftHeapObject object) { + retain(object.$memorySegment()); } // ==== ------------------------------------------------------------------------------------------------------------ @@ -187,7 +187,7 @@ public static void release(MemorySegment object) { var mh$ = swift_release.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("swift_release_retain", object); + traceDowncall("swift_release", object); } mh$.invokeExact(object); } catch (Throwable ex$) { @@ -196,10 +196,11 @@ public static void release(MemorySegment object) { } public static long release(SwiftHeapObject object) { - return retainCount(object.$self()); + return retainCount(object.$memorySegment()); } // ==== ------------------------------------------------------------------------------------------------------------ + // getTypeByName /** * {@snippet lang = swift: @@ -222,7 +223,7 @@ public static MemorySegment getTypeByName(String string) { var mh$ = swift_getTypeByName.HANDLE; try { if (TRACE_DOWNCALLS) { - traceDowncall("_swift_getTypeByName"); + traceDowncall("_typeByName"); } // TODO: A bit annoying to generate, we need an arena for the conversion... try (Arena arena = Arena.ofConfined()) { @@ -247,7 +248,7 @@ public static MemorySegment getTypeByName(String string) { */ private static class swift_getTypeByMangledNameInEnvironment { public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /*returns=*/SWIFT_POINTER, + /*returns=*/SwiftValueLayout.SWIFT_POINTER, ValueLayout.ADDRESS, ValueLayout.JAVA_INT, ValueLayout.ADDRESS, @@ -259,200 +260,119 @@ private static class swift_getTypeByMangledNameInEnvironment { public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - public static MemorySegment getTypeByMangledNameInEnvironment(String string) { + private static class getTypeByStringByteArray { + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /*returns=*/SwiftValueLayout.SWIFT_POINTER, + ValueLayout.ADDRESS + ); + + public static final MemorySegment ADDR = findOrThrow("getTypeByStringByteArray"); + + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); + } + + /** + * Get a Swift {@code Any.Type} wrapped by {@link SwiftAnyType} which represents the type metadata available at runtime. + * + * @param mangledName The mangled type name (often prefixed with {@code $s}). + * @return the Swift Type wrapper object + */ + public static Optional getTypeByMangledNameInEnvironment(String mangledName) { + System.out.println("Get Any.Type for mangled name: " + mangledName); + var mh$ = swift_getTypeByMangledNameInEnvironment.HANDLE; try { - if (string.endsWith("CN")) { - string = string.substring(0, string.length() - 2); - } + // Strip the generic "$s" prefix always + mangledName = stripPrefix(mangledName, "$s"); + // Ma is the "metadata accessor" mangled names of types we get from swiftinterface + // contain this, but we don't need it for type lookup + mangledName = stripSuffix(mangledName, "Ma"); + mangledName = stripSuffix(mangledName, "CN"); if (TRACE_DOWNCALLS) { - traceDowncall(string); + traceDowncall("swift_getTypeByMangledNameInEnvironment", mangledName); } try (Arena arena = Arena.ofConfined()) { - MemorySegment stringMemorySegment = arena.allocateFrom(string); + MemorySegment stringMemorySegment = arena.allocateFrom(mangledName); - return (MemorySegment) mh$.invokeExact(stringMemorySegment, string.length(), MemorySegment.NULL, MemorySegment.NULL); + var memorySegment = (MemorySegment) mh$.invokeExact(stringMemorySegment, mangledName.length(), MemorySegment.NULL, MemorySegment.NULL); + + if (memorySegment.address() == 0) { + return Optional.empty(); + } + + var wrapper = new SwiftAnyType(memorySegment); + return Optional.of(wrapper); } } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } } - /** - * The value layout for Swift's Int type, which is a signed type that follows - * the size of a pointer (aka C's ptrdiff_t). - */ - public static ValueLayout SWIFT_INT = (ValueLayout.ADDRESS.byteSize() == 4) ? - ValueLayout.JAVA_INT : ValueLayout.JAVA_LONG; - - /** - * The value layout for Swift's UInt type, which is an unsigned type that follows - * the size of a pointer (aka C's size_t). Java does not have unsigned integer - * types in general, so we use the layout for Swift's Int. - */ - public static ValueLayout SWIFT_UINT = SWIFT_INT; - /** * Read a Swift.Int value from memory at the given offset and translate it into a Java long. *

* This function copes with the fact that a Swift.Int might be 32 or 64 bits. */ - public static final long getSwiftInt(MemorySegment memorySegment, long offset) { - if (SWIFT_INT == ValueLayout.JAVA_LONG) { + public static long getSwiftInt(MemorySegment memorySegment, long offset) { + if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) { return memorySegment.get(ValueLayout.JAVA_LONG, offset); } else { return memorySegment.get(ValueLayout.JAVA_INT, offset); } } - /** - * Value witness table layout. - */ - public static final MemoryLayout valueWitnessTableLayout = MemoryLayout.structLayout( - ValueLayout.ADDRESS.withName("initializeBufferWithCopyOfBuffer"), - ValueLayout.ADDRESS.withName("destroy"), - ValueLayout.ADDRESS.withName("initializeWithCopy"), - ValueLayout.ADDRESS.withName("assignWithCopy"), - ValueLayout.ADDRESS.withName("initializeWithTake"), - ValueLayout.ADDRESS.withName("assignWithTake"), - ValueLayout.ADDRESS.withName("getEnumTagSinglePayload"), - ValueLayout.ADDRESS.withName("storeEnumTagSinglePayload"), - SwiftKit.SWIFT_INT.withName("size"), - SwiftKit.SWIFT_INT.withName("stride"), - SwiftKit.SWIFT_UINT.withName("flags"), - SwiftKit.SWIFT_UINT.withName("extraInhabitantCount") - ).withName("SwiftValueWitnessTable"); - - /** - * Offset for the "size" field within the value witness table. - */ - static final long valueWitnessTable$size$offset = - valueWitnessTableLayout.byteOffset(PathElement.groupElement("size")); - - /** - * Offset for the "stride" field within the value witness table. - */ - static final long valueWitnessTable$stride$offset = - valueWitnessTableLayout.byteOffset(PathElement.groupElement("stride")); - /** - * Offset for the "flags" field within the value witness table. - */ - static final long valueWitnessTable$flags$offset = - valueWitnessTableLayout.byteOffset(PathElement.groupElement("flags")); - /** - * Type metadata pointer. - */ - public static final StructLayout fullTypeMetadataLayout = MemoryLayout.structLayout( - SWIFT_POINTER.withName("vwt") - ).withName("SwiftFullTypeMetadata"); - - /** - * Offset for the "vwt" field within the full type metadata. - */ - static final long fullTypeMetadata$vwt$offset = - fullTypeMetadataLayout.byteOffset(PathElement.groupElement("vwt")); + private static class swift_getTypeName { - /** - * Given the address of Swift type metadata for a type, return the addres - * of the "full" type metadata that can be accessed via fullTypeMetadataLayout. - */ - public static MemorySegment fullTypeMetadata(MemorySegment typeMetadata) { - return MemorySegment.ofAddress(typeMetadata.address() - SWIFT_POINTER.byteSize()) - .reinterpret(fullTypeMetadataLayout.byteSize()); - } - - /** - * Given the address of Swift type's metadata, return the address that - * references the value witness table for the type. - */ - public static MemorySegment valueWitnessTable(MemorySegment typeMetadata) { - return fullTypeMetadata(typeMetadata).get(SWIFT_POINTER, fullTypeMetadata$vwt$offset); - } - - /** - * Determine the size of a Swift type given its type metadata. - */ - public static long sizeOfSwiftType(MemorySegment typeMetadata) { - return getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$size$offset); - } + /** + * Descriptor for the swift_getTypeName runtime function. + */ + public static final FunctionDescriptor DESC = FunctionDescriptor.of( + /*returns=*/MemoryLayout.structLayout( + SwiftValueLayout.SWIFT_POINTER.withName("utf8Chars"), + SwiftValueLayout.SWIFT_INT.withName("length") + ), + ValueLayout.ADDRESS, + ValueLayout.JAVA_BOOLEAN + ); - /** - * Determine the stride of a Swift type given its type metadata, which is - * how many bytes are between successive elements of this type within an - * array. It is >= the size. - */ - public static long strideOfSwiftType(MemorySegment typeMetadata) { - return getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$stride$offset); - } + /** + * Address of the swift_getTypeName runtime function. + */ + public static final MemorySegment ADDR = findOrThrow("swift_getTypeName"); - /** - * Determine the alignment of the given Swift type. - */ - public static long alignmentOfSwiftType(MemorySegment typeMetadata) { - long flags = getSwiftInt(valueWitnessTable(typeMetadata), valueWitnessTable$flags$offset); - return (flags & 0xFF) + 1; + /** + * Handle for the swift_getTypeName runtime function. + */ + public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); } - /** - * Descriptor for the swift_getTypeName runtime function. - */ - public static final FunctionDescriptor swift_getTypeName$descriptor = FunctionDescriptor.of( - /*returns=*/MemoryLayout.structLayout( - SWIFT_POINTER.withName("utf8Chars"), - SWIFT_INT.withName("length") - ), - ValueLayout.ADDRESS, - ValueLayout.JAVA_BOOLEAN - ); - - /** - * Address of the swift_getTypeName runtime function. - */ - public static final MemorySegment swift_getTypeName$addr = findOrThrow("swift_getTypeName"); - - /** - * Handle for the swift_getTypeName runtime function. - */ - public static final MethodHandle swift_getTypeName$handle = Linker.nativeLinker().downcallHandle(swift_getTypeName$addr, swift_getTypeName$descriptor); - /** * Produce the name of the Swift type given its Swift type metadata. *

- * If 'qualified' is true, leave all of the qualification in place to + * If 'qualified' is true, leave all the qualification in place to * disambiguate the type, producing a more complete (but longer) type name. + * + * @param typeMetadata the memory segment must point to a Swift metadata, + * e.g. the result of a {@link swift_getTypeByMangledNameInEnvironment} call */ public static String nameOfSwiftType(MemorySegment typeMetadata, boolean qualified) { - try { - try (Arena arena = Arena.ofConfined()) { - MemorySegment charsAndLength = (MemorySegment) swift_getTypeName$handle.invokeExact((SegmentAllocator) arena, typeMetadata, qualified); - MemorySegment utf8Chars = charsAndLength.get(SWIFT_POINTER, 0); - String typeName = utf8Chars.getString(0); - cFree(utf8Chars); - return typeName; - } + MethodHandle mh = swift_getTypeName.HANDLE; + + try (Arena arena = Arena.ofConfined()) { + MemorySegment charsAndLength = (MemorySegment) mh.invokeExact((SegmentAllocator) arena, typeMetadata, qualified); + MemorySegment utf8Chars = charsAndLength.get(SwiftValueLayout.SWIFT_POINTER, 0); + String typeName = utf8Chars.getString(0); + + // FIXME: this free is not always correct: + // java(80175,0x17008f000) malloc: *** error for object 0x600000362610: pointer being freed was not allocated + // cFree(utf8Chars); + + return typeName; } catch (Throwable ex$) { throw new AssertionError("should not reach here", ex$); } } - /** - * Produce a layout that describes a Swift type based on its - * type metadata. The resulting layout is completely opaque to Java, but - * has appropriate size/alignment to model the memory associated with a - * Swift type. - *

- * In the future, this layout could be extended to provide more detail, - * such as the fields of a Swift struct. - */ - public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { - long size = sizeOfSwiftType(typeMetadata); - long stride = strideOfSwiftType(typeMetadata); - return MemoryLayout.structLayout( - MemoryLayout.sequenceLayout(size, JAVA_BYTE) - .withByteAlignment(alignmentOfSwiftType(typeMetadata)), - MemoryLayout.paddingLayout(stride - size) - ).withName(nameOfSwiftType(typeMetadata, true)); - } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java new file mode 100644 index 00000000..9387fa85 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +public interface SwiftValue extends SwiftInstance { + SwiftAnyType $swiftType(); +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java new file mode 100644 index 00000000..565b6da5 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueLayout.java @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import java.lang.foreign.AddressLayout; +import java.lang.foreign.MemoryLayout; +import java.lang.foreign.ValueLayout; + +import static java.lang.foreign.ValueLayout.*; + +/** + * Similar to {@link java.lang.foreign.ValueLayout} however with some Swift specifics. + */ +public class SwiftValueLayout { + + /** + * The width of a pointer on the current platform. + */ + public static long addressByteSize() { + return ValueLayout.ADDRESS.byteSize(); + } + + /** + * The value layout for Swift's {@code Int} type, which is a signed type that follows + * the size of a pointer (aka C's {@code ptrdiff_t}). + */ + public static ValueLayout SWIFT_INT = (ValueLayout.ADDRESS.byteSize() == 4) ? + ValueLayout.JAVA_INT : ValueLayout.JAVA_LONG; + + + /** + * The value layout for Swift's {@code UInt} type, which is an unsigned type that follows + * the size of a pointer (aka C's {@code size_t}). + *

+ * Java does not have unsigned integer types, so we use the layout for Swift's {@code Int}. + */ + public static ValueLayout SWIFT_UINT = SWIFT_INT; + + + public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS + .withTargetLayout(MemoryLayout.sequenceLayout(Long.MAX_VALUE, JAVA_BYTE)); + +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java new file mode 100644 index 00000000..826a4fa2 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -0,0 +1,228 @@ +//===----------------------------------------------------------------------===// +// +// 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 static java.lang.foreign.ValueLayout.JAVA_BYTE; +import static org.swift.swiftkit.SwiftKit.getSwiftInt; + +public abstract class SwiftValueWitnessTable { + + /** + * Value witness table layout. + */ + public static final MemoryLayout $LAYOUT = MemoryLayout.structLayout( + ValueLayout.ADDRESS.withName("initializeBufferWithCopyOfBuffer"), + ValueLayout.ADDRESS.withName("destroy"), + ValueLayout.ADDRESS.withName("initializeWithCopy"), + ValueLayout.ADDRESS.withName("assignWithCopy"), + ValueLayout.ADDRESS.withName("initializeWithTake"), + ValueLayout.ADDRESS.withName("assignWithTake"), + ValueLayout.ADDRESS.withName("getEnumTagSinglePayload"), + ValueLayout.ADDRESS.withName("storeEnumTagSinglePayload"), + SwiftValueLayout.SWIFT_INT.withName("size"), + SwiftValueLayout.SWIFT_INT.withName("stride"), + SwiftValueLayout.SWIFT_UINT.withName("flags"), + SwiftValueLayout.SWIFT_UINT.withName("extraInhabitantCount") + ).withName("SwiftValueWitnessTable"); + + + /** + * Type metadata pointer. + */ + private static final StructLayout fullTypeMetadataLayout = MemoryLayout.structLayout( + SwiftValueLayout.SWIFT_POINTER.withName("vwt") + ).withName("SwiftFullTypeMetadata"); + + /** + * Offset for the "vwt" field within the full type metadata. + */ + private static final long fullTypeMetadata$vwt$offset = + fullTypeMetadataLayout.byteOffset( + MemoryLayout.PathElement.groupElement("vwt")); + + /** + * Given the address of Swift type metadata for a type, return the addres + * of the "full" type metadata that can be accessed via fullTypeMetadataLayout. + */ + public static MemorySegment fullTypeMetadata(MemorySegment typeMetadata) { + return MemorySegment.ofAddress(typeMetadata.address() - SwiftValueLayout.SWIFT_POINTER.byteSize()) + .reinterpret(fullTypeMetadataLayout.byteSize()); + } + + /** + * Given the address of Swift type's metadata, return the address that + * references the value witness table for the type. + */ + public static MemorySegment valueWitnessTable(MemorySegment typeMetadata) { + return fullTypeMetadata(typeMetadata) + .get(SwiftValueLayout.SWIFT_POINTER, SwiftValueWitnessTable.fullTypeMetadata$vwt$offset); +// .get(ValueLayout.ADDRESS, SwiftValueWitnessTable.fullTypeMetadata$vwt$offset); + } + + + /** + * Offset for the "size" field within the value witness table. + */ + static final long $size$offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("size")); + + /** + * Determine the size of a Swift type given its type metadata. + * + * @param typeMetadata the memory segment must point to a Swift metadata + */ + public static long sizeOfSwiftType(MemorySegment typeMetadata) { + return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$size$offset); + } + + + /** + * Offset for the "stride" field within the value witness table. + */ + static final long $stride$offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("stride")); + + /** + * Determine the stride of a Swift type given its type metadata, which is + * how many bytes are between successive elements of this type within an + * array. + *

+ * It is >= the size. + * + * @param typeMetadata the memory segment must point to a Swift metadata + */ + public static long strideOfSwiftType(MemorySegment typeMetadata) { + return getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$stride$offset); + } + + + /** + * Determine the alignment of the given Swift type. + * + * @param typeMetadata the memory segment must point to a Swift metadata + */ + public static long alignmentOfSwiftType(MemorySegment typeMetadata) { + long flags = getSwiftInt(valueWitnessTable(typeMetadata), SwiftValueWitnessTable.$flags$offset); + return (flags & 0xFF) + 1; + } + + /** + * Produce a layout that describes a Swift type based on its + * type metadata. The resulting layout is completely opaque to Java, but + * has appropriate size/alignment to model the memory associated with a + * Swift type. + *

+ * In the future, this layout could be extended to provide more detail, + * such as the fields of a Swift struct. + * + * @param typeMetadata the memory segment must point to a Swift metadata + */ + public static MemoryLayout layoutOfSwiftType(MemorySegment typeMetadata) { + long size = sizeOfSwiftType(typeMetadata); + long stride = strideOfSwiftType(typeMetadata); + long padding = stride - size; + + // constructing a zero-length paddingLayout is illegal, so we avoid doing so + MemoryLayout[] layouts = padding == 0 ? + new MemoryLayout[]{ + MemoryLayout.sequenceLayout(size, JAVA_BYTE) + .withByteAlignment(alignmentOfSwiftType(typeMetadata)) + } : + new MemoryLayout[]{ + MemoryLayout.sequenceLayout(size, JAVA_BYTE) + .withByteAlignment(alignmentOfSwiftType(typeMetadata)), + MemoryLayout.paddingLayout(stride - size) + }; + + return MemoryLayout.structLayout( + layouts + ).withName(SwiftKit.nameOfSwiftType(typeMetadata, true)); + } + + + /** + * Offset for the "flags" field within the value witness table. + */ + static final long $flags$offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("flags")); + + /** + * {@snippet lang = C: + * ///void(*destroy)(T *object, witness_t *self); + * /// + * /// Given a valid object of this type, destroy it, leaving it as an + * /// invalid object. This is useful when generically destroying + * /// an object which has been allocated in-line, such as an array, + * /// struct,or tuple element. + * FUNCTION_VALUE_WITNESS(destroy, + * Destroy, + * VOID_TYPE, + * (MUTABLE_VALUE_TYPE, TYPE_TYPE)) + *} + */ + private static class destroy { + + static final long $offset = + $LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("destroy")); + + static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS, // witness table functions expect a pointer to self pointer + ValueLayout.ADDRESS // pointer to the witness table + ); + + /** + * Function pointer for the destroy operation + */ + static MemorySegment addr(SwiftAnyType ty) { + // Get the value witness table of the type + final var vwt = SwiftValueWitnessTable.valueWitnessTable(ty.$memorySegment()); + + // Get the address of the destroy function stored at the offset of the witness table + long funcAddress = getSwiftInt(vwt, destroy.$offset); + return MemorySegment.ofAddress(funcAddress); + } + + static MethodHandle handle(SwiftAnyType ty) { + return Linker.nativeLinker().downcallHandle(addr(ty), DESC); + } + } + + + /** + * Destroy the value/object. + *

+ * This includes deallocating the Swift managed memory for the object. + */ + public static void destroy(SwiftAnyType type, MemorySegment object) { + var fullTypeMetadata = fullTypeMetadata(type.$memorySegment()); + var wtable = valueWitnessTable(fullTypeMetadata); + + var mh = destroy.handle(type); + + try (var arena = Arena.ofConfined()) { + // we need to make a pointer to the self pointer when calling witness table functions: + MemorySegment indirect = arena.allocate(SwiftValueLayout.SWIFT_POINTER); // TODO: remove this and just have classes have this always anyway + MemorySegmentUtils.setSwiftPointerAddress(indirect, object); + + mh.invokeExact(indirect, wtable); + } catch (Throwable th) { + throw new AssertionError("Failed to destroy '" + type + "' at " + object, th); + } + } + +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java new file mode 100644 index 00000000..2c51143f --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/util/PlatformUtils.java @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit.util; + +public class PlatformUtils { + public static boolean isLinux() { + return System.getProperty("os.name").toLowerCase().contains("linux"); + } + + public static boolean isMacOS() { + return System.getProperty("os.name").toLowerCase().contains("mac"); + } + + public static boolean isWindows() { + return System.getProperty("os.name").toLowerCase().contains("windows"); + } + + public static boolean isAarch64() { + return System.getProperty("os.arch").equals("aarm64"); + } + + public static boolean isAmd64() { + String arch = System.getProperty("os.arch"); + return arch.equals("amd64") || arch.equals("x86_64"); + } +} diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java b/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java new file mode 100644 index 00000000..6753ea73 --- /dev/null +++ b/SwiftKit/src/main/java/org/swift/swiftkit/util/StringUtils.java @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit.util; + +public class StringUtils { + public static String stripPrefix(String mangledName, String prefix) { + if (mangledName.startsWith(prefix)) { + return mangledName.substring(prefix.length()); + } + return mangledName; + } + + public static String stripSuffix(String mangledName, String suffix) { + if (mangledName.endsWith(suffix)) { + return mangledName.substring(0, mangledName.length() - suffix.length()); + } + return mangledName; + } + + public static String hexString(long number) { + return String.format("0x%02x", number); + } + +} diff --git a/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java b/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java new file mode 100644 index 00000000..5354cc4f --- /dev/null +++ b/SwiftKit/src/test/java/org/swift/swiftkit/SwiftRuntimeMetadataTest.java @@ -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 +// +//===----------------------------------------------------------------------===// + +package org.swift.swiftkit; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class SwiftRuntimeMetadataTest { + + @Test + public void integer_layout_metadata() { + SwiftAnyType swiftType = SwiftKit.getTypeByMangledNameInEnvironment("Si").get(); + + if (SwiftValueLayout.addressByteSize() == 4) { + // 32-bit platform + Assertions.assertEquals(8, SwiftValueWitnessTable.sizeOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.strideOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Int)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType.$memorySegment()).toString()); + } else { + // 64-bit platform + Assertions.assertEquals(8, SwiftValueWitnessTable.sizeOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.strideOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals("[8%[8:b1]](Swift.Int)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType.$memorySegment()).toString()); + } + } + + @Test + public void optional_integer_layout_metadata() { + SwiftAnyType swiftType = SwiftKit.getTypeByMangledNameInEnvironment("SiSg").get(); + + if (SwiftValueLayout.addressByteSize() == 4) { + // 64-bit platform + Assertions.assertEquals(9, SwiftValueWitnessTable.sizeOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(16, SwiftValueWitnessTable.strideOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType.$memorySegment()).toString()); + } else { + // 64-bit platform + Assertions.assertEquals(9, SwiftValueWitnessTable.sizeOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(16, SwiftValueWitnessTable.strideOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals(8, SwiftValueWitnessTable.alignmentOfSwiftType(swiftType.$memorySegment())); + Assertions.assertEquals("[8%[9:b1]x7](Swift.Optional)", SwiftValueWitnessTable.layoutOfSwiftType(swiftType.$memorySegment()).toString()); + } + } + +} diff --git a/Tests/JExtractSwiftTests/FuncImportTests.swift b/Tests/JExtractSwiftTests/FuncImportTests.swift index 80f04f7d..3b2ef4f4 100644 --- a/Tests/JExtractSwiftTests/FuncImportTests.swift +++ b/Tests/JExtractSwiftTests/FuncImportTests.swift @@ -403,7 +403,7 @@ final class MethodImportTests { }! let output = CodePrinter.toString { printer in - st.printClassInitializerConstructor(&printer, initDecl, parentName: initDecl.parentName!) + st.printClassInitializerConstructors(&printer, initDecl, parentName: initDecl.parentName!) } assertOutput( @@ -418,15 +418,28 @@ final class MethodImportTests { * } */ public MySwiftClass(long len, long cap) { + this(/*arena=*/null, len, cap); + } + /** + * Create an instance of {@code MySwiftClass}. + * This instance is managed by the passed in {@link SwiftArena} and may not outlive the arena's lifetime. + * + * {@snippet lang=swift : + * public init(len: Swift.Int, cap: Swift.Int) + * } + */ + public MySwiftClass(SwiftArena arena, long len, long cap) { var mh$ = init_len_cap.HANDLE; try { - if (TRACE_DOWNCALLS) { - traceDowncall(len, cap); - } - - this.selfMemorySegment = (MemorySegment) mh$.invokeExact(len, cap, TYPE_METADATA); + if (TRACE_DOWNCALLS) { + traceDowncall(len, cap); + } + this.selfMemorySegment = (MemorySegment) mh$.invokeExact(len, cap, TYPE_METADATA.$memorySegment()); + if (arena != null) { + arena.register(this); + } } catch (Throwable ex$) { - throw new AssertionError(\"should not reach here\", ex$); + throw new AssertionError("should not reach here", ex$); } } """ diff --git a/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy b/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy index 3f50890d..e6a29ae9 100644 --- a/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy +++ b/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy @@ -21,20 +21,28 @@ final class BuildUtils { def osName = System.getProperty("os.name") def osArch = System.getProperty("os.arch") def isLinux = osName.toLowerCase(Locale.getDefault()).contains("linux") + def base = rootDir == null ? "" : "${rootDir}/" + System.out.println("Root dir is = ${rootDir}") return [ isLinux ? - /* Linux */(osArch == "amd64" || osArch == "amd64" ? - "${rootDir}/.build/x86_64-unknown-linux-gnu/debug/" : - "${rootDir}/.build/${osArch}-unknown-linux-gnu/debug/") : - /* macOS */(osArch == "aarch64" ? - "${rootDir}/.build/arm64-apple-macosx/debug/" : - "${rootDir}/.build/${osArch}-apple-macosx/debug/"), + /* Linux */ (osArch == "amd64" || osArch == "x86_64" ? + "${base}.build/x86_64-unknown-linux-gnu/debug/" : + "${base}.build/${osArch}-unknown-linux-gnu/debug/") : + /* macOS */ (osArch == "aarch64" ? + "${base}.build/arm64-apple-macosx/debug/" : + "${base}.build/${osArch}-apple-macosx/debug/"), + isLinux ? + /* Linux */ (osArch == "amd64" || osArch == "x86_64" ? + "${base}../../.build/x86_64-unknown-linux-gnu/debug/" : + "${base}../../.build/${osArch}-unknown-linux-gnu/debug/") : + /* macOS */ (osArch == "aarch64" ? + "${base}../../.build/arm64-apple-macosx/debug/" : + "${base}../../.build/${osArch}-apple-macosx/debug/"), isLinux ? "/usr/lib/swift/linux" : // assume macOS "/usr/lib/swift/" ] } - }