Skip to content

Commit a5b2057

Browse files
committed
Prototyping Swift Array Accessor
1 parent 84587b4 commit a5b2057

File tree

12 files changed

+489
-30
lines changed

12 files changed

+489
-30
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import SwiftKitSwift
16+
17+
@_cdecl("swiftjava_manual_getArrayMySwiftClass")
18+
public func swiftjava_manual_getArrayMySwiftClass() -> UnsafeMutableRawPointer /* [MySwiftClass] */ {
19+
p("[thunk] swiftjava_manual_getArrayMySwiftClass")
20+
var array: [MySwiftClass] = getArrayMySwiftClass()
21+
p("[thunk] swiftjava_manual_getArrayMySwiftClass -> \(array)")
22+
// TODO: we need to retain it I guess as we escape it into Java
23+
var ptr: UnsafeRawBufferPointer!
24+
array.withUnsafeBytes {
25+
ptr = $0
26+
}
27+
p("[thunk] swiftjava_manual_getArrayMySwiftClass -> \(ptr)")
28+
29+
return UnsafeMutableRawPointer(mutating:ptr!.baseAddress)!
30+
}
31+
32+
@_cdecl("swiftjava_SwiftKitSwift_Array_count") // FIXME: hardcoded for MySwiftClass
33+
public func swiftjava_SwiftKitSwift_Array____count(
34+
rawPointer: UnsafeMutableRawPointer, // Array<T>
35+
elementType: UnsafeMutableRawPointer // Metadata of T
36+
) -> Int {
37+
print("[swift][\(#fileID):\(#line)](\(#function) passed in rawPointer = \(rawPointer)")
38+
print("[swift][\(#fileID):\(#line)](\(#function) passed in metadata = \(elementType)")
39+
40+
let array = rawPointer.assumingMemoryBound(to: [MySwiftClass].self)
41+
.pointee
42+
43+
print("[swift][\(#fileID):\(#line)](\(#function) ARRAY count = \(array.count)")
44+
print("[swift][\(#fileID):\(#line)](\(#function) ARRAY[0] = \(unsafeBitCast(array[0], to: UInt64.self))")
45+
return array.count
46+
}
47+
48+
@_cdecl("swiftjava_SwiftKitSwift_Array_get") // FIXME: hardcoded for MySwiftClass
49+
public func swiftjava_SwiftKitSwift_Array____get(
50+
rawPointer: UnsafeMutableRawPointer, // Array<T>
51+
index: Int,
52+
elementType: UnsafeMutableRawPointer // Metadata of T
53+
) -> UnsafeMutableRawPointer {
54+
print("[swift][\(#fileID):\(#line)](\(#function) passed in rawPointer = \(rawPointer)")
55+
print("[swift][\(#fileID):\(#line)](\(#function) passed in index = \(index)")
56+
print("[swift][\(#fileID):\(#line)](\(#function) passed in metadata = \(elementType)")
57+
58+
let array: UnsafeMutableBufferPointer<MySwiftClass> = UnsafeMutableBufferPointer(
59+
start: rawPointer.assumingMemoryBound(to: MySwiftClass.self),
60+
count: 999 // FIXME: we need this to be passed in
61+
)
62+
63+
print("[swift][\(#fileID):\(#line)](\(#function) ARRAY[\(index)] = \(unsafeBitCast(array[index], to: UInt64.self))")
64+
let object = array[index]
65+
66+
let objectPointer = unsafeBitCast(object, to: UnsafeMutableRawPointer.self)
67+
return _swiftjava_swift_retain(object: objectPointer)
68+
}

Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftLibrary.swift

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,20 @@ public func globalCallMeRunnable(run: () -> ()) {
4747
run()
4848
}
4949

50+
public func getArrayInt() -> [Int] {
51+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
52+
}
53+
54+
let DATA = [
55+
MySwiftClass(len: 1, cap: 11),
56+
MySwiftClass(len: 2, cap: 22),
57+
MySwiftClass(len: 3, cap: 33),
58+
]
59+
60+
public func getArrayMySwiftClass() -> [MySwiftClass] {
61+
DATA
62+
}
63+
5064
public class MySwiftClass {
5165

5266
public var len: Int
@@ -64,7 +78,7 @@ public class MySwiftClass {
6478

6579
deinit {
6680
let addr = unsafeBitCast(self, to: UInt64.self)
67-
p("Deinit, self = 0x\(String(addr, radix: 16, uppercase: true))")
81+
p("MySwiftClass.deinit, self = 0x\(String(addr, radix: 16, uppercase: true))")
6882
}
6983

7084
public var counter: Int32 = 0
@@ -77,6 +91,15 @@ public class MySwiftClass {
7791
p("i:\(i)")
7892
}
7993

94+
// TODO: workaround until we expose properties again
95+
public func getterForLen() -> Int {
96+
len
97+
}
98+
// TODO: workaround until we expose properties again
99+
public func getterForCap() -> Int {
100+
cap
101+
}
102+
80103
public func echoIntMethod(i: Int) -> Int {
81104
p("i:\(i)")
82105
return i
@@ -99,9 +122,9 @@ public class MySwiftClass {
99122

100123
// ==== Internal helpers
101124

102-
private func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {
103-
// print("[swift][\(file):\(line)](\(function)) \(msg)")
104-
// fflush(stdout)
125+
package func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {
126+
print("[swift][\(file):\(line)](\(function)) \(msg)")
127+
fflush(stdout)
105128
}
106129

107130
#if os(Linux)

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

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,19 @@
2121

2222
// Import javakit/swiftkit support libraries
2323
import org.swift.swiftkit.SwiftArena;
24+
import org.swift.swiftkit.SwiftArrayAccessor;
2425
import org.swift.swiftkit.SwiftKit;
2526
import org.swift.swiftkit.SwiftValueWitnessTable;
2627

28+
import java.lang.foreign.Arena;
29+
import java.lang.foreign.FunctionDescriptor;
30+
import java.lang.foreign.Linker;
31+
import java.lang.foreign.MemorySegment;
32+
import java.lang.invoke.MethodHandle;
2733
import java.util.Arrays;
34+
import java.util.Objects;
35+
36+
import static org.swift.swiftkit.SwiftValueLayout.SWIFT_POINTER;
2837

2938
public class HelloJava2Swift {
3039

@@ -38,26 +47,59 @@ public static void main(String[] args) {
3847
}
3948

4049
static void examples() {
41-
MySwiftLibrary.helloWorld();
50+
// MySwiftLibrary.helloWorld();
51+
//
52+
// MySwiftLibrary.globalTakeInt(1337);
53+
//
54+
// // Example of using an arena; MyClass.deinit is run at end of scope
55+
// try (var arena = SwiftArena.ofConfined()) {
56+
// MySwiftClass obj = new MySwiftClass(arena, 2222, 7777);
57+
//
58+
// // just checking retains/releases work
59+
// SwiftKit.retain(obj.$memorySegment());
60+
// SwiftKit.release(obj.$memorySegment());
61+
//
62+
// obj.voidMethod();
63+
// obj.takeIntMethod(42);
64+
// }
4265

43-
MySwiftLibrary.globalTakeInt(1337);
4466

45-
// Example of using an arena; MyClass.deinit is run at end of scope
46-
try (var arena = SwiftArena.ofConfined()) {
47-
MySwiftClass obj = new MySwiftClass(arena, 2222, 7777);
67+
// public func getArrayMySwiftClass() -> [MySwiftClass]
68+
SwiftArrayAccessor<MySwiftClass> arr = ManualImportedMethods.getArrayMySwiftClass();
4869

49-
// just checking retains/releases work
50-
SwiftKit.retain(obj.$memorySegment());
51-
SwiftKit.release(obj.$memorySegment());
70+
MySwiftClass first = arr.get(0, MySwiftClass::new);
71+
System.out.println("[java] first = " + first);
72+
73+
// FIXME: properties don't work yet, need the thunks!
74+
// System.out.println("[java] first.getLen() = " + first.getLen());
75+
// assert(first.getLen() == 1);
76+
// System.out.println("[java] first.getCap() = " + first.getCap());
77+
// assert(first.getCap() == 2);
78+
79+
System.out.println("[java] first.getterForLen() = " + first.getterForLen());
80+
System.out.println("[java] first.getForCap() = " + first.getterForCap());
81+
precondition(1, first.getterForLen());
82+
precondition(11, first.getterForCap());
83+
84+
MySwiftClass second = arr.get(1, MySwiftClass::new);
85+
System.out.println("[java] second = " + second);
86+
System.out.println("[java] second.getterForLen() = " + second.getterForLen());
87+
System.out.println("[java] second.getForCap() = " + second.getterForCap());
88+
precondition(2, second.getterForLen());
89+
precondition(22, second.getterForCap());
5290

53-
obj.voidMethod();
54-
obj.takeIntMethod(42);
55-
}
5691

5792
System.out.println("DONE.");
5893
}
5994

95+
private static void precondition(long expected, long got) {
96+
if (expected != got) {
97+
throw new AssertionError("Expected '" + expected + "', but got '" + got + "'!");
98+
}
99+
}
100+
60101
public static native long jniWriteString(String str);
61102
public static native long jniGetInt();
62103

63104
}
105+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
package com.example.swift;
16+
17+
import org.swift.swiftkit.SwiftArrayAccessor;
18+
import org.swift.swiftkit.SwiftKit;
19+
20+
import java.lang.foreign.Arena;
21+
import java.lang.foreign.FunctionDescriptor;
22+
import java.lang.foreign.Linker;
23+
import java.lang.foreign.MemorySegment;
24+
import java.lang.invoke.MethodHandle;
25+
26+
import static org.swift.swiftkit.SwiftValueLayout.SWIFT_POINTER;
27+
28+
public final class ManualImportedMethods {
29+
30+
private static class getArrayMySwiftClass {
31+
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
32+
/* -> */SWIFT_POINTER
33+
);
34+
public static final MemorySegment ADDR =
35+
SwiftKit.findOrThrow("swiftjava_manual_getArrayMySwiftClass");
36+
37+
public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
38+
}
39+
40+
41+
public static SwiftArrayAccessor<MySwiftClass> getArrayMySwiftClass() {
42+
MethodHandle mh = getArrayMySwiftClass.HANDLE;
43+
44+
Arena arena = Arena.ofAuto();
45+
try {
46+
if (SwiftKit.TRACE_DOWNCALLS) {
47+
SwiftKit.traceDowncall();
48+
}
49+
50+
MemorySegment arrayPointer = (MemorySegment) mh.invokeExact();
51+
return new SwiftArrayAccessor<>(
52+
arena,
53+
arrayPointer,
54+
/* element type = */MySwiftClass.TYPE_METADATA
55+
);
56+
} catch (Throwable e) {
57+
throw new RuntimeException("Failed to invoke Swift method", e);
58+
}
59+
}
60+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
package org.swift.swiftkit;
16+
17+
import com.example.swift.ManualImportedMethods;
18+
import com.example.swift.MySwiftClass;
19+
import com.example.swift.MySwiftLibrary;
20+
import org.junit.jupiter.api.BeforeAll;
21+
import org.junit.jupiter.api.Test;
22+
23+
import java.lang.foreign.Arena;
24+
import java.lang.foreign.FunctionDescriptor;
25+
import java.lang.foreign.Linker;
26+
import java.lang.foreign.MemorySegment;
27+
import java.lang.invoke.MethodHandle;
28+
29+
import static org.junit.jupiter.api.Assertions.assertEquals;
30+
import static org.swift.swiftkit.SwiftValueLayout.SWIFT_POINTER;
31+
32+
public class SwiftArrayTest {
33+
34+
@BeforeAll
35+
public static void setUp() {
36+
SwiftKit.loadLibrary("swiftCore");
37+
SwiftKit.loadLibrary("SwiftKitSwift");
38+
SwiftKit.loadLibrary("MySwiftLibrary");
39+
}
40+
41+
@Test
42+
public void array_of_MySwiftClass_get() {
43+
try (var arena = SwiftArena.ofConfined()) {
44+
SwiftArrayAccessor<MySwiftClass> arr = ManualImportedMethods.getArrayMySwiftClass();
45+
46+
MySwiftClass first = arr.get(0, MySwiftClass::new);
47+
System.out.println("[java] first = " + first);
48+
49+
// FIXME: properties don't work yet, need the thunks!
50+
// System.out.println("[java] first.getLen() = " + first.getLen());
51+
// assert(first.getLen() == 1);
52+
// System.out.println("[java] first.getCap() = " + first.getCap());
53+
// assert(first.getCap() == 2);
54+
55+
System.out.println("[java] first.getterForLen() = " + first.getterForLen());
56+
System.out.println("[java] first.getForCap() = " + first.getterForCap());
57+
assertEquals(1, first.getterForLen());
58+
assertEquals(11, first.getterForCap());
59+
60+
MySwiftClass second = arr.get(1, MySwiftClass::new);
61+
System.out.println("[java] second = " + second);
62+
System.out.println("[java] second.getterForLen() = " + second.getterForLen());
63+
System.out.println("[java] second.getForCap() = " + second.getterForCap());
64+
assertEquals(2, second.getterForLen());
65+
assertEquals(22, second.getterForCap());
66+
67+
}
68+
}
69+
}
70+

Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ extension Swift2JavaTranslator {
303303

304304
printer.print(
305305
"""
306-
static MemorySegment findOrThrow(String symbol) {
306+
public static MemorySegment findOrThrow(String symbol) {
307307
return SYMBOL_LOOKUP.find(symbol)
308308
.orElseThrow(() -> new UnsatisfiedLinkError("unresolved symbol: %s".formatted(symbol)));
309309
}
@@ -344,7 +344,7 @@ extension Swift2JavaTranslator {
344344
// https://bugs.openjdk.org/browse/JDK-8311090
345345
printer.print(
346346
"""
347-
static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup();
347+
public static final SymbolLookup SYMBOL_LOOKUP = getSymbolLookup();
348348
private static SymbolLookup getSymbolLookup() {
349349
// Ensure Swift and our Lib are loaded during static initialization of the class.
350350
SwiftKit.loadLibrary("swiftCore");
@@ -479,6 +479,17 @@ extension Swift2JavaTranslator {
479479
) {
480480
let descClassIdentifier = renderDescClassName(decl)
481481

482+
printer.print(
483+
"""
484+
/**
485+
* Wrap a memory segment which is pointing to an instance of {@code \(parentName.unqualifiedJavaTypeName)}.
486+
*/
487+
public \(parentName.unqualifiedJavaTypeName)(MemorySegment self) {
488+
this.selfMemorySegment = self;
489+
}
490+
"""
491+
)
492+
482493
printer.print(
483494
"""
484495
/**

0 commit comments

Comments
 (0)