Skip to content

Commit d85c8ed

Browse files
committed
[JExtract] Import any C compatible closure
Generalized closure parameter import, part 1. Start with C-compatible closures which don't need any conversions.
1 parent 6267c8f commit d85c8ed

File tree

14 files changed

+549
-122
lines changed

14 files changed

+549
-122
lines changed

Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftStruct.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ public struct MySwiftStruct {
4848
self.len
4949
}
5050

51+
public func withCapLen(_ body: (Int, Int) -> Void) {
52+
body(cap, len)
53+
}
54+
5155
public mutating func increaseCap(by value: Int) -> Int {
5256
precondition(value > 0)
5357
self.cap += value

Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ static void examples() {
6767

6868
MySwiftStruct swiftValue = MySwiftStruct.init(2222, 1111, arena);
6969
SwiftKit.trace("swiftValue.capacity = " + swiftValue.getCapacity());
70+
swiftValue.withCapLen((cap, len) -> {
71+
SwiftKit.trace("withCapLenCallback: cap=" + cap + ", len=" + len);
72+
});
7073
}
7174

7275
System.out.println("DONE.");

Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftLibraryTest.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,9 @@ void call_writeString_jni() {
6464
void call_globalCallMeRunnable() {
6565
CountDownLatch countDownLatch = new CountDownLatch(3);
6666

67-
MySwiftLibrary.globalCallMeRunnable(new Runnable() {
68-
@Override
69-
public void run() {
70-
countDownLatch.countDown();
71-
}
67+
MySwiftLibrary.globalCallMeRunnable.run fn = () -> {};
68+
MySwiftLibrary.globalCallMeRunnable(() -> {
69+
countDownLatch.countDown();
7270
});
7371
assertEquals(2, countDownLatch.getCount());
7472

Sources/JExtractSwift/CDeclLowering/Swift2JavaTranslator+FunctionLowering.swift

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,25 +276,59 @@ struct CdeclLowering {
276276
}
277277
return LoweredParameter(cdeclParameters: parameters, conversion: .tuplify(conversions))
278278

279-
case .function(let fn) where fn.parameters.isEmpty && fn.resultType.isVoid:
279+
case .function(let fn):
280+
let (loweredTy, conversion) = try lowerFunctionType(fn)
280281
return LoweredParameter(
281282
cdeclParameters: [
282283
SwiftParameter(
283284
convention: .byValue,
284285
parameterName: parameterName,
285-
type: .function(SwiftFunctionType(convention: .c, parameters: [], resultType: fn.resultType))
286+
type: loweredTy
286287
)
287288
],
288-
// '@convention(c) () -> ()' is compatible with '() -> Void'.
289-
conversion: .placeholder
289+
conversion: conversion
290290
)
291291

292-
case .function, .optional:
293-
// FIXME: Support other function types than '() -> Void'.
292+
case .optional:
294293
throw LoweringError.unhandledType(type)
295294
}
296295
}
297296

297+
func lowerFunctionType(
298+
_ fn: SwiftFunctionType
299+
) throws -> (type: SwiftType, conversion: ConversionStep) {
300+
var parameters: [SwiftParameter] = []
301+
var parameterConversions: [ConversionStep] = []
302+
303+
for parameter in fn.parameters {
304+
if let _ = try? CType(cdeclType: parameter.type) {
305+
parameters.append(SwiftParameter(convention: .byValue, type: parameter.type))
306+
parameterConversions.append(.placeholder)
307+
} else {
308+
// Non-trivial types are not yet supported.
309+
throw LoweringError.unhandledType(.function(fn))
310+
}
311+
}
312+
313+
let resultType: SwiftType
314+
let resultConversion: ConversionStep
315+
if let _ = try? CType(cdeclType: fn.resultType) {
316+
resultType = fn.resultType
317+
resultConversion = .placeholder
318+
} else {
319+
// Non-trivial types are not yet supported.
320+
throw LoweringError.unhandledType(.function(fn))
321+
}
322+
323+
// Ignore the conversions for now, since we don't support non-trivial types yet.
324+
_ = (parameterConversions, resultConversion)
325+
326+
return (
327+
type: .function(SwiftFunctionType(convention: .c, parameters: parameters, resultType: resultType)),
328+
conversion: .placeholder
329+
)
330+
}
331+
298332
/// Lower a Swift result type to cdecl out parameters and return type.
299333
///
300334
/// - Parameters:

Sources/JExtractSwift/Swift2JavaTranslator+JavaBindingsPrinting.swift

Lines changed: 159 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ extension Swift2JavaTranslator {
1919
_ printer: inout CodePrinter,
2020
_ decl: ImportedFunc
2121
) {
22-
guard let _ = translatedSignature(for: decl) else {
22+
guard let _ = translatedDecl(for: decl) else {
2323
// Failed to translate. Skip.
2424
return
2525
}
@@ -28,6 +28,8 @@ extension Swift2JavaTranslator {
2828

2929
printJavaBindingDescriptorClass(&printer, decl)
3030

31+
printJavaBindingWrapperHelperClass(&printer, decl)
32+
3133
// Render the "make the downcall" functions.
3234
printJavaBindingWrapperMethod(&printer, decl)
3335
}
@@ -38,9 +40,9 @@ extension Swift2JavaTranslator {
3840
_ decl: ImportedFunc
3941
) {
4042
let thunkName = thunkNameRegistry.functionThunkName(decl: decl)
41-
let translatedSignature = self.translatedSignature(for: decl)!
43+
let translated = self.translatedDecl(for: decl)!
4244
// 'try!' because we know 'loweredSignature' can be described with C.
43-
let cFunc = try! translatedSignature.loweredSignature.cFunctionDecl(cName: thunkName)
45+
let cFunc = try! translated.loweredSignature.cFunctionDecl(cName: thunkName)
4446

4547
printer.printBraceBlock(
4648
"""
@@ -52,37 +54,39 @@ extension Swift2JavaTranslator {
5254
private static class \(cFunc.name)
5355
"""
5456
) { printer in
55-
printFunctionDescriptorValue(&printer, cFunc)
57+
printFunctionDescriptorDefinition(&printer, cFunc.resultType, cFunc.parameters)
5658
printer.print(
5759
"""
58-
public static final MemorySegment ADDR =
60+
private static final MemorySegment ADDR =
5961
\(self.swiftModuleName).findOrThrow("\(cFunc.name)");
60-
public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
62+
private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
6163
"""
6264
)
6365
printJavaBindingDowncallMethod(&printer, cFunc)
66+
printParameterDescriptorClasses(&printer, cFunc)
6467
}
6568
}
6669

6770
/// Print the 'FunctionDescriptor' of the lowered cdecl thunk.
68-
func printFunctionDescriptorValue(
71+
func printFunctionDescriptorDefinition(
6972
_ printer: inout CodePrinter,
70-
_ cFunc: CFunction
73+
_ resultType: CType,
74+
_ parameters: [CParameter]
7175
) {
72-
printer.start("public static final FunctionDescriptor DESC = ")
76+
printer.start("private static final FunctionDescriptor DESC = ")
7377

74-
let isEmptyParam = cFunc.parameters.isEmpty
75-
if cFunc.resultType.isVoid {
78+
let isEmptyParam = parameters.isEmpty
79+
if resultType.isVoid {
7680
printer.print("FunctionDescriptor.ofVoid(", isEmptyParam ? .continue : .newLine)
7781
printer.indent()
7882
} else {
7983
printer.print("FunctionDescriptor.of(")
8084
printer.indent()
8185
printer.print("/* -> */", .continue)
82-
printer.print(cFunc.resultType.foreignValueLayout, .parameterNewlineSeparator(isEmptyParam))
86+
printer.print(resultType.foreignValueLayout, .parameterNewlineSeparator(isEmptyParam))
8387
}
8488

85-
for (param, isLast) in cFunc.parameters.withIsLast {
89+
for (param, isLast) in parameters.withIsLast {
8690
printer.print("/* \(param.name ?? "_"): */", .continue)
8791
printer.print(param.type.foreignValueLayout, .parameterNewlineSeparator(isLast))
8892
}
@@ -124,16 +128,143 @@ extension Swift2JavaTranslator {
124128
)
125129
}
126130

131+
func printParameterDescriptorClasses(
132+
_ printer: inout CodePrinter,
133+
_ cFunc: CFunction
134+
) {
135+
for param in cFunc.parameters {
136+
switch param.type {
137+
case .pointer(.function):
138+
let name = "$\(param.name!)"
139+
printFunctionPointerParameterDescriptorClass(&printer, name, param.type)
140+
default:
141+
continue
142+
}
143+
}
144+
}
145+
146+
/// Print a class describing a closure parameter type.
147+
/// ```
148+
/// class <paramter-name> {
149+
/// interface Function {
150+
/// <return-type> apply(<parameters>);
151+
/// }
152+
/// static final MethodDescriptor DESC = FunctionDescriptor.of(...);s
153+
/// static final MethodHandle HANDLE = SwiftKit.upcallHandle(Function.class, "apply", DESC);
154+
/// static MemorySegment toUpcallStub(Function fi, Arena arena) {
155+
/// return Linker.nativeLinker().upcallStub(HANDLE.bindTo(fi), DESC, arena);
156+
/// }
157+
/// }
158+
/// ```
159+
func printFunctionPointerParameterDescriptorClass(
160+
_ printer: inout CodePrinter,
161+
_ name: String,
162+
_ cType: CType
163+
) {
164+
guard case .pointer(.function(let cResultType, let cParameterTypes, variadic: false)) = cType else {
165+
preconditionFailure("must be a C function pointer type")
166+
}
167+
168+
let cParams = cParameterTypes.enumerated().map { i, ty in
169+
CParameter(name: "_\(i)", type: ty)
170+
}
171+
let paramDecls = cParams.map({"\($0.type.javaType) \($0.name!)"})
172+
173+
printer.printBraceBlock(
174+
"""
175+
/**
176+
* {snippet lang=c :
177+
* \(cType)
178+
* }
179+
*/
180+
private static class \(name)
181+
"""
182+
) { printer in
183+
printer.print(
184+
"""
185+
public interface Function {
186+
\(cResultType.javaType) apply(\(paramDecls.joined(separator: ", ")));
187+
}
188+
"""
189+
)
190+
printFunctionDescriptorDefinition(&printer, cResultType, cParams)
191+
printer.print(
192+
"""
193+
private static final MethodHandle HANDLE = SwiftKit.upcallHandle(Function.class, "apply", DESC);
194+
private static MemorySegment toUpcallStub(Function fi, Arena arena) {
195+
return Linker.nativeLinker().upcallStub(HANDLE.bindTo(fi), DESC, arena);
196+
}
197+
"""
198+
)
199+
}
200+
}
201+
202+
func printJavaBindingWrapperHelperClass(
203+
_ printer: inout CodePrinter,
204+
_ decl: ImportedFunc
205+
) {
206+
let translated = self.translatedDecl(for: decl)!
207+
let bindingDescriptorName = self.thunkNameRegistry.functionThunkName(decl: decl)
208+
if translated.functionTypes.isEmpty {
209+
return
210+
}
211+
212+
printer.printBraceBlock(
213+
"""
214+
private static class \(translated.name)
215+
"""
216+
) { printer in
217+
for functionType in translated.functionTypes {
218+
printJavaBindingWrapperFunctionTypeHelper(&printer, functionType, bindingDescriptorName)
219+
}
220+
}
221+
}
222+
223+
func printJavaBindingWrapperFunctionTypeHelper(
224+
_ printer: inout CodePrinter,
225+
_ functionType: TranslatedFunctionType,
226+
_ bindingDescriptorName: String
227+
) {
228+
let cdeclDescritor = "\(bindingDescriptorName).$\(functionType.name)"
229+
if functionType.isTrivial {
230+
printer.print(
231+
"""
232+
public interface \(functionType.name) extends \(cdeclDescritor).Function {
233+
default MemorySegment toUpcallStub(Arena arena) {
234+
return \(bindingDescriptorName).$\(functionType.name).toUpcallStub(this, arena);
235+
}
236+
}
237+
"""
238+
)
239+
} else {
240+
assertionFailure("should be unreachable at this point")
241+
let apiParams = functionType.parameters.flatMap {
242+
$0.javaParameters.map { param in "\(param.type) \(param.name)" }
243+
}
244+
245+
printer.print(
246+
"""
247+
public interface \(functionType.name) {
248+
\(functionType.result.javaResultType) apply(\(apiParams.joined(separator: ", ")));
249+
private default MemorySegment toUpcallStub(Arena arena) {
250+
return \(cdeclDescritor).toUpcallStub((<cdecl-params>) -> {
251+
<maybe-return> fi(<converted-args>)
252+
}, arena);
253+
}
254+
}
255+
"""
256+
)
257+
}
258+
}
259+
127260
/// Print the calling body that forwards all the parameters to the `methodName`,
128261
/// with adding `SwiftArena.ofAuto()` at the end.
129262
public func printJavaBindingWrapperMethod(
130263
_ printer: inout CodePrinter,
131-
_ decl: ImportedFunc) {
132-
let methodName: String = switch decl.apiKind {
133-
case .getter: "get\(decl.name.toCamelCase)"
134-
case .setter: "set\(decl.name.toCamelCase)"
135-
case .function, .initializer: decl.name
136-
}
264+
_ decl: ImportedFunc
265+
) {
266+
let translated = self.translatedDecl(for: decl)!
267+
let methodName = translated.name
137268

138269
var modifiers = "public"
139270
switch decl.functionSignature.selfParameter {
@@ -143,7 +274,7 @@ extension Swift2JavaTranslator {
143274
break
144275
}
145276

146-
let translatedSignature = self.translatedSignature(for: decl)!
277+
let translatedSignature = translated.translatedSignature
147278
let returnTy = translatedSignature.result.javaResultType
148279

149280
var paramDecls = translatedSignature.parameters
@@ -182,7 +313,7 @@ extension Swift2JavaTranslator {
182313
_ decl: ImportedFunc
183314
) {
184315
//=== Part 1: prepare temporary arena if needed.
185-
let translatedSignature = self.translatedSignature(for: decl)!
316+
let translatedSignature = self.translatedDecl(for: decl)!.translatedSignature
186317

187318
if translatedSignature.requiresTemporaryArena {
188319
printer.print("try(var arena$ = Arena.ofConfined()) {")
@@ -273,7 +404,7 @@ extension JavaConversionStep {
273404
/// Whether the conversion uses SwiftArena.
274405
var requiresSwiftArena: Bool {
275406
switch self {
276-
case .pass, .swiftValueSelfSegment, .construct, .cast, .call:
407+
case .pass, .swiftValueSelfSegment, .construct, .cast, .call, .method:
277408
return false
278409
case .constructSwiftValue:
279410
return true
@@ -285,7 +416,7 @@ extension JavaConversionStep {
285416
switch self {
286417
case .pass, .swiftValueSelfSegment, .construct, .constructSwiftValue, .cast:
287418
return false
288-
case .call(_, let withArena):
419+
case .call(_, let withArena), .method(_, _, let withArena):
289420
return withArena
290421
}
291422
}
@@ -297,7 +428,7 @@ extension JavaConversionStep {
297428
switch self {
298429
case .pass, .swiftValueSelfSegment:
299430
return true
300-
case .cast, .construct, .constructSwiftValue, .call:
431+
case .cast, .construct, .constructSwiftValue, .call, .method:
301432
return false
302433
}
303434
}
@@ -317,6 +448,10 @@ extension JavaConversionStep {
317448
let arenaArg = withArena ? ", arena$" : ""
318449
return "\(function)(\(placeholder)\(arenaArg))"
319450

451+
case .method(let methodName, let arguments, let withArena):
452+
let argsStr = (arguments + (withArena ? ["arena$"] : [])).joined(separator: " ,")
453+
return "\(placeholder).\(methodName)(\(argsStr))"
454+
320455
case .constructSwiftValue(let javaType):
321456
return "new \(javaType.className!)(\(placeholder), swiftArena$)"
322457

0 commit comments

Comments
 (0)