Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ public func globalCallMeDoubleSupplier(run: () -> Double) -> Double {
run()
}

public func globalCallMeIntConsumer(run: (Int32) -> Void) {
run(1)
}

// ==== Internal helpers

func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,9 @@ void call_globalCallMeDoubleSupplier_noThrow() {
double result = MySwiftLibrary.globalCallMeBooleanSupplier(() -> { return 2.0; });
assertEquals(2.0, result);
}

@Test
void call_globalCallMeIntConsumer_noThrow() {
MySwiftLibrary.globalCallMeIntConsumer((int a) -> { });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public func globalCallMeDoubleSupplier(run: () -> Double) -> Double {
run()
}

public func globalCallMeIntConsumer(run: (Int32) -> Void) {
run(1)
}

public func globalReceiveRawBuffer(buf: UnsafeRawBufferPointer) -> Int {
buf.count
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,9 @@ void call_globalCallMeDoubleSupplier_noThrow() {
double result = MySwiftLibrary.globalCallMeDoubleSupplier(() -> { return 2.0; });
assertEquals(2.0, result);
}

@Test
void call_globalCallMeIntConsumer_noThrow() {
MySwiftLibrary.globalCallMeIntConsumer((int a) -> { });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ public func globalCallMeDoubleSupplier(run: () -> Double) -> Double {
run()
}

public func globalCallMeIntConsumer(run: (Int32) -> Void) {
run(1)
}

public func closureMultipleArguments(
input1: Int64,
input2: Int64,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,9 @@ void globalCallMeDoubleSupplier() {
double result = MySwiftLibrary.globalCallMeDoubleSupplier(() -> 2.0);
assertEquals(2.0, result);
}

@Test
void globalCallMeIntConsumer() {
MySwiftLibrary.globalCallMeIntConsumer((int a) -> {});
}
}
4 changes: 4 additions & 0 deletions Sources/ExampleSwiftLibrary/MySwiftLibrary.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ public func globalCallMeDoubleSupplier(run: () -> Double) -> Double {
run()
}

public func globalCallMeIntConsumer(run: (Int32) -> Void) {
run(1)
}

public func globalReceiveRawBuffer(buf: UnsafeRawBufferPointer) -> Int {
buf.count
}
Expand Down
5 changes: 5 additions & 0 deletions Sources/JExtractSwiftLib/JavaTypes/JavaType+JDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ extension JavaType {
.class(package: "java.util.function", name: "DoubleSupplier")
}

/// The description of the type java.util.function.IntConsumer.
static var javaUtilFunctionIntConsumer: JavaType {
.class(package: "java.util.function", name: "IntConsumer")
}

/// The description of the type java.lang.Class.
static var javaLangClass: JavaType {
.class(package: "java.lang", name: "Class")
Expand Down
51 changes: 38 additions & 13 deletions Sources/JExtractSwiftLib/KnownFunctionalInterfaces.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,20 @@ struct KnownJavaFunctionalInterface: Sendable {
result: .double
)

static let intConsumer = KnownJavaFunctionalInterface(
JavaType.javaUtilFunctionIntConsumer,
method: "accept",
parameters: [.int],
result: .void
)

static let all: [KnownJavaFunctionalInterface] = [
.runnable,
.booleanSupplier,
.intSupplier,
.longSupplier,
.doubleSupplier,
.intConsumer,
]

static func find(parameters: [JavaType], result: JavaType) -> KnownJavaFunctionalInterface? {
Expand All @@ -88,20 +96,37 @@ struct KnownJavaFunctionalInterface: Sendable {

let parameters = functionType.parameters
let result = functionType.resultType
return switch (parameters, result) {
case ([], _) where result.isVoid:
runnable
case ([], _) where result.isBoolean:
booleanSupplier
case ([], _) where result.isInt32:
intSupplier
case ([], _) where result.isInt64:
longSupplier
case ([], _) where result.isDouble:
doubleSupplier
default:
nil

// Runnable & Suppliers
if parameters == [] {
return switch () {
case _ where result.isVoid:
runnable
case _ where result.isBoolean:
booleanSupplier
case _ where result.isInt32:
intSupplier
case _ where result.isInt64:
longSupplier
case _ where result.isDouble:
doubleSupplier
default:
nil
}
}

// Consumers
if parameters.count == 1 && result.isVoid {
let parameter = parameters[0].type
return switch () {
case _ where parameter.isInt32:
intConsumer
default:
nil
}
}

return nil
}

static func find(_ functionType: JNISwift2JavaGenerator.TranslatedFunctionType) -> KnownJavaFunctionalInterface? {
Expand Down
47 changes: 47 additions & 0 deletions Tests/JExtractSwiftTests/FuncCallbackImportTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ final class FuncCallbackImportTests {
public func callMeIntSupplier(callback: () -> Int32)
public func callMeLongSupplier(callback: () -> Int64)
public func callMeDoubleSupplier(callback: () -> Double)

public func callMeIntConsumer(callback: (Int32) -> Void)

public func callMeMore(callback: (UnsafeRawPointer, Float) -> Int, fn: () -> ())
public func withBuffer(body: (UnsafeRawBufferPointer) -> Int)
"""
Expand Down Expand Up @@ -341,6 +344,50 @@ final class FuncCallbackImportTests {
)
}

@Test("Import: public func callMeDoubleSupplier(callback: (Int32) -> Void)")
func func_callMeIntConsumerFunc_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 == "callMeIntConsumer" }!

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 callMeIntConsumer(callback: (Int32) -> Void)
* }
*/
public static void callMeIntConsumer(java.util.function.IntConsumer callback) {
try(var arena$ = Arena.ofConfined()) {
swiftjava___FakeModule_callMeIntConsumer_callback.call(callMeIntConsumer.$toUpcallStub(callback, arena$));
}
}
"""
]
)
}


@Test("Import: public func callMeMore(callback: (UnsafeRawPointer, Float) -> Int, fn: () -> ())")
func func_callMeMoreFunc_callback() throws {
var config = Configuration()
Expand Down
53 changes: 53 additions & 0 deletions Tests/JExtractSwiftTests/JNI/JNIClosureTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ struct JNIClosureTests {
public func closureIntSupplier(closure: () -> Int32) {}
public func closureLongSupplier(closure: () -> Int64) {}
public func closureDoubleSupplier(closure: () -> Double) {}

public func closureIntConsumer(closure: (Int32) -> Void) {}

public func closureWithArgumentsAndReturn(closure: (Int64, Bool) -> Int64) {}
"""

Expand Down Expand Up @@ -152,6 +155,31 @@ struct JNIClosureTests {
)
}

@Test
func closureIntConsumer_javaBindings() throws {
try assertOutput(
input: source,
.jni,
.java,
expectedChunks: [
"""
/**
* Downcall to Swift:
* {@snippet lang=swift :
* public func closureIntConsumer(closure: (Int32) -> Void)
* }
*/
public static void closureIntConsumer(java.util.function.IntConsumer closure) {
SwiftModule.$closureIntConsumer(closure);
}
""",
"""
private static native void $closureIntConsumer(java.util.function.IntConsumer closure);
""",
]
)
}

@Test
func emptyClosure_swiftThunks() throws {
try assertOutput(
Expand Down Expand Up @@ -277,6 +305,31 @@ struct JNIClosureTests {
)
}

@Test
func closureIntConsumer_swiftThunks() throws {
try assertOutput(
input: source,
.jni,
.swift,
detectChunkByInitialLines: 1,
expectedChunks: [
"""
@_cdecl("Java_com_example_swift_SwiftModule__00024closureIntConsumer__Ljava_util_function_IntConsumer_2")
public func Java_com_example_swift_SwiftModule__00024closureIntConsumer__Ljava_util_function_IntConsumer_2(environment: UnsafeMutablePointer<JNIEnv?>!, thisClass: jclass, closure: jobject?) {
SwiftModule.closureIntConsumer(closure: {
let class$ = environment.interface.GetObjectClass(environment, closure)
let methodID$ = environment.interface.GetMethodID(environment, class$, "accept", "(I)V")!
environment.interface.DeleteLocalRef(environment, class$)
let arguments$: [jvalue] = [_0.getJValue(in: environment)]
environment.interface.CallVoidMethodA(environment, closure, methodID$, arguments$)
}
)
}
"""
]
)
}

@Test
func closureWithArgumentsAndReturn_javaBindings() throws {
try assertOutput(
Expand Down
Loading