Skip to content

Commit 2d7fb88

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

File tree

13 files changed

+542
-37
lines changed

13 files changed

+542
-37
lines changed
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

+31-4
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ public func globalMakeInt() -> Int {
3535
return 42
3636
}
3737

38+
public func getMySwiftClassUntyped<T>(as: T.Type) -> Any {
39+
return MySwiftClass(len: 1, cap: 2)
40+
}
41+
3842
public func globalWriteString(string: String) -> Int {
3943
return string.count
4044
}
@@ -47,6 +51,20 @@ public func globalCallMeRunnable(run: () -> ()) {
4751
run()
4852
}
4953

54+
public func getArrayInt() -> [Int] {
55+
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
56+
}
57+
58+
let DATA = [
59+
MySwiftClass(len: 1, cap: 11),
60+
MySwiftClass(len: 2, cap: 22),
61+
MySwiftClass(len: 3, cap: 33),
62+
]
63+
64+
public func getArrayMySwiftClass() -> [MySwiftClass] {
65+
DATA
66+
}
67+
5068
public class MySwiftClass {
5169

5270
public var len: Int
@@ -64,7 +82,7 @@ public class MySwiftClass {
6482

6583
deinit {
6684
let addr = unsafeBitCast(self, to: UInt64.self)
67-
p("Deinit, self = 0x\(String(addr, radix: 16, uppercase: true))")
85+
p("MySwiftClass.deinit, self = 0x\(String(addr, radix: 16, uppercase: true))")
6886
}
6987

7088
public var counter: Int32 = 0
@@ -77,6 +95,15 @@ public class MySwiftClass {
7795
p("i:\(i)")
7896
}
7997

98+
// TODO: workaround until we expose properties again
99+
public func getterForLen() -> Int {
100+
len
101+
}
102+
// TODO: workaround until we expose properties again
103+
public func getterForCap() -> Int {
104+
cap
105+
}
106+
80107
public func echoIntMethod(i: Int) -> Int {
81108
p("i:\(i)")
82109
return i
@@ -99,9 +126,9 @@ public class MySwiftClass {
99126

100127
// ==== Internal helpers
101128

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)
129+
package func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {
130+
print("[swift][\(file):\(line)](\(function)) \(msg)")
131+
fflush(stdout)
105132
}
106133

107134
#if os(Linux)

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

+44-18
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,9 @@
1616

1717
// Import swift-extract generated sources
1818

19-
import com.example.swift.MySwiftLibrary;
20-
import com.example.swift.MySwiftClass;
21-
2219
// Import javakit/swiftkit support libraries
23-
import org.swift.swiftkit.SwiftArena;
20+
import org.swift.swiftkit.SwiftArrayRef;
2421
import org.swift.swiftkit.SwiftKit;
25-
import org.swift.swiftkit.SwiftValueWitnessTable;
26-
27-
import java.util.Arrays;
2822

2923
public class HelloJava2Swift {
3024

@@ -38,26 +32,58 @@ public static void main(String[] args) {
3832
}
3933

4034
static void examples() {
41-
MySwiftLibrary.helloWorld();
35+
// MySwiftLibrary.helloWorld();
36+
//
37+
// MySwiftLibrary.globalTakeInt(1337);
38+
//
39+
// // Example of using an arena; MyClass.deinit is run at end of scope
40+
// try (var arena = SwiftArena.ofConfined()) {
41+
// MySwiftClass obj = new MySwiftClass(arena, 2222, 7777);
42+
//
43+
// // just checking retains/releases work
44+
// SwiftKit.retain(obj.$memorySegment());
45+
// SwiftKit.release(obj.$memorySegment());
46+
//
47+
// obj.voidMethod();
48+
// obj.takeIntMethod(42);
49+
// }
4250

43-
MySwiftLibrary.globalTakeInt(1337);
51+
// public func getArrayMySwiftClass() -> [MySwiftClass]
52+
SwiftArrayRef<MySwiftClass> arr = ManualImportedMethods.getArrayMySwiftClass();
4453

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);
54+
MySwiftClass first = arr.get(0, MySwiftClass::new);
55+
System.out.println("[java] first = " + first);
4856

49-
// just checking retains/releases work
50-
SwiftKit.retain(obj.$memorySegment());
51-
SwiftKit.release(obj.$memorySegment());
57+
// FIXME: properties don't work yet, need the thunks!
58+
// System.out.println("[java] first.getLen() = " + first.getLen());
59+
// assert(first.getLen() == 1);
60+
// System.out.println("[java] first.getCap() = " + first.getCap());
61+
// assert(first.getCap() == 2);
62+
63+
System.out.println("[java] first.getterForLen() = " + first.getterForLen());
64+
System.out.println("[java] first.getForCap() = " + first.getterForCap());
65+
precondition(1, first.getterForLen());
66+
precondition(11, first.getterForCap());
67+
68+
MySwiftClass second = arr.get(1, MySwiftClass::new);
69+
System.out.println("[java] second = " + second);
70+
System.out.println("[java] second.getterForLen() = " + second.getterForLen());
71+
System.out.println("[java] second.getForCap() = " + second.getterForCap());
72+
precondition(2, second.getterForLen());
73+
precondition(22, second.getterForCap());
5274

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

5776
System.out.println("DONE.");
5877
}
5978

79+
private static void precondition(long expected, long got) {
80+
if (expected != got) {
81+
throw new AssertionError("Expected '" + expected + "', but got '" + got + "'!");
82+
}
83+
}
84+
6085
public static native long jniWriteString(String str);
6186
public static native long jniGetInt();
6287

6388
}
89+
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 com.example.swift;
16+
17+
import org.swift.swiftkit.SwiftArrayRef;
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+
static final String LIB_NAME = "MySwiftLibrary";
30+
31+
@SuppressWarnings("unused")
32+
private static final boolean INITIALIZED_LIBS = initializeLibs();
33+
static boolean initializeLibs() {
34+
System.loadLibrary(SwiftKit.STDLIB_DYLIB_NAME);
35+
System.loadLibrary("SwiftKitSwift");
36+
System.loadLibrary(LIB_NAME);
37+
return true;
38+
}
39+
40+
private static class getArrayMySwiftClass {
41+
public static final FunctionDescriptor DESC = FunctionDescriptor.of(
42+
/* -> */SWIFT_POINTER
43+
);
44+
public static final MemorySegment ADDR =
45+
SwiftKit.findOrThrow("swiftjava_manual_getArrayMySwiftClass");
46+
47+
public static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
48+
}
49+
50+
51+
public static SwiftArrayRef<MySwiftClass> getArrayMySwiftClass() {
52+
MethodHandle mh = getArrayMySwiftClass.HANDLE;
53+
54+
Arena arena = Arena.ofAuto();
55+
try {
56+
if (SwiftKit.TRACE_DOWNCALLS) {
57+
SwiftKit.traceDowncall();
58+
}
59+
60+
MemorySegment arrayPointer = (MemorySegment) mh.invokeExact();
61+
return new SwiftArrayRef<>(
62+
arena,
63+
arrayPointer,
64+
/* element type = */MySwiftClass.TYPE_METADATA
65+
);
66+
} catch (Throwable e) {
67+
throw new RuntimeException("Failed to invoke Swift method", e);
68+
}
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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 org.junit.jupiter.api.BeforeAll;
20+
import org.junit.jupiter.api.Test;
21+
22+
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
24+
public class SwiftArrayTest {
25+
26+
@BeforeAll
27+
public static void setUp() {
28+
SwiftKit.loadLibrary("swiftCore");
29+
SwiftKit.loadLibrary("SwiftKitSwift");
30+
SwiftKit.loadLibrary("MySwiftLibrary");
31+
}
32+
33+
@Test
34+
public void array_of_MySwiftClass_get() {
35+
try (var arena = SwiftArena.ofConfined()) {
36+
SwiftArrayRef<MySwiftClass> arr = ManualImportedMethods.getArrayMySwiftClass();
37+
38+
MySwiftClass first = arr.get(0, MySwiftClass::new);
39+
System.out.println("[java] first = " + first);
40+
41+
// FIXME: properties don't work yet, need the thunks!
42+
// System.out.println("[java] first.getLen() = " + first.getLen());
43+
// assert(first.getLen() == 1);
44+
// System.out.println("[java] first.getCap() = " + first.getCap());
45+
// assert(first.getCap() == 2);
46+
47+
System.out.println("[java] first.getterForLen() = " + first.getterForLen());
48+
System.out.println("[java] first.getForCap() = " + first.getterForCap());
49+
assertEquals(1, first.getterForLen());
50+
assertEquals(11, first.getterForCap());
51+
52+
MySwiftClass second = arr.get(1, MySwiftClass::new);
53+
System.out.println("[java] second = " + second);
54+
System.out.println("[java] second.getterForLen() = " + second.getterForLen());
55+
System.out.println("[java] second.getForCap() = " + second.getterForCap());
56+
assertEquals(2, second.getterForLen());
57+
assertEquals(22, second.getterForCap());
58+
59+
}
60+
}
61+
}
62+

Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift

+13-2
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)