Skip to content

Commit 55c5fd4

Browse files
committed
Add tests
1 parent 61995aa commit 55c5fd4

File tree

8 files changed

+700
-1
lines changed

8 files changed

+700
-1
lines changed

Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftStruct.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,9 @@ public struct MySwiftStruct {
6161
public func makeRandomIntMethod() -> Int {
6262
return Int.random(in: 1..<256)
6363
}
64+
65+
public subscript() -> Int {
66+
get { return len }
67+
set { len = newValue }
68+
}
6469
}

Samples/SwiftJavaExtractFFMSampleApp/src/test/java/org/swift/swiftkitffm/MySwiftStructTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,15 @@ void create_struct() {
3333
assertEquals(len, struct.getLength());
3434
}
3535
}
36+
37+
@Test
38+
void testSubscript() {
39+
try (var arena = AllocatingSwiftArena.ofConfined()) {
40+
MySwiftStruct s = MySwiftStruct.init(1337, 42, arena);
41+
long currentValue = s.getSubscript();
42+
s.setSubscript(66);
43+
assertEquals(42, currentValue);
44+
assertEquals(66, s.getLength());
45+
}
46+
}
3647
}

Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftStruct.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,9 @@ public struct MySwiftStruct {
3838
self.cap += value
3939
return self.cap
4040
}
41+
42+
public subscript() -> Int64 {
43+
get { return len }
44+
set { len = newValue }
45+
}
4146
}

Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftStructTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,15 @@ void increaseCap() {
6161
assertEquals(1347, s.getCapacity());
6262
}
6363
}
64+
65+
@Test
66+
void testSubscript() {
67+
try (var arena = SwiftArena.ofConfined()) {
68+
MySwiftStruct s = MySwiftStruct.init(1337, 42, arena);
69+
long currentValue = s.getSubscript();
70+
s.setSubscript(66);
71+
assertEquals(42, currentValue);
72+
assertEquals(66, s.getLen());
73+
}
74+
}
6475
}

Sources/JExtractSwiftLib/Swift2JavaVisitor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ final class Swift2JavaVisitor {
342342
)
343343

344344
log.debug(
345-
"Record imported variable accessor \(kind == .getter ? "getter" : "setter"):\(node.qualifiedNameForDebug)"
345+
"Record imported variable accessor \(kind == .getter || kind == .subscriptGetter ? "getter" : "setter"):\(node.qualifiedNameForDebug)"
346346
)
347347
if let typeContext {
348348
typeContext.variables.append(imported)
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2025 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 JExtractSwiftLib
16+
import Testing
17+
18+
import JExtractSwiftLib
19+
import Testing
20+
21+
@Suite
22+
struct FFMSubscriptsTests {
23+
private let noParamsSubscriptSource = """
24+
public struct MyStruct {
25+
private var testVariable: Double = 0
26+
27+
public subscript() -> Double {
28+
get { return testVariable }
29+
set { testVariable = newValue }
30+
}
31+
}
32+
"""
33+
34+
private let subscriptWithParamsSource = """
35+
public struct MyStruct {
36+
private var testVariable: [Int32] = []
37+
38+
public subscript(index: Int32) -> Int32 {
39+
get { return testVariable[Int(index)] }
40+
set { testVariable[Int(index)] = newValue }
41+
}
42+
}
43+
"""
44+
45+
@Test("Test generation of JavaClass for subscript with no parameters")
46+
func generatesJavaClassForNoParams() throws {
47+
try assertOutput(
48+
input: noParamsSubscriptSource, .ffm, .java,
49+
expectedChunks: [
50+
"""
51+
// Generated by jextract-swift
52+
// Swift module: SwiftModule
53+
54+
package com.example.swift;
55+
56+
import org.swift.swiftkit.core.*;
57+
import org.swift.swiftkit.core.util.*;
58+
import org.swift.swiftkit.ffm.*;
59+
import org.swift.swiftkit.core.annotations.*;
60+
import java.lang.foreign.*;
61+
import java.lang.invoke.*;
62+
import java.util.*;
63+
import java.nio.charset.StandardCharsets;
64+
""",])
65+
try assertOutput(
66+
input: noParamsSubscriptSource, .ffm, .java,
67+
expectedChunks: [
68+
"""
69+
public final class MyStruct extends FFMSwiftInstance implements SwiftValue {
70+
static final String LIB_NAME = "SwiftModule";
71+
static final Arena LIBRARY_ARENA = Arena.ofAuto();
72+
@SuppressWarnings("unused")
73+
private static final boolean INITIALIZED_LIBS = initializeLibs();
74+
static boolean initializeLibs() {
75+
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_CORE);
76+
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS);
77+
System.loadLibrary(LIB_NAME);
78+
return true;
79+
}
80+
""",
81+
"""
82+
public static final SwiftAnyType TYPE_METADATA =
83+
new SwiftAnyType(SwiftRuntime.swiftjava.getType("SwiftModule", "MyStruct"));
84+
public final SwiftAnyType $swiftType() {
85+
return TYPE_METADATA;
86+
}
87+
88+
public static final GroupLayout $LAYOUT = (GroupLayout) SwiftValueWitnessTable.layoutOfSwiftType(TYPE_METADATA.$memorySegment());
89+
public final GroupLayout $layout() {
90+
return $LAYOUT;
91+
}
92+
93+
private MyStruct(MemorySegment segment, AllocatingSwiftArena arena) {
94+
super(segment, arena);
95+
}
96+
"""
97+
])
98+
try assertOutput(input: noParamsSubscriptSource, .ffm, .java, expectedChunks: [
99+
"""
100+
private static class swiftjava_SwiftModule_MyStruct_subscript$get {
101+
private static final FunctionDescriptor DESC = FunctionDescriptor.of(
102+
/* -> */SwiftValueLayout.SWIFT_DOUBLE,
103+
/* self: */SwiftValueLayout.SWIFT_POINTER
104+
);
105+
""",
106+
"""
107+
private static final MemorySegment ADDR =
108+
SwiftModule.findOrThrow("swiftjava_SwiftModule_MyStruct_subscript$get");
109+
""",
110+
"""
111+
private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
112+
public static double call(java.lang.foreign.MemorySegment self) {
113+
try {
114+
if (CallTraces.TRACE_DOWNCALLS) {
115+
CallTraces.traceDowncall(self);
116+
}
117+
return (double) HANDLE.invokeExact(self);
118+
} catch (Throwable ex$) {
119+
throw new AssertionError("should not reach here", ex$);
120+
}
121+
}
122+
""",
123+
"""
124+
public double getSubscript() {
125+
$ensureAlive();
126+
return swiftjava_SwiftModule_MyStruct_subscript$get.call(this.$memorySegment());
127+
""",
128+
])
129+
try assertOutput(input: noParamsSubscriptSource, .ffm, .java, expectedChunks: [
130+
"""
131+
private static class swiftjava_SwiftModule_MyStruct_subscript$set {
132+
private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(
133+
/* newValue: */SwiftValueLayout.SWIFT_DOUBLE,
134+
/* self: */SwiftValueLayout.SWIFT_POINTER
135+
);
136+
private static final MemorySegment ADDR =
137+
SwiftModule.findOrThrow("swiftjava_SwiftModule_MyStruct_subscript$set");
138+
private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
139+
public static void call(double newValue, java.lang.foreign.MemorySegment self) {
140+
try {
141+
if (CallTraces.TRACE_DOWNCALLS) {
142+
CallTraces.traceDowncall(newValue, self);
143+
}
144+
HANDLE.invokeExact(newValue, self);
145+
} catch (Throwable ex$) {
146+
throw new AssertionError("should not reach here", ex$);
147+
}
148+
}
149+
""",
150+
"""
151+
public void setSubscript(double newValue) {
152+
$ensureAlive();
153+
swiftjava_SwiftModule_MyStruct_subscript$set.call(newValue, this.$memorySegment());
154+
"""
155+
])
156+
}
157+
158+
@Test("Test generation of JavaClass for subscript with parameters")
159+
func generatesJavaClassForParameters() throws {
160+
try assertOutput(
161+
input: subscriptWithParamsSource, .ffm, .java,
162+
expectedChunks: [
163+
"""
164+
// Generated by jextract-swift
165+
// Swift module: SwiftModule
166+
167+
package com.example.swift;
168+
169+
import org.swift.swiftkit.core.*;
170+
import org.swift.swiftkit.core.util.*;
171+
import org.swift.swiftkit.ffm.*;
172+
import org.swift.swiftkit.core.annotations.*;
173+
import java.lang.foreign.*;
174+
import java.lang.invoke.*;
175+
import java.util.*;
176+
import java.nio.charset.StandardCharsets;
177+
""",])
178+
try assertOutput(
179+
input: noParamsSubscriptSource, .ffm, .java,
180+
expectedChunks: [
181+
"""
182+
public final class MyStruct extends FFMSwiftInstance implements SwiftValue {
183+
static final String LIB_NAME = "SwiftModule";
184+
static final Arena LIBRARY_ARENA = Arena.ofAuto();
185+
@SuppressWarnings("unused")
186+
private static final boolean INITIALIZED_LIBS = initializeLibs();
187+
static boolean initializeLibs() {
188+
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_CORE);
189+
System.loadLibrary(SwiftLibraries.LIB_NAME_SWIFT_RUNTIME_FUNCTIONS);
190+
System.loadLibrary(LIB_NAME);
191+
return true;
192+
}
193+
""",
194+
"""
195+
public static final SwiftAnyType TYPE_METADATA =
196+
new SwiftAnyType(SwiftRuntime.swiftjava.getType("SwiftModule", "MyStruct"));
197+
public final SwiftAnyType $swiftType() {
198+
return TYPE_METADATA;
199+
}
200+
201+
public static final GroupLayout $LAYOUT = (GroupLayout) SwiftValueWitnessTable.layoutOfSwiftType(TYPE_METADATA.$memorySegment());
202+
public final GroupLayout $layout() {
203+
return $LAYOUT;
204+
}
205+
206+
private MyStruct(MemorySegment segment, AllocatingSwiftArena arena) {
207+
super(segment, arena);
208+
}
209+
"""
210+
])
211+
try assertOutput(input: subscriptWithParamsSource, .ffm, .java, expectedChunks: [
212+
"""
213+
private static class swiftjava_SwiftModule_MyStruct_subscript$get {
214+
private static final FunctionDescriptor DESC = FunctionDescriptor.of(
215+
/* -> */SwiftValueLayout.SWIFT_INT32,
216+
/* index: */SwiftValueLayout.SWIFT_INT32,
217+
/* self: */SwiftValueLayout.SWIFT_POINTER
218+
);
219+
private static final MemorySegment ADDR =
220+
SwiftModule.findOrThrow("swiftjava_SwiftModule_MyStruct_subscript$get");
221+
private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
222+
public static int call(int index, java.lang.foreign.MemorySegment self) {
223+
try {
224+
if (CallTraces.TRACE_DOWNCALLS) {
225+
CallTraces.traceDowncall(index, self);
226+
}
227+
return (int) HANDLE.invokeExact(index, self);
228+
} catch (Throwable ex$) {
229+
throw new AssertionError("should not reach here", ex$);
230+
}
231+
}
232+
""",
233+
"""
234+
public int getSubscript(int index) {
235+
$ensureAlive();
236+
return swiftjava_SwiftModule_MyStruct_subscript$get.call(index, this.$memorySegment());
237+
""",
238+
])
239+
try assertOutput(input: subscriptWithParamsSource, .ffm, .java, expectedChunks: [
240+
"""
241+
private static class swiftjava_SwiftModule_MyStruct_subscript$set {
242+
private static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(
243+
/* index: */SwiftValueLayout.SWIFT_INT32,
244+
/* newValue: */SwiftValueLayout.SWIFT_INT32,
245+
/* self: */SwiftValueLayout.SWIFT_POINTER
246+
);
247+
private static final MemorySegment ADDR =
248+
SwiftModule.findOrThrow("swiftjava_SwiftModule_MyStruct_subscript$set");
249+
private static final MethodHandle HANDLE = Linker.nativeLinker().downcallHandle(ADDR, DESC);
250+
public static void call(int index, int newValue, java.lang.foreign.MemorySegment self) {
251+
try {
252+
if (CallTraces.TRACE_DOWNCALLS) {
253+
CallTraces.traceDowncall(index, newValue, self);
254+
}
255+
HANDLE.invokeExact(index, newValue, self);
256+
} catch (Throwable ex$) {
257+
throw new AssertionError("should not reach here", ex$);
258+
}
259+
}
260+
""",
261+
"""
262+
public void setSubscript(int index, int newValue) {
263+
$ensureAlive();
264+
swiftjava_SwiftModule_MyStruct_subscript$set.call(index, newValue, this.$memorySegment());
265+
""",
266+
])
267+
}
268+
269+
@Test("Test generation of Swift thunks for subscript without parameters")
270+
func subscriptWithoutParamsMethodSwiftThunk() throws {
271+
try assertOutput(
272+
input: noParamsSubscriptSource,
273+
.ffm,
274+
.swift,
275+
expectedChunks: [
276+
"""
277+
@_cdecl("swiftjava_SwiftModule_MyStruct_subscript$get")
278+
public func swiftjava_SwiftModule_MyStruct_subscript$get(_ self: UnsafeRawPointer) -> Double {
279+
return self.assumingMemoryBound(to: MyStruct.self).pointee[]
280+
}
281+
""",
282+
"""
283+
@_cdecl("swiftjava_SwiftModule_MyStruct_subscript$set")
284+
public func swiftjava_SwiftModule_MyStruct_subscript$set(_ newValue: Double, _ self: UnsafeMutableRawPointer) {
285+
self.assumingMemoryBound(to: MyStruct.self).pointee[] = newValue
286+
}
287+
"""
288+
]
289+
)
290+
}
291+
292+
@Test("Test generation of Swift thunks for subscript with parameters")
293+
func subscriptWithParamsMethodSwiftThunk() throws {
294+
try assertOutput(
295+
input: subscriptWithParamsSource,
296+
.ffm,
297+
.swift,
298+
expectedChunks: [
299+
"""
300+
@_cdecl("swiftjava_SwiftModule_MyStruct_subscript$get")
301+
public func swiftjava_SwiftModule_MyStruct_subscript$get(_ index: Int32, _ self: UnsafeRawPointer) -> Int32 {
302+
return self.assumingMemoryBound(to: MyStruct.self).pointee[index]
303+
}
304+
""",
305+
"""
306+
@_cdecl("swiftjava_SwiftModule_MyStruct_subscript$set")
307+
public func swiftjava_SwiftModule_MyStruct_subscript$set(_ index: Int32, _ newValue: Int32, _ self: UnsafeMutableRawPointer) {
308+
self.assumingMemoryBound(to: MyStruct.self).pointee[index] = newValue
309+
}
310+
"""
311+
]
312+
)
313+
}
314+
}

0 commit comments

Comments
 (0)