Skip to content

Commit a774558

Browse files
committed
work on upcall
1 parent 26bb992 commit a774558

File tree

23 files changed

+349
-221
lines changed

23 files changed

+349
-221
lines changed

Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Async.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import SwiftJava
1616

1717
public func asyncSum(i1: Int64, i2: Int64) async -> Int64 {
18-
try? await Task.sleep(for: .milliseconds(500))
1918
return i1 + i2
2019
}
2120

@@ -29,6 +28,11 @@ public func asyncCopy(myClass: MySwiftClass) async throws -> MySwiftClass {
2928
return new
3029
}
3130

31+
public func asyncOptional(i: Int64) async throws -> Int64? {
32+
try await Task.sleep(for: .milliseconds(100))
33+
return i
34+
}
35+
3236
public func asyncRunningSum() async -> Int64 {
3337
let totalSum = await withTaskGroup(of: Int64.self) { group in
3438
// 1. Add child tasks to the group

Samples/SwiftJavaExtractJNISampleApp/src/jmh/java/com/example/swift/AsyncBenchmark.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,41 @@
2020
import org.swift.swiftkit.core.ConfinedSwiftMemorySession;
2121
import org.swift.swiftkit.core.SwiftArena;
2222

23-
import java.util.ArrayList;
24-
import java.util.List;
25-
import java.util.Optional;
26-
import java.util.OptionalInt;
23+
import java.util.*;
24+
import java.util.concurrent.CompletableFuture;
25+
import java.util.concurrent.ExecutorService;
26+
import java.util.concurrent.Executors;
2727
import java.util.concurrent.TimeUnit;
2828

2929
@BenchmarkMode(Mode.AverageTime)
3030
@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
3131
@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
32-
@OutputTimeUnit(TimeUnit.SECONDS)
33-
@Fork(value = 3, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED" })
32+
@OutputTimeUnit(TimeUnit.MICROSECONDS)
33+
@State(Scope.Benchmark)
34+
@Fork(value = 1, jvmArgsAppend = { "--enable-native-access=ALL-UNNAMED" })
3435
public class AsyncBenchmark {
36+
/**
37+
* Parameter for the number of parallel tasks to launch.
38+
*/
39+
@Param({"100", "500", "1000"})
40+
public int taskCount;
41+
42+
@Setup(Level.Trial)
43+
public void beforeAll() {}
44+
45+
@TearDown(Level.Trial)
46+
public void afterAll() {}
3547

3648
@Benchmark
37-
public long asyncSum() {
38-
return MySwiftLibrary.asyncRunningSum().join();
49+
public void asyncSum(Blackhole bh) {
50+
CompletableFuture<Void>[] futures = new CompletableFuture[taskCount];
51+
52+
// Launch all tasks in parallel using supplyAsync on our custom executor
53+
for (int i = 0; i < taskCount; i++) {
54+
futures[i] = MySwiftLibrary.asyncSum(10, 5).thenAccept(bh::consume);
55+
}
56+
57+
// Wait for all futures to complete.
58+
CompletableFuture.allOf(futures).join();
3959
}
4060
}

Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ extension JavaType {
2525
case .long: return "J"
2626
case .float: return "F"
2727
case .double: return "D"
28-
case .class(let package, let name):
28+
case .class(let package, let name, _):
2929
let nameWithInnerClasses = name.replacingOccurrences(of: ".", with: "$")
3030
if let package {
3131
return "L\(package.replacingOccurrences(of: ".", with: "/"))/\(nameWithInnerClasses);"
@@ -112,26 +112,27 @@ extension JavaType {
112112
}
113113
}
114114

115-
var wrapperClassIfNeeded: JavaType {
115+
/// Returns the boxed type, or self if the type is already a Java class.
116+
var boxedType: JavaType {
116117
switch self {
117118
case .boolean:
118-
return .class(package: nil, name: "Boolean")
119-
case .byte(let parameterAnnotations):
120-
return .class(package: nil, name: "Byte")
121-
case .char(let parameterAnnotations):
122-
return .class(package: nil, name: "Character")
123-
case .short(let parameterAnnotations):
124-
return .class(package: nil, name: "Short")
125-
case .int(let parameterAnnotations):
126-
return .class(package: nil, name: "Integer")
127-
case .long(let parameterAnnotations):
128-
return .class(package: nil, name: "Long")
119+
return .class(package: "java.lang", name: "Boolean")
120+
case .byte:
121+
return .class(package: "java.lang", name: "Byte")
122+
case .char:
123+
return .class(package: "java.lang", name: "Character")
124+
case .short:
125+
return .class(package: "java.lang", name: "Short")
126+
case .int:
127+
return .class(package: "java.lang", name: "Integer")
128+
case .long:
129+
return .class(package: "java.lang", name: "Long")
129130
case .float:
130-
return .class(package: nil, name: "Float")
131+
return .class(package: "java.lang", name: "Float")
131132
case .double:
132-
return .class(package: nil, name: "Double")
133+
return .class(package: "java.lang", name: "Double")
133134
case .void:
134-
return .class(package: nil, name: "Void")
135+
return .class(package: "java.lang", name: "Void")
135136
default:
136137
return self
137138
}

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,7 @@ extension FFMSwift2JavaGenerator {
453453
func renderMemoryLayoutValue(for javaType: JavaType) -> String {
454454
if let layout = ForeignValueLayout(javaType: javaType) {
455455
return layout.description
456-
} else if case .class(package: _, name: let customClass) = javaType {
456+
} else if case .class(package: _, name: let customClass, _) = javaType {
457457
return ForeignValueLayout(customType: customClass).description
458458
} else {
459459
fatalError("renderMemoryLayoutValue not supported for \(javaType)")

Sources/JExtractSwiftLib/ImportedDecls.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible {
193193
}
194194

195195
var isAsync: Bool {
196-
self.functionSignature.effectSpecifiers.contains(.async)
196+
self.functionSignature.isAsync
197197
}
198198

199199
init(

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ extension JNISwift2JavaGenerator {
522522

523523
// Indirect return receivers
524524
for outParameter in translatedFunctionSignature.resultType.outParameters {
525-
printer.print("\(outParameter.type) \(outParameter.name) = \(outParameter.allocation.render());")
525+
printer.print("\(outParameter.type) \(outParameter.name) = \(outParameter.allocation.render(type: outParameter.type));")
526526
arguments.append(outParameter.name)
527527
}
528528

Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,13 @@ extension JNISwift2JavaGenerator {
182182
}
183183

184184
// Swift -> Java
185-
let translatedFunctionSignature = try translate(
185+
var translatedFunctionSignature = try translate(
186186
functionSignature: decl.functionSignature,
187187
methodName: javaName,
188188
parentName: parentName
189189
)
190190
// Java -> Java (native)
191-
let nativeFunctionSignature = try nativeTranslation.translate(
191+
var nativeFunctionSignature = try nativeTranslation.translate(
192192
functionSignature: decl.functionSignature,
193193
translatedFunctionSignature: translatedFunctionSignature,
194194
methodName: javaName,
@@ -213,6 +213,16 @@ extension JNISwift2JavaGenerator {
213213
}
214214
}
215215

216+
// Handle async methods
217+
if decl.functionSignature.isAsync {
218+
self.convertToAsync(
219+
translatedFunctionSignature: &translatedFunctionSignature,
220+
nativeFunctionSignature: &nativeFunctionSignature,
221+
originalFunctionSignature: decl.functionSignature,
222+
mode: config.effectiveAsyncFuncMode
223+
)
224+
}
225+
216226
return TranslatedFunctionDecl(
217227
name: javaName,
218228
isStatic: decl.isStatic || !decl.hasParent || decl.isInitializer,
@@ -281,11 +291,7 @@ extension JNISwift2JavaGenerator {
281291
genericRequirements: functionSignature.genericRequirements
282292
)
283293

284-
var resultType = try translate(swiftResult: functionSignature.result)
285-
286-
if functionSignature.effectSpecifiers.contains(.async) {
287-
resultType = asyncResultConversion(result: resultType, mode: config.effectiveAsyncMode)
288-
}
294+
let resultType = try translate(swiftResult: functionSignature.result)
289295

290296
return TranslatedFunctionSignature(
291297
selfParameter: selfParameter,
@@ -476,49 +482,58 @@ extension JNISwift2JavaGenerator {
476482
}
477483
}
478484

479-
func asyncResultConversion(
480-
result: TranslatedResult,
481-
mode: JExtractAsyncMode
482-
) -> TranslatedResult {
485+
func convertToAsync(
486+
translatedFunctionSignature: inout TranslatedFunctionSignature,
487+
nativeFunctionSignature: inout NativeFunctionSignature,
488+
originalFunctionSignature: SwiftFunctionSignature,
489+
mode: JExtractAsyncFuncMode
490+
) {
483491
switch mode {
484492
case .completableFuture:
485-
let supplyAsyncBodyConversion: JavaNativeConversionStep = if result.javaType.isVoid {
486-
.aggregate([
487-
.print(result.conversion),
488-
.null
489-
])
490-
} else {
491-
result.conversion
492-
}
493+
// let supplyAsyncBodyConversion: JavaNativeConversionStep = if result.javaType.isVoid {
494+
// .aggregate([
495+
// .print(result.conversion),
496+
// .null
497+
// ])
498+
// } else {
499+
// result.conversion
500+
// }
501+
502+
// Update translated function
503+
504+
let nativeFutureType = JavaType.completableFuture(nativeFunctionSignature.result.javaType)
505+
506+
let futureOutParameter = OutParameter(
507+
name: "$future",
508+
type: nativeFutureType,
509+
allocation: .new
510+
)
493511

494-
return TranslatedResult(
495-
javaType: .class(package: "java.util.concurrent", name: "CompletableFuture<\(result.javaType.wrapperClassIfNeeded)>"),
512+
let result = translatedFunctionSignature.resultType
513+
translatedFunctionSignature.resultType = TranslatedResult(
514+
javaType: .completableFuture(translatedFunctionSignature.resultType.javaType),
496515
annotations: result.annotations,
497-
outParameters: result.outParameters,
498-
conversion: .method(.constant("java.util.concurrent.CompletableFuture"), function: "supplyAsync", arguments: [
499-
.lambda(body: supplyAsyncBodyConversion),
500-
.constant("SwiftAsync.SWIFT_ASYNC_EXECUTOR")
516+
outParameters: result.outParameters + [futureOutParameter],
517+
conversion: .aggregate(variable: nil, [
518+
.print(.placeholder), // Make the downcall
519+
.method(.constant("$future"), function: "thenApply", arguments: [
520+
.lambda(args: ["futureResult$"], body: .replacingPlaceholder(result.conversion, placeholder: "futureResult$"))
521+
])
501522
])
502523
)
503524

504-
case .future:
505-
let asyncBodyConversion: JavaNativeConversionStep = if result.javaType.isVoid {
506-
.aggregate([
507-
.print(result.conversion),
508-
.null
509-
])
510-
} else {
511-
result.conversion
512-
}
513-
514-
return TranslatedResult(
515-
javaType: .class(package: "java.util.concurrent", name: "Future<\(result.javaType.wrapperClassIfNeeded)>"),
516-
annotations: result.annotations,
517-
outParameters: result.outParameters,
518-
conversion: .method(.constant("SwiftAsync.SWIFT_ASYNC_EXECUTOR"), function: "submit", arguments: [
519-
.lambda(body: asyncBodyConversion)
520-
])
525+
// Update native function
526+
nativeFunctionSignature.result.javaType = .void
527+
nativeFunctionSignature.result.conversion = .asyncCompleteFuture(
528+
nativeFunctionSignature.result.conversion,
529+
swiftFunctionResultType: originalFunctionSignature.result.type,
530+
nativeReturnType: nativeFunctionSignature.result.javaType,
531+
outParameters: nativeFunctionSignature.result.outParameters
521532
)
533+
nativeFunctionSignature.result.outParameters.append(.init(name: "result_future", type: nativeFutureType))
534+
535+
case .future:
536+
fatalError()
522537
}
523538
}
524539

@@ -866,11 +881,15 @@ extension JNISwift2JavaGenerator {
866881
struct OutParameter {
867882
enum Allocation {
868883
case newArray(JavaType, size: Int)
884+
case new
869885

870-
func render() -> String {
886+
func render(type: JavaType) -> String {
871887
switch self {
872888
case .newArray(let javaType, let size):
873889
"new \(javaType)[\(size)]"
890+
891+
case .new:
892+
"new \(type)()"
874893
}
875894
}
876895
}
@@ -969,8 +988,8 @@ extension JNISwift2JavaGenerator {
969988
/// `return value`
970989
indirect case `return`(JavaNativeConversionStep)
971990

972-
/// `() -> { return body; }`
973-
indirect case lambda(body: JavaNativeConversionStep)
991+
/// `(args) -> { return body; }`
992+
indirect case lambda(args: [String] = [], body: JavaNativeConversionStep)
974993

975994
case null
976995

@@ -1094,9 +1113,9 @@ extension JNISwift2JavaGenerator {
10941113
let inner = inner.render(&printer, placeholder)
10951114
return "return \(inner);"
10961115

1097-
case .lambda(let body):
1116+
case .lambda(let args, let body):
10981117
var printer = CodePrinter()
1099-
printer.printBraceBlock("() ->") { printer in
1118+
printer.printBraceBlock("(\(args.joined(separator: ", "))) ->") { printer in
11001119
let body = body.render(&printer, placeholder)
11011120
if !body.isEmpty {
11021121
printer.print("return \(body);")
@@ -1167,7 +1186,7 @@ extension JNISwift2JavaGenerator {
11671186
case .return(let inner):
11681187
return inner.requiresSwiftArena
11691188

1170-
case .lambda(let body):
1189+
case .lambda(_, let body):
11711190
return body.requiresSwiftArena
11721191

11731192
case .print(let inner):

0 commit comments

Comments
 (0)