diff --git a/Samples/SwiftAndJavaJarFFMSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftAndJavaJarFFMSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift index f9fca0ca5..126f9e422 100644 --- a/Samples/SwiftAndJavaJarFFMSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftAndJavaJarFFMSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -116,6 +116,14 @@ public func globalCallMeIntToLongFunction(run: (Int32) -> Int64) -> Int64 { run(1) } +public func globalCallMeIntToDoubleFunction(run: (Int32) -> Double) -> Double { + run(1) +} + +public func globalCallMeLongToDoubleFunction(run: (Int64) -> Double) -> Double { + run(1) +} + // ==== Internal helpers func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) { diff --git a/Samples/SwiftAndJavaJarFFMSampleLib/src/test/java/com/example/swift/MySwiftLibraryTest.java b/Samples/SwiftAndJavaJarFFMSampleLib/src/test/java/com/example/swift/MySwiftLibraryTest.java index 6511377c0..837cc8d7e 100644 --- a/Samples/SwiftAndJavaJarFFMSampleLib/src/test/java/com/example/swift/MySwiftLibraryTest.java +++ b/Samples/SwiftAndJavaJarFFMSampleLib/src/test/java/com/example/swift/MySwiftLibraryTest.java @@ -168,4 +168,16 @@ void call_globalCallMeIntToLongFunction_noThrow() { long result = MySwiftLibrary.globalCallMeIntToLongFunction((int a) -> { return (long) a; }); assertEquals(1L, result); } + + @Test + void call_globalCallMeIntToDoubleFunction_noThrow() { + double result = MySwiftLibrary.globalCallMeIntToDoubleFunction((int a) -> { return (double) a; }); + assertEquals(1.0, result); + } + + @Test + void call_globalCallMeLongToDoubleFunction_noThrow() { + double result = MySwiftLibrary.globalCallMeLongToDoubleFunction((long a) -> { return (double) a; }); + assertEquals(1.0, result); + } } diff --git a/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift index a1a7e2fbd..e8294488d 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -128,6 +128,14 @@ public func globalCallMeIntToLongFunction(run: (Int32) -> Int64) -> Int64 { run(1) } +public func globalCallMeIntToDoubleFunction(run: (Int32) -> Double) -> Double { + run(1) +} + +public func globalCallMeLongToDoubleFunction(run: (Int64) -> Double) -> Double { + run(1) +} + public func globalReceiveRawBuffer(buf: UnsafeRawBufferPointer) -> Int { buf.count } diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java index 730979196..158c307df 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java @@ -280,4 +280,16 @@ void call_globalCallMeIntToLongFunction_noThrow() { long result = MySwiftLibrary.globalCallMeIntToLongFunction((int a) -> { return (long) a; }); assertEquals(1L, result); } + + @Test + void call_globalCallMeIntToDoubleFunction_noThrow() { + double result = MySwiftLibrary.globalCallMeIntToDoubleFunction((int a) -> { return (double) a; }); + assertEquals(1.0, result); + } + + @Test + void call_globalCallMeLongToDoubleFunction_noThrow() { + double result = MySwiftLibrary.globalCallMeLongToDoubleFunction((long a) -> { return (double) a; }); + assertEquals(1.0, result); + } } diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Closures.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Closures.swift index 6eb23399d..3013a0ce2 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Closures.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Closures.swift @@ -101,6 +101,14 @@ public func globalCallMeIntToLongFunction(run: (Int32) -> Int64) -> Int64 { run(1) } +public func globalCallMeIntToDoubleFunction(run: (Int32) -> Double) -> Double { + run(1) +} + +public func globalCallMeLongToDoubleFunction(run: (Int64) -> Double) -> Double { + run(1) +} + public func closureMultipleArguments( input1: Int64, input2: Int64, diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ClosuresTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ClosuresTest.java index 47d83eeac..89c40a66a 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ClosuresTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ClosuresTest.java @@ -164,4 +164,16 @@ void globalCallMeIntToLongFunction() { long result = MySwiftLibrary.globalCallMeIntToLongFunction((int a) -> { return (long) a; }); assertEquals(1L, result); } + + @Test + void globalCallMeIntToDoubleFunction() { + double result = MySwiftLibrary.globalCallMeIntToDoubleFunction((int a) -> { return (double) a; }); + assertEquals(1.0, result); + } + + @Test + void globalCallMeLongToDoubleFunction() { + double result = MySwiftLibrary.globalCallMeLongToDoubleFunction((long a) -> { return (double) a; }); + assertEquals(1.0, result); + } } diff --git a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift index 1f37898b9..bb68b98da 100644 --- a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift +++ b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift @@ -123,6 +123,14 @@ public func globalCallMeIntToLongFunction(run: (Int32) -> Int64) -> Int64 { run(1) } +public func globalCallMeIntToDoubleFunction(run: (Int32) -> Double) -> Double { + run(1) +} + +public func globalCallMeLongToDoubleFunction(run: (Int64) -> Double) -> Double { + run(1) +} + public func globalReceiveRawBuffer(buf: UnsafeRawBufferPointer) -> Int { buf.count } diff --git a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift index 7c094df4f..0a5743cc1 100644 --- a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift +++ b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift @@ -130,6 +130,16 @@ extension JavaType { .class(package: "java.util.function", name: "IntToLongFunction") } + /// The description of the type java.util.function.IntToDoubleFunction. + static var javaUtilFunctionIntToDoubleFunction: JavaType { + .class(package: "java.util.function", name: "IntToDoubleFunction") + } + + /// The description of the type java.util.function.LongToDoubleFunction. + static var javaUtilFunctionLongToDoubleFunction: JavaType { + .class(package: "java.util.function", name: "LongToDoubleFunction") + } + /// The description of the type java.lang.Class. static var javaLangClass: JavaType { .class(package: "java.lang", name: "Class") diff --git a/Sources/JExtractSwiftLib/KnownFunctionalInterfaces.swift b/Sources/JExtractSwiftLib/KnownFunctionalInterfaces.swift index fe4b0b04e..9b1f75ab2 100644 --- a/Sources/JExtractSwiftLib/KnownFunctionalInterfaces.swift +++ b/Sources/JExtractSwiftLib/KnownFunctionalInterfaces.swift @@ -169,6 +169,20 @@ struct KnownJavaFunctionalInterface: Sendable { result: .long ) + static let intToDoubleFunction = KnownJavaFunctionalInterface( + JavaType.javaUtilFunctionIntToDoubleFunction, + method: "applyAsDouble", + parameters: [.int], + result: .double + ) + + static let longToDoubleFunction = KnownJavaFunctionalInterface( + JavaType.javaUtilFunctionLongToDoubleFunction, + method: "applyAsDouble", + parameters: [.long], + result: .double + ) + static let all: [KnownJavaFunctionalInterface] = [ .runnable, .booleanSupplier, @@ -191,6 +205,8 @@ struct KnownJavaFunctionalInterface: Sendable { .longToIntFunction, .doubleToLongFunction, .intToLongFunction, + .intToDoubleFunction, + .longToDoubleFunction, ] static func find(parameters: [JavaType], result: JavaType) -> KnownJavaFunctionalInterface? { @@ -306,6 +322,19 @@ struct KnownJavaFunctionalInterface: Sendable { } } + // To double functions + if parameters.count == 1 && result.isDouble { + let parameter = parameters[0].type + return switch () { + case _ where parameter.isInt32: + intToDoubleFunction + case _ where parameter.isInt64: + longToDoubleFunction + default: + nil + } + } + // Binary operators if parameters.count == 2 && parameters[0].type == result && parameters[1].type == result { let parameter = parameters[0].type diff --git a/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift index 6e555b635..d92c0a4c7 100644 --- a/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift +++ b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift @@ -53,6 +53,9 @@ final class FuncCallbackImportTests { public func callMeDoubleToLongFunction(callback: (Double) -> Int64) public func callMeIntToLongFunction(callback: (Int32) -> Int64) + public func callMeIntToDoubleFunction(callback: (Int32) -> Double) + public func callMeLongToDoubleFunction(callback: (Int64) -> Double) + public func callMeIntUnaryOperator(callback: (Int32) -> Int32) public func callMeLongUnaryOperator(callback: (Int64) -> Int64) public func callMeDoubleUnaryOperator(callback: (Double) -> Double) @@ -1032,6 +1035,92 @@ final class FuncCallbackImportTests { ) } + @Test("Import: public func callMeIntToDoubleFunction(callback: (Int32) -> Double)") + func func_callMecallMeIntToDoubleFunctionFunc_callback() throws { + var config = Configuration() + config.swiftModule = "__FakeModule" + let st = makeSwiftJavaAnalyzer(config: config) + st.log.logLevel = .error + + try st.analyze(path: "Fake.swift", text: Self.class_interfaceFile) + + let funcDecl = st.extractedGlobalFuncs.first { $0.name == "callMeIntToDoubleFunction" }! + + let generator = FFMSwift2JavaGenerator( + config: config, + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + + let output = JavaPrinter.toString { printer in + generator.printFunctionDowncallMethods(&printer, funcDecl) + } + + assertOutput( + output, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func callMeIntToDoubleFunction(callback: (Int32) -> Double) + * } + */ + public static void callMeIntToDoubleFunction(java.util.function.IntToDoubleFunction callback) { + try(var arena$ = Arena.ofConfined()) { + swiftjava___FakeModule_callMeIntToDoubleFunction_callback.call(callMeIntToDoubleFunction.$toUpcallStub(callback, arena$)); + } + } + """ + ] + ) + } + + @Test("Import: public func callMeLongToDoubleFunction(callback: (Int64) -> Double)") + func func_callMecallMeLongToDoubleFunctionFunc_callback() throws { + var config = Configuration() + config.swiftModule = "__FakeModule" + let st = makeSwiftJavaAnalyzer(config: config) + st.log.logLevel = .error + + try st.analyze(path: "Fake.swift", text: Self.class_interfaceFile) + + let funcDecl = st.extractedGlobalFuncs.first { $0.name == "callMeLongToDoubleFunction" }! + + let generator = FFMSwift2JavaGenerator( + config: config, + translator: st, + javaPackage: "com.example.swift", + swiftOutputDirectory: "/fake", + javaOutputDirectory: "/fake" + ) + + let output = JavaPrinter.toString { printer in + generator.printFunctionDowncallMethods(&printer, funcDecl) + } + + assertOutput( + output, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func callMeLongToDoubleFunction(callback: (Int64) -> Double) + * } + */ + public static void callMeLongToDoubleFunction(java.util.function.LongToDoubleFunction callback) { + try(var arena$ = Arena.ofConfined()) { + swiftjava___FakeModule_callMeLongToDoubleFunction_callback.call(callMeLongToDoubleFunction.$toUpcallStub(callback, arena$)); + } + } + """ + ] + ) + } + @Test("Import: public func callMecallMeIntBinaryOperatorFunc(callback: (Int32, Int32) -> Int32)") func func_callMecallMeIntBinaryOperatorFunc_callback() throws { var config = Configuration() diff --git a/Tests/JExtractSwiftTests/JNI/JNIClosureTests.swift b/Tests/JExtractSwiftTests/JNI/JNIClosureTests.swift index 4ef90684c..aa5f1fa6e 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIClosureTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIClosureTests.swift @@ -43,6 +43,9 @@ struct JNIClosureTests { public func closureDoubleToLongFunction(closure: (Double) -> Int64) {} public func closureIntToLongFunction(closure: (Int32) -> Int64) {} + public func closureIntToDoubleFunction(closure: (Int32) -> Double) {} + public func closureLongToDoubleFunction(closure: (Int64) -> Double) {} + public func closureIntBinaryOperator(closure: (Int32, Int32) -> Int32) {} public func closureLongBinaryOperator(closure: (Int64, Int64) -> Int64) {} public func closureDoubleBinaryOperator(closure: (Double, Double) -> Double) {} @@ -500,6 +503,56 @@ struct JNIClosureTests { ) } + @Test + func closureIntToDoubleFunction_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func closureIntToDoubleFunction(closure: (Int32) -> Double) + * } + */ + public static void closureIntToDoubleFunction(java.util.function.IntToDoubleFunction closure) { + SwiftModule.$closureIntToDoubleFunction(closure); + } + """, + """ + private static native void $closureIntToDoubleFunction(java.util.function.IntToDoubleFunction closure); + """, + ] + ) + } + + @Test + func closureLongToDoubleFunction_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func closureLongToDoubleFunction(closure: (Int64) -> Double) + * } + */ + public static void closureLongToDoubleFunction(java.util.function.LongToDoubleFunction closure) { + SwiftModule.$closureLongToDoubleFunction(closure); + } + """, + """ + private static native void $closureLongToDoubleFunction(java.util.function.LongToDoubleFunction closure); + """, + ] + ) + } + @Test func closureIntBinaryOperator_javaBindings() throws { try assertOutput( @@ -1000,6 +1053,56 @@ struct JNIClosureTests { ) } + @Test + func closureIntToDoubleFunction_swiftThunks() throws { + try assertOutput( + input: source, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024closureIntToDoubleFunction__Ljava_util_function_IntToDoubleFunction_2") + public func Java_com_example_swift_SwiftModule__00024closureIntToDoubleFunction__Ljava_util_function_IntToDoubleFunction_2(environment: UnsafeMutablePointer!, thisClass: jclass, closure: jobject?) { + SwiftModule.closureIntToDoubleFunction(closure: { + let class$ = environment.interface.GetObjectClass(environment, closure) + let methodID$ = environment.interface.GetMethodID(environment, class$, "applyAsDouble", "(I)D")! + environment.interface.DeleteLocalRef(environment, class$) + let arguments$: [jvalue] = [_0.getJValue(in: environment)] + return Double(fromJNI: environment.interface.CallDoubleMethodA(environment, closure, methodID$, arguments$), in: environment) + } + ) + } + """ + ] + ) + } + + @Test + func closureLongToDoubleFunction_swiftThunks() throws { + try assertOutput( + input: source, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024closureLongToDoubleFunction__Ljava_util_function_LongToDoubleFunction_2") + public func Java_com_example_swift_SwiftModule__00024closureLongToDoubleFunction__Ljava_util_function_LongToDoubleFunction_2(environment: UnsafeMutablePointer!, thisClass: jclass, closure: jobject?) { + SwiftModule.closureLongToDoubleFunction(closure: { + let class$ = environment.interface.GetObjectClass(environment, closure) + let methodID$ = environment.interface.GetMethodID(environment, class$, "applyAsDouble", "(J)D")! + environment.interface.DeleteLocalRef(environment, class$) + let arguments$: [jvalue] = [_0.getJValue(in: environment)] + return Double(fromJNI: environment.interface.CallDoubleMethodA(environment, closure, methodID$, arguments$), in: environment) + } + ) + } + """ + ] + ) + } + @Test func closureDoubleUnaryOperator_swiftThunks() throws {