diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 08ebc7b6..5a3ecf6a 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -58,8 +58,7 @@ jobs: # run the actual build - name: Gradle build run: | - ./gradlew build -x test --no-daemon # skip tests - find . + ./gradlew build -x test --no-daemon # just build ./gradlew build --info --no-daemon test-swift: diff --git a/.gitignore b/.gitignore index 43b51002..5055eec2 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,4 @@ Package.resolved .gradletasknamecache # Ignore files generated by jextract, we always can re-generate them -*/**/*.java +*/**/src/generated/java/**/* diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/CallMe.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/CallMe.java deleted file mode 100644 index e3e10815..00000000 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/CallMe.java +++ /dev/null @@ -1,25 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift.org project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift.org project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -package com.example.swift; - -public class CallMe { - public static String callMeStatic() { - return "[java] Swift called Java via Panama!"; - } - - public String callMe() { - return "[java] Swift called Java via Panama!"; - } -} diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualJavaKitExample.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualJavaKitExample.java deleted file mode 100644 index 1d9cfa77..00000000 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualJavaKitExample.java +++ /dev/null @@ -1,344 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift.org project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift.org project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -package com.example.swift; - -import java.lang.invoke.*; -import java.lang.foreign.*; -import java.util.*; -import java.util.stream.*; - -import static java.lang.foreign.ValueLayout.*; - -/** - * Generated by {@code jextract-swift}. // TODO: will be... :-) - */ -public class ManualJavaKitExample { - - ManualJavaKitExample() { - // Should not be called directly - } - - static final String DYLIB_NAME = "JavaKitExample"; - static final Arena LIBRARY_ARENA = Arena.ofAuto(); - static final boolean TRACE_DOWNCALLS = Boolean.getBoolean("jextract.trace.downcalls"); - - static void traceDowncall(Object... args) { - var ex = new RuntimeException(); - - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("[java][%s:%d] %s(%s)\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - - static void trace(Object... args) { - var ex = new RuntimeException(); - - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("[java][%s:%d] %s: %s\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - - static MemorySegment findOrThrow(String symbol) { - return SYMBOL_LOOKUP.find(symbol) - .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); - } - - static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { - try { - return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); - } catch (ReflectiveOperationException ex) { - throw new AssertionError(ex); - } - } - - static MemoryLayout align(MemoryLayout layout, long align) { - return switch (layout) { - case PaddingLayout p -> p; - case ValueLayout v -> v.withByteAlignment(align); - case GroupLayout g -> { - MemoryLayout[] alignedMembers = g.memberLayouts().stream() - .map(m -> align(m, align)).toArray(MemoryLayout[]::new); - yield g instanceof StructLayout ? - MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); - } - case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); - }; - } - - static final SymbolLookup SYMBOL_LOOKUP = - SymbolLookup.libraryLookup(System.mapLibraryName(DYLIB_NAME), LIBRARY_ARENA) - .or(SymbolLookup.loaderLookup()) - .or(Linker.nativeLinker().defaultLookup()); - - // TODO: rather than the C ones offer the Swift mappings - public static final ValueLayout.OfBoolean C_BOOL = ValueLayout.JAVA_BOOLEAN; - public static final ValueLayout.OfByte C_CHAR = ValueLayout.JAVA_BYTE; - public static final ValueLayout.OfShort C_SHORT = ValueLayout.JAVA_SHORT; - public static final ValueLayout.OfInt C_INT = ValueLayout.JAVA_INT; - public static final ValueLayout.OfLong C_LONG_LONG = ValueLayout.JAVA_LONG; - public static final ValueLayout.OfFloat C_FLOAT = ValueLayout.JAVA_FLOAT; - public static final ValueLayout.OfDouble C_DOUBLE = ValueLayout.JAVA_DOUBLE; - public static final AddressLayout C_POINTER = ValueLayout.ADDRESS - .withTargetLayout(MemoryLayout.sequenceLayout(java.lang.Long.MAX_VALUE, JAVA_BYTE)); - public static final ValueLayout.OfLong C_LONG = ValueLayout.JAVA_LONG; - - public static final ValueLayout.OfBoolean SWIFT_BOOL = ValueLayout.JAVA_BOOLEAN; - public static final ValueLayout.OfByte SWIFT_INT8 = ValueLayout.JAVA_BYTE; - public static final ValueLayout.OfChar SWIFT_UINT16 = ValueLayout.JAVA_CHAR; - public static final ValueLayout.OfShort SWIFT_INT16 = ValueLayout.JAVA_SHORT; - public static final ValueLayout.OfInt SWIFT_INT32 = ValueLayout.JAVA_INT; - public static final ValueLayout.OfLong SWIFT_INT64 = ValueLayout.JAVA_LONG; - public static final ValueLayout.OfFloat SWIFT_FLOAT = ValueLayout.JAVA_FLOAT; - public static final ValueLayout.OfDouble SWIFT_DOUBLE = ValueLayout.JAVA_DOUBLE; - public static final AddressLayout SWIFT_POINTER = ValueLayout.ADDRESS; - // On the platform this was generated on, Int was Int64 - public static final SequenceLayout SWIFT_BYTE_ARRAY = - MemoryLayout.sequenceLayout(8, JAVA_BYTE); - public static final ValueLayout.OfLong SWIFT_INT = SWIFT_INT64; - public static final ValueLayout.OfLong SWIFT_UINT = SWIFT_INT64; - public static final AddressLayout SWIFT_TYPE_METADATA_PTR = SWIFT_POINTER; - - // ----------------------------------------------------------------------------------------------------------------- - // helloWorld() - - // sil [ossa] @$s14JavaKitExample10helloWorldyyF : $@convention(thin) () -> () { - private static class helloWorld { - public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(); - - public static final MemorySegment ADDR = ManualJavaKitExample.findOrThrow("$s14JavaKitExample10helloWorldyyF"); - - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } - - /** - * Function descriptor for: - * {@snippet lang = swift: - * public func useMySwiftSlice(slice: MySwiftLibrary.MySwiftSlice) - *} - */ - public static FunctionDescriptor helloWorld$descriptor() { - return helloWorld.DESC; - } - - /** - * Downcall method handle for: - * {@snippet lang = swift: - * public func useMySwiftSlice(slice: MySwiftLibrary.MySwiftSlice) - *} - */ - public static MethodHandle helloWorld$handle() { - return helloWorld.HANDLE; - } - - /** - * Address for: - * {@snippet lang = swift: - * public func useMySwiftSlice(slice: MySwiftLibrary.MySwiftSlice) - *} - */ - public static MemorySegment helloWorld$address() { - return helloWorld.ADDR; - } - - /** - * {@snippet lang = swift: - * public func useMySwiftSlice(slice: MySwiftLibrary.MySwiftSlice) - *} - */ - public static void helloWorld() { - var mh$ = helloWorld.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall(); - } - mh$.invokeExact(); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - // ==== -------------------------------------------------- - // 0000000000031934 T JavaKitExample.globalCallJavaCallback(callMe: () -> ()) -> () - // 0000000000031934 T _$s14JavaKitExample010globalCallA8Callback6callMeyyyXE_tF - - private static class globalCallJavaCallback { - public static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid( - ADDRESS // Runnable / () -> () - ); - - public static final MemorySegment ADDR = ManualJavaKitExample.findOrThrow("$s14JavaKitExample010globalCallA8Callback6callMeyyyXE_tF"); - - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } - - - public static void globalCallJavaCallback(Runnable callMe) { - var mh$ = globalCallJavaCallback.HANDLE; - - try { - // signature of 'void run()' - FunctionDescriptor callMe_run_desc = FunctionDescriptor.ofVoid( - ); - MethodHandle callMe_run_handle = MethodHandles.lookup() - .findVirtual(Runnable.class, - "run", - callMe_run_desc.toMethodType()); - callMe_run_handle = callMe_run_handle.bindTo(callMe); // set the first parameter to the Runnable as the "this" of the callback pretty much - - try (Arena arena = Arena.ofConfined()) { - Linker linker = Linker.nativeLinker(); // the () -> () is not escaping, we can destroy along with the confined arena - MemorySegment runFunc = linker.upcallStub(callMe_run_handle, callMe_run_desc, arena); - mh$.invokeExact(runFunc); - } - - } catch (NoSuchMethodException | IllegalAccessException e) { - throw new RuntimeException(e); - } catch (Throwable e) { - throw new AssertionError(e); - } - } - - - // ----------------------------------------------------------------------------------------------------------------- - // FIXME: this must move into SwiftKit and call _typeByName with a Swift.String - - /// 00000000000255a4 T _$s14JavaKitExample33swiftkit_getTypeByStringByteArrayyypXpSgSPys5UInt8VGF - /// 00000000000255a4 T JavaKitExample.swiftkit_getTypeByStringByteArray(Swift.UnsafePointer) -> Any.Type? - private static class swiftkit_getTypeByStringByteArray { - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /* -> */ ValueLayout.ADDRESS, - ValueLayout.ADDRESS - ); - - public static final MemorySegment ADDR = ManualJavaKitExample.findOrThrow("$s14JavaKitExample33swiftkit_getTypeByStringByteArrayyypXpSgSPys5UInt8VGF"); - - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } - - public static FunctionDescriptor swiftkit_getTypeByStringByteArray$descriptor() { - return swiftkit_getTypeByStringByteArray.DESC; - } - - public static MethodHandle swiftkit_getTypeByStringByteArray$handle() { - return swiftkit_getTypeByStringByteArray.HANDLE; - } - - public static MemorySegment swiftkit_getTypeByStringByteArray$address() { - return swiftkit_getTypeByStringByteArray.ADDR; - } - - public static MemorySegment swiftkit_getTypeByStringByteArray(String mangledName) { - var mh$ = swiftkit_getTypeByStringByteArray.HANDLE; - try { - if (TRACE_DOWNCALLS) { - traceDowncall("swiftkit_getTypeByStringByteArray", mangledName); - } - try (Arena arena = Arena.ofConfined()) { - MemorySegment mangledNameMS = arena.allocateFrom(mangledName); - return (MemorySegment) mh$.invokeExact(mangledNameMS); - } - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - -// // ----------------------------------------------------------------------------------------------------------------- -// // useMySwiftSlice_getLen(slice: MySwiftSlice) -// -// -// // JavaKitExample.useMySwiftSlice_getLen(slice: JavaKitExample.MySwiftSlice) -> Swift.Int -// private static class useMySwiftSlice_getLen { -// public static final FunctionDescriptor DESC = FunctionDescriptor.of( -// /* -> */ JavaKitExample.SWIFT_INT, -// JavaKitExample.SWIFT_POINTER // FIXME: not: MySwiftSlice.layout() -// ); -// -//// public static final MemorySegment ADDR = JavaKitExample.findOrThrow("$s14JavaKitExample22useMySwiftSlice_getLen5sliceSiAA0efG0V_tF"); -// -// // with inout -// public static final MemorySegment ADDR = JavaKitExample.findOrThrow("$s14JavaKitExample22useMySwiftSlice_getLen5sliceSiAA0efG0Vz_tF"); -// -// public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); -// } -// -// /** -// * Function descriptor for: -// * {@snippet lang=swift : -// * public func useMySwiftSlice(slice: MySwiftLibrary.MySwiftSlice) -// * } -// */ -// public static FunctionDescriptor useMySwiftSlice$descriptor() { -// return useMySwiftSlice_getLen.DESC; -// } -// -// /** -// * Downcall method handle for: -// * {@snippet lang=swift : -// * public func useMySwiftSlice(slice: MySwiftLibrary.MySwiftSlice) -// * } -// */ -// public static MethodHandle useMySwiftSlice$handle() { -// return useMySwiftSlice_getLen.HANDLE; -// } -// -// /** -// * Address for: -// * {@snippet lang=swift : -// * public func useMySwiftSlice(slice: MySwiftLibrary.MySwiftSlice) -// * } -// */ -// public static MemorySegment useMySwiftSlice$address() { -// return useMySwiftSlice_getLen.ADDR; -// } -// -// /** -// * {@snippet lang=swift : -// * public func useMySwiftSlice(slice: MySwiftLibrary.MySwiftSlice) -// * } -// */ -// public static long useMySwiftSlice_getLen(MemorySegment self) { -// var mh$ = useMySwiftSlice_getLen.HANDLE; -// try { -// if (TRACE_DOWNCALLS) { -// traceDowncall(self); -// } -// System.out.println("self = " + self); -// return (long) mh$.invokeExact(self); -// } catch (Throwable ex$) { -// throw new AssertionError("should not reach here", ex$); -// } -// } -// -// /** -// * {@snippet lang=swift : -// * public func useMySwiftSlice(slice: MySwiftLibrary.MySwiftSlice) -// * } -// */ -// public static long useMySwiftSlice_getLen(MySwiftSlice self) { -// return useMySwiftSlice_getLen(self.$memorySegment()); -// } - -} - diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java deleted file mode 100644 index 9aa63f22..00000000 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java +++ /dev/null @@ -1,200 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift.org project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift.org project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -package com.example.swift; - -// ==== Extra convenience APIs ------------------------------------------------------------------------------------- -// TODO: Think about offering these or not, perhaps only as an option? - -import org.swift.swiftkit.ManagedSwiftType; - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; - -public final class ManualMySwiftClass extends Manual_MySwiftClass implements ManagedSwiftType { - - // 000000000003f4a8 S type metadata for JavaKitExample.MySwiftSlice - // strip the _$s - // drop the N - public static final String TYPE_MANGLED_NAME = "14JavaKitExample12MySwiftClassC"; - - private final MemorySegment self; - - - ManualMySwiftClass(MemorySegment self) { - this.self = self; - } - - public MemorySegment $memorySegment() { - return self; - } - - public GroupLayout $layout() { - return Manual_MySwiftClass.layout(); - } - - public long len() { - return Manual_MySwiftClass.getLen(self); - } - public void len(long value) { - Manual_MySwiftClass.setLen(self, value); - } - - - // ----------------------------------------------------------------------------------------------------------------- - // init(len: Int, cap: Int) - - /** - * - * Normal init: - * {@snippet lang=Swift: - * 0000000000031d78 T JavaKitExample.MySwiftClass.init(len: Swift.Int, cap: Swift.Int) -> JavaKitExample.MySwiftClass - * 0000000000031d78 T _$s14JavaKitExample12MySwiftClassC3len3capACSi_Sitcfc - * } - * - * {@snippet lang=Swift: - * // MySwiftClass.init(len:cap:) - * sil [ossa] @$s14MySwiftLibrary0aB5ClassC3len3capACSi_Sitcfc : $@convention(method) (Int, Int, @owned MySwiftClass) -> @owned MySwiftClass { - * // %0 "len" - * // %1 "cap" - * // %2 "self" - * bb0(%0 : $Int, %1 : $Int, %2 : @owned $MySwiftClass): - * } - * } - */ - private static class __init_len_cap { - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /* -> */ ValueLayout.ADDRESS, - /* len = */len$layout(), - /* cap = */cap$layout(), - /* self = */ ValueLayout.ADDRESS - ); - - public static final MemorySegment ADDR = ManualJavaKitExample.findOrThrow("$s14JavaKitExample12MySwiftClassC3len3capACSi_Sitcfc"); - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } - - public static FunctionDescriptor __init_len_cap$descriptor() { - return __init_len_cap.DESC; - } - - public static MethodHandle __init_len_cap$handle() { - return __init_len_cap.HANDLE; - } - - public static MemorySegment __init_len_cap$address() { - return __init_len_cap.ADDR; - } - - static MemorySegment __init_len_cap(Arena arena, long len, long cap) { - var mh$ = __init_len_cap.HANDLE; - try { - if (ManualJavaKitExample.TRACE_DOWNCALLS) { - ManualJavaKitExample.traceDowncall("init(len:cap:)", len, cap); - } - - // FIXME: get from Swift how wide this type should be, and alignment - MemorySegment alloc = arena.allocate(128); - - var address = (MemorySegment) mh$.invokeExact(len, cap, /*self*/alloc); - trace("address = " + address); - return address; - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - - /** - * - * Allocating init: - * - * {@snippet lang=Swift: - * 0000000000031d28 T JavaKitExample.MySwiftClass.__allocating_init(len: Swift.Int, cap: Swift.Int) -> JavaKitExample.MySwiftClass - * 0000000000031d28 T _$s14JavaKitExample12MySwiftClassC3len3capACSi_SitcfC - * } - * - * {@snippet lang=Swift: - * // MySwiftClass.__allocating_init(len:cap:) - * sil [serialized] [exact_self_class] [ossa] @$s14MySwiftLibrary0aB5ClassC3len3capACSi_SitcfC : $@convention(method) (Int, Int, @thick MySwiftClass.Type) -> @owned MySwiftClass { - * // %0 "len" - * // %1 "cap" - * // %2 "$metatype" - * bb0(%0 : $Int, %1 : $Int, %2 : $@thick MySwiftClass.Type): - * } - * } - */ - private static class __allocating_init_len_cap { - public static final FunctionDescriptor DESC = FunctionDescriptor.of( - /* -> */ ValueLayout.ADDRESS, - /*len = */len$layout(), - /*cap = */ManualJavaKitExample.SWIFT_INT, // FIXME: cap$layout(), - /* type metadata = */ManualJavaKitExample.SWIFT_TYPE_METADATA_PTR - ); - - public static final MemorySegment ADDR = ManualJavaKitExample.findOrThrow("$s14JavaKitExample12MySwiftClassC3len3capACSi_SitcfC"); - public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC); - } - - - - public static FunctionDescriptor __allocating_init_len_cap$descriptor() { - return __allocating_init_len_cap.DESC; - } - - public static MethodHandle __allocating_init_len_cap$handle() { - return __allocating_init_len_cap.HANDLE; - } - - public static MemorySegment __allocating_init_len_cap$address() { - return __allocating_init_len_cap.ADDR; - } - - static MemorySegment __allocating_init_len_cap(long len, long cap) { - var mh$ = __allocating_init_len_cap.HANDLE; - try { - if (ManualJavaKitExample.TRACE_DOWNCALLS) { - ManualJavaKitExample.traceDowncall("MySwiftClass.__allocating_init(len:cap:)", len, cap); - } - ManualJavaKitExample.trace("type name = " + TYPE_MANGLED_NAME); - - // FIXME: problems with _getTypeByName because of the String memory repr - // 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_MANGLED_NAME); - ManualJavaKitExample.trace("type = " + type); - - var address = (MemorySegment) mh$.invokeExact(len, cap, type); - System.out.println("address = " + address); - return address; - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - - // ----------------------------------------------------------------------------------------------------------------- - // ==== Initializer Java API - - public static ManualMySwiftClass init(Arena arena, long len, long cap) { - MemorySegment alloc = __init_len_cap(arena, len, cap); - return new ManualMySwiftClass(alloc); - } - - public static ManualMySwiftClass init(long len, long cap) { - MemorySegment alloc = __allocating_init_len_cap(len, cap); - return new ManualMySwiftClass(alloc); - } -} diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/Manual_MySwiftClass.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/Manual_MySwiftClass.java deleted file mode 100644 index f57ac9d8..00000000 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/Manual_MySwiftClass.java +++ /dev/null @@ -1,304 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2024 Apple Inc. and the Swift.org project authors -// Licensed under Apache License v2.0 -// -// See LICENSE.txt for license information -// See CONTRIBUTORS.txt for the list of Swift.org project authors -// -// SPDX-License-Identifier: Apache-2.0 -// -//===----------------------------------------------------------------------===// - -package com.example.swift; - -import java.lang.foreign.*; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.util.Arrays; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import static java.lang.foreign.MemoryLayout.PathElement.groupElement; -import static java.lang.foreign.ValueLayout.OfLong; - -/** - * {@snippet lang = Swift: - * public class MySwiftClass { - * var len: Int - * var cap: Int - * } - *} - */ -public class Manual_MySwiftClass { - - static final String DYLIB_NAME = "JavaKitExample"; - static final Arena LIBRARY_ARENA = Arena.ofAuto(); - static final boolean TRACE_DOWNCALLS = true || Boolean.getBoolean("jextract.trace.downcalls"); - - - static void traceDowncall(Object... args) { - var ex = new RuntimeException(); - - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("[java][%s:%d] %s(%s)\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - - static void trace(Object... args) { - var ex = new RuntimeException(); - - String traceArgs = Arrays.stream(args) - .map(Object::toString) - .collect(Collectors.joining(", ")); - System.out.printf("[java][%s:%d] %s: %s\n", - ex.getStackTrace()[1].getFileName(), - ex.getStackTrace()[1].getLineNumber(), - ex.getStackTrace()[1].getMethodName(), - traceArgs); - } - - static MemorySegment findOrThrow(String symbol) { - return SYMBOL_LOOKUP.find(symbol) - .orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol))); - } - - static MethodHandle upcallHandle(Class fi, String name, FunctionDescriptor fdesc) { - try { - return MethodHandles.lookup().findVirtual(fi, name, fdesc.toMethodType()); - } catch (ReflectiveOperationException ex) { - throw new AssertionError(ex); - } - } - - static MemoryLayout align(MemoryLayout layout, long align) { - return switch (layout) { - case PaddingLayout p -> p; - case ValueLayout v -> v.withByteAlignment(align); - case GroupLayout g -> { - MemoryLayout[] alignedMembers = g.memberLayouts().stream() - .map(m -> align(m, align)).toArray(MemoryLayout[]::new); - yield g instanceof StructLayout ? - MemoryLayout.structLayout(alignedMembers) : MemoryLayout.unionLayout(alignedMembers); - } - case SequenceLayout s -> MemoryLayout.sequenceLayout(s.elementCount(), align(s.elementLayout(), align)); - }; - } - - static final SymbolLookup SYMBOL_LOOKUP = - SymbolLookup.libraryLookup(System.mapLibraryName(DYLIB_NAME), LIBRARY_ARENA) - .or(SymbolLookup.loaderLookup()) - .or(Linker.nativeLinker().defaultLookup()); - - - Manual_MySwiftClass() { - // Should not be called directly - } - - - private static final GroupLayout $LAYOUT = MemoryLayout.structLayout( - ManualJavaKitExample.SWIFT_INT.withName("heapObject"), - ManualJavaKitExample.SWIFT_INT.withName("len"), - ManualJavaKitExample.SWIFT_INT.withName("cap") - ).withName("$MySwiftClass$31:1"); // TODO: is the name right? - - /** - * The layout of this class - */ - public static final GroupLayout layout() { - return $LAYOUT; - } - - // -------------------------------------------------------------------------------------------------------- - // ==== len - - private static final OfLong len$LAYOUT = (OfLong)$LAYOUT.select(groupElement("len")); - - private static class len$property { - public static final FunctionDescriptor DESC_GET = FunctionDescriptor.of( - /* -> */ManualJavaKitExample.SWIFT_INT, - /* self = */ ManualJavaKitExample.SWIFT_POINTER - ); - public static final FunctionDescriptor DESC_SET = FunctionDescriptor.ofVoid( - /* self = */ ManualJavaKitExample.SWIFT_POINTER, - ManualJavaKitExample.SWIFT_INT - ); - - private static final String BASE_NAME = "$s14JavaKitExample12MySwiftClassC3lenSiv"; - public static final MemorySegment ADDR_GET = ManualJavaKitExample.findOrThrow(BASE_NAME + "g"); - public static final MemorySegment ADDR_SET = ManualJavaKitExample.findOrThrow(BASE_NAME + "s"); - - public static final MethodHandle HANDLE_GET = Linker.nativeLinker().downcallHandle(ADDR_GET, DESC_GET); - public static final MethodHandle HANDLE_SET = Linker.nativeLinker().downcallHandle(ADDR_SET, DESC_SET); - } - - public static final OfLong len$layout() { - return len$LAYOUT; - } - - private static final long len$OFFSET = 8; - - public static final long len$offset() { - return len$OFFSET; - } - - public static FunctionDescriptor len$get$descriptor() { - return len$property.DESC_GET; - } - public static MethodHandle len$get$handle() { - return len$property.HANDLE_GET; - } - public static MemorySegment len$get$address() { - return len$property.ADDR_GET; - } - - public static long getLen(MemorySegment self) { - var mh$ = len$property.HANDLE_GET; - try { - if (TRACE_DOWNCALLS) { - traceDowncall("len$getter", self); - } - return (long) mh$.invokeExact(self); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - - public static long getLenRaw(MemorySegment self) { - // FIXME: seems wrong? - return self.get(len$LAYOUT, len$OFFSET); - } - - - public static FunctionDescriptor len$set$descriptor() { - return len$property.DESC_SET; - } - public static MethodHandle len$set$handle() { - return len$property.HANDLE_SET; - } - public static MemorySegment len$set$address() { - return len$property.ADDR_SET; - } - - - /** - * Setter for field: - * {@snippet lang = Swift : - * var len: Int { set } - * } - */ - public static void setLen(MemorySegment self, long fieldValue) { - var mh$ = len$property.HANDLE_SET; - try { - if (TRACE_DOWNCALLS) { - traceDowncall("len$setter", self, fieldValue); - } - mh$.invokeExact(self, fieldValue); - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } - } - public static void setLenRaw(MemorySegment self, long fieldValue) { - // FIXME: seems wrong? - self.set(len$LAYOUT, len$OFFSET, fieldValue); - } - - // -------------------------------------------------------------------------------------------------------- - // ==== len - - private static final OfLong cap$LAYOUT = (OfLong)$LAYOUT.select(groupElement("cap")); - - private static class cap$property { - public static final FunctionDescriptor DESC_GET = FunctionDescriptor.of( - /* -> */ManualJavaKitExample.SWIFT_INT, - /* self = */ ManualJavaKitExample.SWIFT_POINTER - ); - public static final FunctionDescriptor DESC_SET = FunctionDescriptor.ofVoid( - /* self = */ ManualJavaKitExample.SWIFT_POINTER, - ManualJavaKitExample.SWIFT_INT - ); - - private static final String BASE_NAME = "$s14JavaKitExample12MySwiftClassC3capSiv"; - public static final MemorySegment ADDR_GET = ManualJavaKitExample.findOrThrow(BASE_NAME + "g"); - public static final MemorySegment ADDR_SET = ManualJavaKitExample.findOrThrow(BASE_NAME + "s"); - - public static final MethodHandle HANDLE_GET = Linker.nativeLinker().downcallHandle(ADDR_GET, DESC_GET); - public static final MethodHandle HANDLE_SET = Linker.nativeLinker().downcallHandle(ADDR_SET, DESC_SET); - } - - public static final OfLong cap$layout() { - return cap$LAYOUT; - } - - private static final long cap$OFFSET = 16; - - public static final long cap$offset() { - return cap$OFFSET; - } - - public static FunctionDescriptor cap$get$descriptor() { - return cap$property.DESC_GET; - } - public static MethodHandle cap$get$handle() { - return cap$property.HANDLE_GET; - } - public static MemorySegment cap$get$address() { - return cap$property.ADDR_GET; - } - - - // ==== ------------------------------------------------------------------------------------------------------------ - - /** - * Obtains a slice of {@code arrayParam} which selects the array element at {@code index}. - * The returned segment has address {@code arrayParam.address() + index * layout().byteSize()} - */ - public static MemorySegment asSlice(MemorySegment array, long index) { - return array.asSlice(layout().byteSize() * index); - } - - /** - * The size (in bytes) of this struct - */ - public static long sizeof() { return layout().byteSize(); } - - /** - * Allocate a segment of size {@code layout().byteSize()} using {@code allocator} - */ - public static MemorySegment allocate(SegmentAllocator allocator) { - return allocator.allocate(layout()); - } - - /** - * Allocate an array of size {@code elementCount} using {@code allocator}. - * The returned segment has size {@code elementCount * layout().byteSize()}. - */ - public static MemorySegment allocateArray(long elementCount, SegmentAllocator allocator) { - return allocator.allocate(MemoryLayout.sequenceLayout(elementCount, layout())); - } - - /** - * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). - * The returned segment has size {@code layout().byteSize()} - */ - public static MemorySegment reinterpret(MemorySegment addr, Arena arena, Consumer cleanup) { - return reinterpret(addr, 1, arena, cleanup); - } - - /** - * Reinterprets {@code addr} using target {@code arena} and {@code cleanupAction} (if any). - * The returned segment has size {@code elementCount * layout().byteSize()} - */ - public static MemorySegment reinterpret(MemorySegment addr, long elementCount, Arena arena, Consumer cleanup) { - return addr.reinterpret(layout().byteSize() * elementCount, arena, cleanup); - } -} - - diff --git a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/GeneratedJavaKitExampleModuleTest.java b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/GeneratedJavaKitExampleModuleTest.java index 9ec18ba6..9cceacf5 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/GeneratedJavaKitExampleModuleTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/GeneratedJavaKitExampleModuleTest.java @@ -18,6 +18,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledOnOs; import org.junit.jupiter.api.condition.OS; +import org.swift.swiftkit.SwiftKit; + +import java.util.concurrent.CountDownLatch; import static org.junit.jupiter.api.Assertions.*; @@ -25,12 +28,8 @@ public class GeneratedJavaKitExampleModuleTest { @BeforeAll static void beforeAll() { - System.out.println("java.library.path = " + System.getProperty("java.library.path")); - - System.loadLibrary("swiftCore"); - System.loadLibrary("ExampleSwiftLibrary"); - - System.setProperty("jextract.trace.downcalls", "true"); + System.out.println("java.library.path = " + SwiftKit.getJavaLibraryPath()); + System.out.println("java.library.path = " + SwiftKit.getJextractTraceDowncalls()); } @Test @@ -47,4 +46,24 @@ void call_globalTakeInt() { assertNotNull(ExampleSwiftLibrary.globalTakeInt$address()); } + @Test + @SuppressWarnings({"Convert2Lambda", "Convert2MethodRef"}) + void call_globalCallMeRunnable() { + CountDownLatch countDownLatch = new CountDownLatch(3); + + ExampleSwiftLibrary.globalCallMeRunnable(new Runnable() { + @Override + public void run() { + countDownLatch.countDown(); + } + }); + assertEquals(2, countDownLatch.getCount()); + + ExampleSwiftLibrary.globalCallMeRunnable(() -> countDownLatch.countDown()); + assertEquals(1, countDownLatch.getCount()); + + ExampleSwiftLibrary.globalCallMeRunnable(countDownLatch::countDown); + assertEquals(0, countDownLatch.getCount()); + } + } diff --git a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift index 5ad8e6fb..146601d0 100644 --- a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift +++ b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift @@ -35,6 +35,10 @@ public func globalTakeIntInt(i: Int, j: Int) { p("i:\(i), j:\(j)") } +public func globalCallMeRunnable(run: () -> ()) { + run() +} + public class MySwiftClass { public var len: Int diff --git a/Sources/JExtractSwift/CodePrinter.swift b/Sources/JExtractSwift/CodePrinter.swift index 56a61a42..308db69e 100644 --- a/Sources/JExtractSwift/CodePrinter.swift +++ b/Sources/JExtractSwift/CodePrinter.swift @@ -75,6 +75,22 @@ public struct CodePrinter { print("}", .sloc, function: function, file: file, line: line) } + public mutating func printParts( + _ parts: String..., + terminator: PrinterTerminator = .newLine, + function: String = #function, + file: String = #fileID, + line: UInt = #line + ) { + for part in parts { + guard part.trimmingCharacters(in: .whitespacesAndNewlines).count != 0 else { + continue + } + + self.print(part, terminator, function: function, file: file, line: line) + } + } + public mutating func print( _ text: Any, _ terminator: PrinterTerminator = .newLine, diff --git a/Sources/JExtractSwift/ImportedDecls.swift b/Sources/JExtractSwift/ImportedDecls.swift index d89ed77d..3999a0c9 100644 --- a/Sources/JExtractSwift/ImportedDecls.swift +++ b/Sources/JExtractSwift/ImportedDecls.swift @@ -106,10 +106,13 @@ public struct ImportedParam { extension ImportedParam { func renderParameterForwarding() -> String? { if type.javaType.isPrimitive { - return effectiveName + effectiveName + } else if type.javaType.isSwiftClosure { + // use the name of the upcall handle we'll have emitted by now + "\(effectiveName!)$" + } else { + "\(effectiveName!).$memorySegment()" } - - return "\(effectiveName!).$memorySegment()" } } diff --git a/Sources/JExtractSwift/JavaType+Printing.swift b/Sources/JExtractSwift/JavaType+Printing.swift new file mode 100644 index 00000000..39a348d9 --- /dev/null +++ b/Sources/JExtractSwift/JavaType+Printing.swift @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// 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 Foundation +import SwiftBasicFormat +import SwiftParser +import SwiftSyntax +import JavaTypes + +extension JavaType { + /// Returns a 'handle' name to pass to the `invoke` call as well as the + /// `FunctionDescription` and `MethodHandle` of the downcall handle for this parameter. + /// + /// Pass the prior to `invoke`, and directly render the latter in the Java wrapper downcall function body. + func prepareClosureDowncallHandle(decl: ImportedFunc, parameter: String) -> String { + let varNameBase = "\(decl.baseIdentifier)_\(parameter)" + let handle = "\(varNameBase)_handle$" + let desc = "\(varNameBase)_desc$" + + if self == .javaLangRunnable { + return + """ + FunctionDescriptor \(desc) = FunctionDescriptor.ofVoid(); + MethodHandle \(handle) = MethodHandles.lookup() + .findVirtual(Runnable.class, "run", + \(desc).toMethodType()); + \(handle) = \(handle).bindTo(\(parameter)); + + Linker linker = Linker.nativeLinker(); + MemorySegment \(parameter)$ = linker.upcallStub(\(handle), \(desc), arena); + """ + } + + fatalError("Cannot render closure downcall handle for: \(self), in: \(decl), parameter: \(parameter)") + } +} diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 1d20114a..c8b9bf09 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -648,13 +648,20 @@ extension Swift2JavaTranslator { return } + let needsArena = downcallNeedsConfinedArena(decl) let handleName = accessorKind.renderHandleFieldName - printer.print( + + printer.printParts( """ \(javaDocComment) public static \(returnTy) \(identifier)(\(renderJavaParamDecls(decl, selfVariant: selfVariant))) { var mh$ = \(decl.baseIdentifier).\(handleName); - try { + \(renderTry(withArena: needsArena)) + """, + """ + \(renderUpcallHandles(decl)) + """, + """ if (TRACE_DOWNCALLS) { traceDowncall(\(renderForwardParams(decl, selfVariant: .memorySegment))); } @@ -739,6 +746,27 @@ extension Swift2JavaTranslator { return res } + /// Do we need to construct an inline confined arena for the duration of the downcall? + public func downcallNeedsConfinedArena(_ decl: ImportedFunc) -> Bool { + for p in decl.parameters { + // We need to detect if any of the parameters is a closure we need to prepare + // an upcall handle for. + if p.type.javaType.isSwiftClosure { + return true + } + } + + return false + } + + public func renderTry(withArena: Bool) -> String { + if withArena { + "try (Arena arena = Arena.ofConfined()) {" + } else { + "try {" + } + } + public func renderJavaParamDecls(_ decl: ImportedFunc, selfVariant: SelfParameterVariant?) -> String { var ps: [String] = [] var pCounter = 0 @@ -757,6 +785,19 @@ extension Swift2JavaTranslator { return res } + public func renderUpcallHandles(_ decl: ImportedFunc) -> String { + var printer = CodePrinter() + for p in decl.parameters where p.type.javaType.isSwiftClosure { + if p.type.javaType == .javaLangRunnable { + let paramName = p.secondName ?? p.firstName ?? "_" + let handleDesc = p.type.javaType.prepareClosureDowncallHandle(decl: decl, parameter: paramName) + printer.print(handleDesc) + } + } + + return printer.contents + } + public func renderForwardParams(_ decl: ImportedFunc, selfVariant: SelfParameterVariant?) -> String { var ps: [String] = [] var pCounter = 0 diff --git a/Sources/JavaTypes/JavaType+SwiftNames.swift b/Sources/JavaTypes/JavaType+SwiftNames.swift index 4bef1e75..8577640e 100644 --- a/Sources/JavaTypes/JavaType+SwiftNames.swift +++ b/Sources/JavaTypes/JavaType+SwiftNames.swift @@ -30,6 +30,18 @@ extension JavaType { } } + public var isSwiftClosure: Bool { + switch self { + case .boolean, .byte, .char, .short, .int, .long, .float, .double, .void, + .array: + return false + case .class(package: "java.lang", name: "Runnable"): + return true + case .class: + return false + } + } + /// Produce the Swift type name for this Java type. public func swiftTypeName(resolver: JavaToSwiftClassNameResolver) rethrows -> String { switch self { diff --git a/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift new file mode 100644 index 00000000..90541263 --- /dev/null +++ b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import JExtractSwift +import Testing + +final class FuncCallbackImportTests { + + static let class_interfaceFile = + """ + // swift-interface-format-version: 1.0 + // swift-compiler-version: Apple Swift version 6.0 effective-5.10 (swiftlang-6.0.0.7.6 clang-1600.0.24.1) + // swift-module-flags: -target arm64-apple-macosx15.0 -enable-objc-interop -enable-library-evolution -module-name MySwiftLibrary + import Darwin.C + import Darwin + import Swift + import _Concurrency + import _StringProcessing + import _SwiftConcurrencyShims + + // MANGLED NAME: $mockName + public func callMe(callback: () -> ()) + """ + + @Test("Import: public func callMe(callback: () -> ())") + func func_callMeFunc_Runnable() throws { + let st = Swift2JavaTranslator( + javaPackage: "com.example.swift", + swiftModuleName: "__FakeModule" + ) + st.log.logLevel = .error + + try st.analyze(swiftInterfacePath: "/fake/Fake.swiftinterface", text: Self.class_interfaceFile) + + let funcDecl = st.importedGlobalFuncs.first { $0.baseIdentifier == "callMe" }! + + let output = CodePrinter.toString { printer in + st.printFuncDowncallMethod(&printer, decl: funcDecl, selfVariant: nil) + } + + assertOutput( + output, + expected: + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func callMe(callback: () -> ()) + * } + */ + public static void callMe(java.lang.Runnable callback) { + var mh$ = callMe.HANDLE; + try (Arena arena = Arena.ofConfined()) { + FunctionDescriptor callMe_callback_desc$ = FunctionDescriptor.ofVoid(); + MethodHandle callMe_callback_handle$ = MethodHandles.lookup() + .findVirtual(Runnable.class, "run", + callMe_callback_desc$.toMethodType()); + callMe_callback_handle$ = callMe_callback_handle$.bindTo(callback); + Linker linker = Linker.nativeLinker(); + MemorySegment callback$ = linker.upcallStub(callMe_callback_handle$, callMe_callback_desc$, arena); + if (TRACE_DOWNCALLS) { + traceDowncall(callback$); + } + mh$.invokeExact(callback$); + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + } + """ + ) + } + +} diff --git a/Tests/JExtractSwiftTests/FuncImportTests.swift b/Tests/JExtractSwiftTests/MethodImportTests.swift similarity index 100% rename from Tests/JExtractSwiftTests/FuncImportTests.swift rename to Tests/JExtractSwiftTests/MethodImportTests.swift 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 e6a29ae9..2e09d11b 100644 --- a/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy +++ b/buildSrc/src/main/groovy/org/swift/swiftkit/gradle/BuildUtils.groovy @@ -22,7 +22,6 @@ final class BuildUtils { 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 ?