Skip to content

Commit 3063fef

Browse files
committed
final boilerplate additions for ffm array returns
1 parent 6df1b41 commit 3063fef

File tree

4 files changed

+96
-47
lines changed

4 files changed

+96
-47
lines changed

Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/FFMArraysTest.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ void test_sumAllByteArrayElements_throughMemorySegment() {
4141
var bytesCopy = arena.allocateFrom(ValueLayout.JAVA_BYTE, bytes);
4242
var swiftSideSum = MySwiftLibrary.sumAllByteArrayElements(bytesCopy, bytes.length);
4343

44-
System.out.println("swiftSideSum = " + swiftSideSum);
45-
4644
int javaSideSum = IntStream.range(0, bytes.length).map(i -> bytes[i]).sum();
4745
assertEquals(javaSideSum, swiftSideSum);
4846
}
@@ -55,9 +53,15 @@ void test_sumAllByteArrayElements_arrayCopy() {
5553

5654
var swiftSideSum = MySwiftLibrary.sumAllByteArrayElements(bytes);
5755

58-
System.out.println("swiftSideSum = " + swiftSideSum);
59-
6056
int javaSideSum = IntStream.range(0, bytes.length).map(i -> bytes[i]).sum();
6157
assertEquals(javaSideSum, swiftSideSum);
6258
}
59+
60+
@Test
61+
void test_getArray() {
62+
AtomicLong bufferSize = new AtomicLong();
63+
byte[] javaBytes = MySwiftLibrary.getArray(); // automatically converted [UInt8] to byte[]
64+
65+
assertArrayEquals(new byte[]{1, 2, 3}, javaBytes);
66+
}
6367
}

Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/WithBufferTest.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,4 @@ void test_withBuffer() {
3939
assertEquals(124, bufferSize.get());
4040
}
4141

42-
@Test
43-
void test_getArray() {
44-
AtomicLong bufferSize = new AtomicLong();
45-
byte[] javaBytes = MySwiftLibrary.getArray()
46-
47-
assertEquals({1, 2, 3}, bufferSize.get());
48-
}
4942
}

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift

Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,7 @@ extension FFMSwift2JavaGenerator {
182182
/// }
183183
/// ```
184184
///
185-
/// If a `functionBody` is provided the `Function` becomes a `final static class`,
186-
/// with the specified implementation as the implementation of the `apply` method.
185+
/// If a `functionBody` is provided, a `Function$Impl` class will be emitted as well.
187186
func printFunctionPointerParameterDescriptorClass(
188187
_ printer: inout CodePrinter,
189188
_ name: String,
@@ -217,27 +216,28 @@ extension FFMSwift2JavaGenerator {
217216
private static class \(name)
218217
"""
219218
) { printer in
219+
printer.print(
220+
"""
221+
@FunctionalInterface
222+
public interface Function {
223+
\(cResultType.javaType) apply(\(paramDecls.joined(separator: ", ")));
224+
}
225+
"""
226+
)
227+
220228
if let impl {
221229
printer.print(
222230
"""
223-
public final static class Function {
231+
public final static class Function$Impl implements Function {
224232
\(impl.members.joinedJavaStatements(indent: 2))
225-
\(cResultType.javaType) apply(\(paramDecls.joined(separator: ", "))) {
233+
public \(cResultType.javaType) apply(\(paramDecls.joined(separator: ", "))) {
226234
\(impl.body)
227235
}
228236
}
229237
"""
230238
)
231-
} else {
232-
printer.print(
233-
"""
234-
@FunctionalInterface
235-
public interface Function {
236-
\(cResultType.javaType) apply(\(paramDecls.joined(separator: ", ")));
237-
}
238-
"""
239-
)
240-
}
239+
}
240+
241241
printFunctionDescriptorDefinition(&printer, cResultType, cParams)
242242
printer.print(
243243
"""
@@ -456,19 +456,20 @@ extension FFMSwift2JavaGenerator {
456456
downCallArguments.append(varName)
457457
}
458458

459+
let thunkName = thunkNameRegistry.functionThunkName(decl: decl)
460+
459461
if let outCallback = translatedSignature.result.outCallback {
460462
let funcName = outCallback.name
461463
assert(funcName.first == "$", "OutCallback names must start with $")
462464
let varName = funcName.dropFirst()
463465
downCallArguments.append(
464466
"""
465-
swiftjava_SwiftModule_returnArray.\(outCallback.name).toUpcallStub(\(varName), arena$)
467+
\(thunkName).\(outCallback.name).toUpcallStub(\(varName), arena$)
466468
"""
467469
)
468470
}
469471

470472
//=== Part 3: Downcall.
471-
let thunkName = thunkNameRegistry.functionThunkName(decl: decl)
472473
let downCall = "\(thunkName).call(\(downCallArguments.joined(separator: ", ")))"
473474

474475
//=== Part 4: Convert the return value.
@@ -527,7 +528,9 @@ extension FFMSwift2JavaGenerator.JavaConversionStep {
527528
/// Whether the conversion uses SwiftArena.
528529
var requiresSwiftArena: Bool {
529530
switch self {
530-
case .placeholder, .placeholderForDowncall, .explodedName, .constant, .readMemorySegment:
531+
case .placeholder, .placeholderForDowncall, .placeholderForSwiftThunkName:
532+
return false
533+
case .explodedName, .constant, .readMemorySegment, .javaNew:
531534
return false
532535
case .constructSwiftValue, .wrapMemoryAddressUnsafe:
533536
return true
@@ -536,6 +539,8 @@ extension FFMSwift2JavaGenerator.JavaConversionStep {
536539

537540
case .initializeResultWithUpcall(let steps, let result):
538541
return steps.contains { $0.requiresSwiftArena } || result.requiresSwiftArena
542+
case .introduceVariable(_, let value):
543+
return value.requiresSwiftArena
539544

540545
case .call(let inner, let base, _, _):
541546
return inner.requiresSwiftArena || (base?.requiresSwiftArena == true)
@@ -547,22 +552,26 @@ extension FFMSwift2JavaGenerator.JavaConversionStep {
547552
.swiftValueSelfSegment(let inner):
548553
return inner.requiresSwiftArena
549554

550-
case .commaSeparated(let list):
555+
case .commaSeparated(let list, _):
551556
return list.contains(where: { $0.requiresSwiftArena })
552557
}
553558
}
554559

555560
/// Whether the conversion uses temporary Arena.
556561
var requiresTemporaryArena: Bool {
557562
switch self {
558-
case .placeholder, .placeholderForDowncall, .explodedName, .constant:
563+
case .placeholder, .placeholderForDowncall, .placeholderForSwiftThunkName:
564+
return false
565+
case .explodedName, .constant, .javaNew:
559566
return false
560567
case .temporaryArena:
561568
return true
562569
case .readMemorySegment:
563570
return true
564571
case .initializeResultWithUpcall:
565572
return true
573+
case .introduceVariable(_, let value):
574+
return value.requiresTemporaryArena
566575
case .cast(let inner, _),
567576
.construct(let inner, _),
568577
.constructSwiftValue(let inner, _),
@@ -575,7 +584,7 @@ extension FFMSwift2JavaGenerator.JavaConversionStep {
575584
return withArena || inner.requiresTemporaryArena || args.contains(where: { $0.requiresTemporaryArena })
576585
case .property(let inner, _):
577586
return inner.requiresTemporaryArena
578-
case .commaSeparated(let list):
587+
case .commaSeparated(let list, _):
579588
return list.contains(where: { $0.requiresTemporaryArena })
580589
}
581590
}
@@ -594,17 +603,28 @@ extension FFMSwift2JavaGenerator.JavaConversionStep {
594603
} else {
595604
return "/*placeholderForDowncall undefined!*/"
596605
}
606+
case .placeholderForSwiftThunkName:
607+
if let placeholderForDowncall {
608+
let downcall = "\(placeholderForDowncall)"
609+
return String(downcall[..<(downcall.firstIndex(of: ".") ?? downcall.endIndex)]) // . separates thunk name from the `.call`
610+
} else {
611+
return "/*placeholderForDowncall undefined!*/"
612+
}
613+
614+
case .temporaryArena:
615+
return "arena$"
597616

598617
case .explodedName(let component):
599618
return "\(placeholder)_\(component)"
600619

601620
case .swiftValueSelfSegment:
602621
return "\(placeholder).$memorySegment()"
603622

604-
case .temporaryArena:
605-
return "arena$"
623+
case .javaNew(let value):
624+
return "new \(value.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall))"
606625

607626
case .initializeResultWithUpcall(let steps, _):
627+
// TODO: could we use the printing to introduce the upcall handle instead?
608628
return steps.map { step in
609629
var printer = CodePrinter()
610630
var out = ""
@@ -626,33 +646,39 @@ extension FFMSwift2JavaGenerator.JavaConversionStep {
626646

627647
// TODO: deduplicate with 'method'
628648
case .method(let inner, let methodName, let arguments, let withArena):
629-
let inner = inner.render(&printer, placeholder)
630-
let args = arguments.map { $0.render(&printer, placeholder) }
649+
let inner = inner.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall)
650+
let args = arguments.map { $0.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall) }
631651
let argsStr = (args + (withArena ? ["arena$"] : [])).joined(separator: " ,")
632652
return "\(inner).\(methodName)(\(argsStr))"
633653

634654
case .property(let inner, let propertyName):
635-
let inner = inner.render(&printer, placeholder)
655+
let inner = inner.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall)
636656
return "\(inner).\(propertyName)"
637657

638658
case .constructSwiftValue(let inner, let javaType):
639-
let inner = inner.render(&printer, placeholder)
659+
let inner = inner.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall)
640660
return "new \(javaType.className!)(\(inner), swiftArena$)"
641661

642662
case .wrapMemoryAddressUnsafe(let inner, let javaType):
643-
let inner = inner.render(&printer, placeholder)
663+
let inner = inner.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall)
644664
return "\(javaType.className!).wrapMemoryAddressUnsafe(\(inner), swiftArena$)"
645665

646666
case .construct(let inner, let javaType):
647-
let inner = inner.render(&printer, placeholder)
667+
let inner = inner.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall)
648668
return "new \(javaType)(\(inner))"
669+
670+
case .introduceVariable(let name, let value):
671+
let value = value.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall)
672+
return "var \(name) = \(value);"
649673

650674
case .cast(let inner, let javaType):
651-
let inner = inner.render(&printer, placeholder)
675+
let inner = inner.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall)
652676
return "(\(javaType)) \(inner)"
653677

654-
case .commaSeparated(let list):
655-
return list.map({ $0.render(&printer, placeholder)}).joined(separator: ", ")
678+
case .commaSeparated(let list, let separator):
679+
return list.map({
680+
$0.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall)
681+
}).joined(separator: separator)
656682

657683
case .constant(let value):
658684
return value

Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,15 @@ extension FFMSwift2JavaGenerator {
753753
body: "this.result = _0.reinterpret(_1).toArray(ValueLayout.JAVA_BYTE); // copy native Swift array to Java heap array"
754754
),
755755
conversion: .initializeResultWithUpcall([
756-
.constant("var _result_initialize = new swiftjava_SwiftModule_returnArray.$_result_initialize.Function()"),
756+
.introduceVariable(
757+
name: "_result_initialize",
758+
initializeWith: .javaNew(.commaSeparated([
759+
// We need to refer to the nested class that is created for this function.
760+
// The class that contains all the related functional interfaces is called the same
761+
// as the downcall function, so we use the thunk name to find this class/
762+
.placeholderForSwiftThunkName, .constant("$_result_initialize.Function$Impl()")
763+
], separator: "."))),
764+
// .constant("var = new \(.placeholderForDowncallThunkName).."),
757765
.placeholderForDowncall, // perform the downcall here
758766
],
759767
extractResult: .property(.constant("_result_initialize"), propertyName: "result"))
@@ -783,8 +791,16 @@ extension FFMSwift2JavaGenerator {
783791
/// The "downcall", e.g. `swiftjava_SwiftModule_returnArray.call(...)`.
784792
/// This can be used in combination with aggregate conversion steps to prepare a setup and processing of the downcall.
785793
case placeholderForDowncall
794+
795+
/// Placeholder for Swift thunk name, e.g. "swiftjava_SwiftModule_returnArray".
796+
///
797+
/// This is derived from the placeholderForDowncall substitution - could be done more cleanly,
798+
/// however this has the benefit of not needing to pass the name substituion separately.
799+
case placeholderForSwiftThunkName
786800

787801
/// The temporary `arena$` that is necessary to complete the conversion steps.
802+
///
803+
/// This is distinct from just a constant 'arena$' string, since it forces the creation of a temporary arena.
788804
case temporaryArena
789805

790806
/// The input exploded into components.
@@ -814,7 +830,7 @@ extension FFMSwift2JavaGenerator {
814830
.call(step, base: nil, function: function, withArena: withArena)
815831
}
816832

817-
// TODO: just use call instead?
833+
// TODO: just use make call more powerful and use it instead?
818834
/// Apply a method on the placeholder.
819835
/// If `withArena` is true, `arena$` argument is added.
820836
indirect case method(JavaConversionStep, methodName: String, arguments: [JavaConversionStep] = [], withArena: Bool)
@@ -826,17 +842,27 @@ extension FFMSwift2JavaGenerator {
826842
/// Call 'new \(Type)(\(placeholder), swiftArena$)'.
827843
indirect case constructSwiftValue(JavaConversionStep, JavaType)
828844

845+
/// Construct the type using the placeholder as arguments.
846+
indirect case construct(JavaConversionStep, JavaType)
847+
829848
/// Call the `MyType.wrapMemoryAddressUnsafe` in order to wrap a memory address using the Java binding type
830849
indirect case wrapMemoryAddressUnsafe(JavaConversionStep, JavaType)
831850

832-
/// Construct the type using the placeholder as arguments.
833-
indirect case construct(JavaConversionStep, JavaType)
851+
/// Introduce a local variable, e.g. `var result = new Something()`
852+
indirect case introduceVariable(name: String, initializeWith: JavaConversionStep)
834853

835854
/// Casting the placeholder to the certain type.
836855
indirect case cast(JavaConversionStep, JavaType)
856+
857+
/// Prefix the conversion step with a java `new`.
858+
///
859+
/// This is useful if constructing the value is complex and we use
860+
/// a combination of separated values and constants to do so; Generally prefer using `construct`
861+
/// if you only want to construct a "wrapper" for the current `.placeholder`.
862+
indirect case javaNew(JavaConversionStep)
837863

838864
/// Convert the results of the inner steps to a comma separated list.
839-
indirect case commaSeparated([JavaConversionStep])
865+
indirect case commaSeparated([JavaConversionStep], separator: String = ", ")
840866

841867
/// Refer an exploded argument suffixed with `_\(name)`.
842868
indirect case readMemorySegment(JavaConversionStep, as: JavaType)

0 commit comments

Comments
 (0)