diff --git a/Samples/SwiftAndJavaJarFFMSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftAndJavaJarFFMSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift index 0bbf84769..827f23190 100644 --- a/Samples/SwiftAndJavaJarFFMSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftAndJavaJarFFMSampleLib/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -100,6 +100,14 @@ public func globalCallMeDoubleBinaryOperator(run: (Double, Double) -> Double) -> run(1.0, 2.0) } +public func globalCallMeDoubleToIntFunction(run: (Double) -> Int32) -> Int32 { + run(1.0) +} + +public func globalCallMeLongToIntFunction(run: (Int64) -> Int32) -> Int32 { + 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 3dd923e2d..3365c950a 100644 --- a/Samples/SwiftAndJavaJarFFMSampleLib/src/test/java/com/example/swift/MySwiftLibraryTest.java +++ b/Samples/SwiftAndJavaJarFFMSampleLib/src/test/java/com/example/swift/MySwiftLibraryTest.java @@ -146,4 +146,16 @@ void call_globalCallMeDoubleBinaryOperator_noThrow() { double result = MySwiftLibrary.globalCallMeDoubleBinaryOperator((double a, double b) -> { return a + b; }); assertEquals(3.0, result); } + + @Test + void call_globalCallMeDoubleToIntFunction_noThrow() { + int result = MySwiftLibrary.globalCallMeDoubleToIntFunction((double a) -> { return (int) a; }); + assertEquals(1, result); + } + + @Test + void call_globalCallMeLongToIntFunction_noThrow() { + int result = MySwiftLibrary.globalCallMeLongToIntFunction((long a) -> { return (int) a; }); + assertEquals(1, result); + } } diff --git a/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift b/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift index bc207d425..afb0466f6 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift +++ b/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift @@ -112,6 +112,14 @@ public func globalCallMeDoubleBinaryOperator(run: (Double, Double) -> Double) -> run(1.0, 2.0) } +public func globalCallMeDoubleToIntFunction(run: (Double) -> Int32) -> Int32 { + run(1.0) +} + +public func globalCallMeLongToIntFunction(run: (Int64) -> Int32) -> Int32 { + 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 7a098c17c..763862f41 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java @@ -258,4 +258,16 @@ void call_globalCallMeDoubleBinaryOperator_noThrow() { double result = MySwiftLibrary.globalCallMeDoubleBinaryOperator((double a, double b) -> { return a + b; }); assertEquals(3.0, result); } + + @Test + void call_globalCallMeDoubleToIntFunction_noThrow() { + int result = MySwiftLibrary.globalCallMeDoubleToIntFunction((double a) -> { return (int) a; }); + assertEquals(1, result); + } + + @Test + void call_globalCallMeLongToIntFunction_noThrow() { + int result = MySwiftLibrary.globalCallMeLongToIntFunction((long a) -> { return (int) a; }); + assertEquals(1, result); + } } diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Closures.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Closures.swift index 58785b252..c10321b53 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Closures.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Closures.swift @@ -85,6 +85,14 @@ public func globalCallMeDoubleBinaryOperator(run: (Double, Double) -> Double) -> run(1.0, 2.0) } +public func globalCallMeDoubleToIntFunction(run: (Double) -> Int32) -> Int32 { + run(1.0) +} + +public func globalCallMeLongToIntFunction(run: (Int64) -> Int32) -> Int32 { + 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 05f2909a6..f69e3375c 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ClosuresTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/ClosuresTest.java @@ -140,4 +140,16 @@ void globalCallMeDoubleBinaryOperator() { double result = MySwiftLibrary.globalCallMeDoubleBinaryOperator((double a, double b) -> { return a + b; }); assertEquals(3.0, result); } + + @Test + void globalCallMeDoubleToIntFunction() { + int result = MySwiftLibrary.globalCallMeDoubleToIntFunction((double a) -> { return (int) a; }); + assertEquals(1, result); + } + + @Test + void globalCallMeLongToIntFunction() { + int result = MySwiftLibrary.globalCallMeLongToIntFunction((long a) -> { return (int) a; }); + assertEquals(1, result); + } } diff --git a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift index 642080cdb..a23937019 100644 --- a/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift +++ b/Sources/ExampleSwiftLibrary/MySwiftLibrary.swift @@ -107,6 +107,14 @@ public func globalCallMeDoubleBinaryOperator(run: (Double, Double) -> Double) -> run(1.0, 2.0) } +public func globalCallMeDoubleToIntFunction(run: (Double) -> Int32) -> Int32 { + run(1.0) +} + +public func globalCallMeLongToIntFunction(run: (Int64) -> Int32) -> Int32 { + 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 686968d87..5fe0d2d7f 100644 --- a/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift +++ b/Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift @@ -110,6 +110,16 @@ extension JavaType { .class(package: "java.util.function", name: "DoubleBinaryOperator") } + /// The description of the type java.util.function.DoubleToIntFunction. + static var javaUtilFunctionDoubleToIntFunction: JavaType { + .class(package: "java.util.function", name: "DoubleToIntFunction") + } + + /// The description of the type java.util.function.LongToIntFunction. + static var javaUtilFunctionLongToIntFunction: JavaType { + .class(package: "java.util.function", name: "LongToIntFunction") + } + /// 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 405c5938c..ca77ff0ef 100644 --- a/Sources/JExtractSwiftLib/KnownFunctionalInterfaces.swift +++ b/Sources/JExtractSwiftLib/KnownFunctionalInterfaces.swift @@ -141,6 +141,20 @@ struct KnownJavaFunctionalInterface: Sendable { result: .double ) + static let doubleToIntFunction = KnownJavaFunctionalInterface( + JavaType.javaUtilFunctionDoubleToIntFunction, + method: "applyAsInt", + parameters: [.double], + result: .int + ) + + static let longToIntFunction = KnownJavaFunctionalInterface( + JavaType.javaUtilFunctionLongToIntFunction, + method: "applyAsInt", + parameters: [.long], + result: .int + ) + static let all: [KnownJavaFunctionalInterface] = [ .runnable, .booleanSupplier, @@ -159,6 +173,8 @@ struct KnownJavaFunctionalInterface: Sendable { .intBinaryOperator, .longBinaryOperator, .doubleBinaryOperator, + .doubleToIntFunction, + .longToIntFunction, ] static func find(parameters: [JavaType], result: JavaType) -> KnownJavaFunctionalInterface? { @@ -248,6 +264,19 @@ struct KnownJavaFunctionalInterface: Sendable { } } + // To int functions + if parameters.count == 1 && result.isInt32 { + let parameter = parameters[0].type + return switch () { + case _ where parameter.isInt64: + longToIntFunction + case _ where parameter.isDouble: + doubleToIntFunction + 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 4a40dfcf6..fe969c833 100644 --- a/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift +++ b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift @@ -47,6 +47,9 @@ final class FuncCallbackImportTests { public func callMeLongPredicate(callback: (Int64) -> Bool) public func callMeDoublePredicate(callback: (Double) -> Bool) + public func callMeDoubleToIntFunction(callback: (Double) -> Int32) + public func callMeLongToIntFunction(callback: (Int64) -> Int32) + public func callMeIntUnaryOperator(callback: (Int32) -> Int32) public func callMeLongUnaryOperator(callback: (Int64) -> Int64) public func callMeDoubleUnaryOperator(callback: (Double) -> Double) @@ -854,6 +857,92 @@ final class FuncCallbackImportTests { ) } + @Test("Import: public func callMeDoubleToIntFunction(callback: (Double) -> Int32)") + func func_callMecallMeDoubleToIntFunctionFunc_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 == "callMeDoubleToIntFunction" }! + + 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 callMeDoubleToIntFunction(callback: (Double) -> Int32) + * } + */ + public static void callMeDoubleToIntFunction(java.util.function.DoubleToIntFunction callback) { + try(var arena$ = Arena.ofConfined()) { + swiftjava___FakeModule_callMeDoubleToIntFunction_callback.call(callMeDoubleToIntFunction.$toUpcallStub(callback, arena$)); + } + } + """ + ] + ) + } + + @Test("Import: public func callMeLongToIntFunction(callback: (Int64) -> Int32)") + func func_callMecallMeLongToIntFunctionFunc_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 == "callMeLongToIntFunction" }! + + 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 callMeLongToIntFunction(callback: (Int64) -> Int32) + * } + */ + public static void callMeLongToIntFunction(java.util.function.LongToIntFunction callback) { + try(var arena$ = Arena.ofConfined()) { + swiftjava___FakeModule_callMeLongToIntFunction_callback.call(callMeLongToIntFunction.$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 1796d2e45..b0bede469 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIClosureTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIClosureTests.swift @@ -37,6 +37,9 @@ struct JNIClosureTests { public func closureLongUnaryOperator(closure: (Int64) -> Int64) {} public func closureDoubleUnaryOperator(closure: (Double) -> Double) {} + public func closureDoubleToIntFunction(closure: (Double) -> Int32) {} + public func closureLongToIntFunction(closure: (Int64) -> Int32) {} + public func closureIntBinaryOperator(closure: (Int32, Int32) -> Int32) {} public func closureLongBinaryOperator(closure: (Int64, Int64) -> Int64) {} public func closureDoubleBinaryOperator(closure: (Double, Double) -> Double) {} @@ -369,7 +372,6 @@ struct JNIClosureTests { ) } - @Test func closureDoubleUnaryOperator_javaBindings() throws { try assertOutput( @@ -395,6 +397,56 @@ struct JNIClosureTests { ) } + @Test + func closureDoubleToIntFunction_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func closureDoubleToIntFunction(closure: (Double) -> Int32) + * } + */ + public static void closureDoubleToIntFunction(java.util.function.DoubleToIntFunction closure) { + SwiftModule.$closureDoubleToIntFunction(closure); + } + """, + """ + private static native void $closureDoubleToIntFunction(java.util.function.DoubleToIntFunction closure); + """, + ] + ) + } + + @Test + func closureLongToIntFunction_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public func closureLongToIntFunction(closure: (Int64) -> Int32) + * } + */ + public static void closureLongToIntFunction(java.util.function.LongToIntFunction closure) { + SwiftModule.$closureLongToIntFunction(closure); + } + """, + """ + private static native void $closureLongToIntFunction(java.util.function.LongToIntFunction closure); + """, + ] + ) + } + @Test func closureIntBinaryOperator_javaBindings() throws { try assertOutput( @@ -795,6 +847,56 @@ struct JNIClosureTests { ) } + @Test + func closureDoubleToIntFunction_swiftThunks() throws { + try assertOutput( + input: source, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024closureDoubleToIntFunction__Ljava_util_function_DoubleToIntFunction_2") + public func Java_com_example_swift_SwiftModule__00024closureDoubleToIntFunction__Ljava_util_function_DoubleToIntFunction_2(environment: UnsafeMutablePointer!, thisClass: jclass, closure: jobject?) { + SwiftModule.closureDoubleToIntFunction(closure: { + let class$ = environment.interface.GetObjectClass(environment, closure) + let methodID$ = environment.interface.GetMethodID(environment, class$, "applyAsInt", "(D)I")! + environment.interface.DeleteLocalRef(environment, class$) + let arguments$: [jvalue] = [_0.getJValue(in: environment)] + return Int32(fromJNI: environment.interface.CallIntMethodA(environment, closure, methodID$, arguments$), in: environment) + } + ) + } + """ + ] + ) + } + + @Test + func closureLongToIntFunction_swiftThunks() throws { + try assertOutput( + input: source, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024closureLongToIntFunction__Ljava_util_function_LongToIntFunction_2") + public func Java_com_example_swift_SwiftModule__00024closureLongToIntFunction__Ljava_util_function_LongToIntFunction_2(environment: UnsafeMutablePointer!, thisClass: jclass, closure: jobject?) { + SwiftModule.closureLongToIntFunction(closure: { + let class$ = environment.interface.GetObjectClass(environment, closure) + let methodID$ = environment.interface.GetMethodID(environment, class$, "applyAsInt", "(J)I")! + environment.interface.DeleteLocalRef(environment, class$) + let arguments$: [jvalue] = [_0.getJValue(in: environment)] + return Int32(fromJNI: environment.interface.CallIntMethodA(environment, closure, methodID$, arguments$), in: environment) + } + ) + } + """ + ] + ) + } + @Test func closureDoubleUnaryOperator_swiftThunks() throws { try assertOutput(