Skip to content

Commit eea2ac7

Browse files
authored
Merge branch 'main' into wip-converge-tools-into-one
2 parents 1c30288 + 86b44fa commit eea2ac7

File tree

51 files changed

+2753
-758
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2753
-758
lines changed

Samples/SwiftKitSampleApp/Sources/MySwiftLibrary/MySwiftStruct.swift

+19-3
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414

1515
public struct MySwiftStruct {
1616

17-
public var number: Int
17+
private var cap: Int
18+
private var len: Int
1819

19-
public init(number: Int) {
20-
self.number = number
20+
public init(cap: Int, len: Int) {
21+
self.cap = cap
22+
self.len = len
2123
}
2224

2325
public func voidMethod() {
@@ -38,6 +40,20 @@ public struct MySwiftStruct {
3840
return 12
3941
}
4042

43+
public func getCapacity() -> Int {
44+
self.cap
45+
}
46+
47+
public func getLength() -> Int {
48+
self.len
49+
}
50+
51+
public mutating func increaseCap(by value: Int) -> Int {
52+
precondition(value > 0)
53+
self.cap += value
54+
return self.cap
55+
}
56+
4157
public func makeRandomIntMethod() -> Int {
4258
return Int.random(in: 1..<256)
4359
}

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

+9-11
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,13 @@
2424
import org.swift.swiftkit.SwiftKit;
2525
import org.swift.swiftkit.SwiftValueWitnessTable;
2626

27-
import java.util.Arrays;
28-
2927
public class HelloJava2Swift {
3028

3129
public static void main(String[] args) {
3230
boolean traceDowncalls = Boolean.getBoolean("jextract.trace.downcalls");
3331
System.out.println("Property: jextract.trace.downcalls = " + traceDowncalls);
3432

35-
System.out.print("Property: java.library.path = " +SwiftKit.getJavaLibraryPath());
33+
System.out.print("Property: java.library.path = " + SwiftKit.getJavaLibraryPath());
3634

3735
examples();
3836
}
@@ -44,23 +42,23 @@ static void examples() {
4442

4543
// Example of using an arena; MyClass.deinit is run at end of scope
4644
try (var arena = SwiftArena.ofConfined()) {
47-
MySwiftClass obj = new MySwiftClass(arena, 2222, 7777);
48-
49-
// just checking retains/releases work
50-
SwiftKit.retain(obj.$memorySegment());
51-
SwiftKit.release(obj.$memorySegment());
45+
MySwiftClass obj = new MySwiftClass(arena, 2222, 7777);
5246

53-
obj.voidMethod();
54-
obj.takeIntMethod(42);
47+
// just checking retains/releases work
48+
SwiftKit.retain(obj.$memorySegment());
49+
SwiftKit.release(obj.$memorySegment());
5550

56-
MySwiftStruct swiftValue = new MySwiftStruct(12);
51+
obj.voidMethod();
52+
obj.takeIntMethod(42);
5753

54+
MySwiftStruct swiftValue = new MySwiftStruct(arena, 2222, 1111);
5855
}
5956

6057
System.out.println("DONE.");
6158
}
6259

6360
public static native long jniWriteString(String str);
61+
6462
public static native long jniGetInt();
6563

6664
}

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

-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ public class MySwiftClassTest {
2828
void checkPaths(Throwable throwable) {
2929
var paths = SwiftKit.getJavaLibraryPath().split(":");
3030
for (var path : paths) {
31-
System.out.println("CHECKING PATH: " + path);
3231
Stream.of(new File(path).listFiles())
3332
.filter(file -> !file.isDirectory())
3433
.forEach((file) -> {

Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftStructTest.java renamed to Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java

+9-11
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,24 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
package com.example.swift;
15+
package org.swift.swiftkit;
1616

17-
import org.junit.jupiter.api.Disabled;
17+
import com.example.swift.MySwiftStruct;
1818
import org.junit.jupiter.api.Test;
19-
import org.swift.swiftkit.SwiftArena;
20-
import org.swift.swiftkit.SwiftKit;
21-
22-
import java.io.File;
23-
import java.util.stream.Stream;
2419

2520
import static org.junit.jupiter.api.Assertions.assertEquals;
2621

2722
public class MySwiftStructTest {
2823

2924
@Test
30-
void test_MySwiftClass_voidMethod() {
25+
void create_struct() {
3126
try (var arena = SwiftArena.ofConfined()) {
32-
MySwiftStruct o = new MySwiftStruct(12);
33-
// o.voidMethod();
27+
long cap = 12;
28+
long len = 34;
29+
var struct = new MySwiftStruct(arena, cap, len);
30+
31+
assertEquals(cap, struct.getCapacity());
32+
assertEquals(len, struct.getLength());
3433
}
3534
}
36-
3735
}

Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java

+38-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package org.swift.swiftkit;
1616

1717
import com.example.swift.MySwiftClass;
18+
import com.example.swift.MySwiftStruct;
1819
import org.junit.jupiter.api.BeforeAll;
1920
import org.junit.jupiter.api.Test;
2021
import org.junit.jupiter.api.condition.DisabledIf;
@@ -47,8 +48,44 @@ public void arena_releaseClassOnClose_class_ok() {
4748
release(obj.$memorySegment());
4849
assertEquals(1, retainCount(obj.$memorySegment()));
4950
}
51+
}
52+
53+
// FIXME: The destroy witness table call hangs on x86_64 platforms during the destroy witness table call
54+
// See: https://github.com/swiftlang/swift-java/issues/97
55+
@Test
56+
public void arena_markAsDestroyed_preventUseAfterFree_class() {
57+
MySwiftClass unsafelyEscapedOutsideArenaScope = null;
58+
59+
try (var arena = SwiftArena.ofConfined()) {
60+
var obj = new MySwiftClass(arena,1, 2);
61+
unsafelyEscapedOutsideArenaScope = obj;
62+
}
63+
64+
try {
65+
unsafelyEscapedOutsideArenaScope.echoIntMethod(1);
66+
fail("Expected exception to be thrown! Object was suposed to be dead.");
67+
} catch (IllegalStateException ex) {
68+
return;
69+
}
70+
}
71+
72+
// FIXME: The destroy witness table call hangs on x86_64 platforms during the destroy witness table call
73+
// See: https://github.com/swiftlang/swift-java/issues/97
74+
@Test
75+
public void arena_markAsDestroyed_preventUseAfterFree_struct() {
76+
MySwiftStruct unsafelyEscapedOutsideArenaScope = null;
5077

51-
// TODO: should we zero out the $memorySegment perhaps?
78+
try (var arena = SwiftArena.ofConfined()) {
79+
var s = new MySwiftStruct(arena,1, 2);
80+
unsafelyEscapedOutsideArenaScope = s;
81+
}
82+
83+
try {
84+
unsafelyEscapedOutsideArenaScope.echoIntMethod(1);
85+
fail("Expected exception to be thrown! Object was suposed to be dead.");
86+
} catch (IllegalStateException ex) {
87+
return;
88+
}
5289
}
5390

5491
@Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
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+
extension ConversionStep {
16+
/// Produce a conversion that takes in a value (or set of values) that
17+
/// would be available in a @_cdecl function to represent the given Swift
18+
/// type, and convert that to an instance of the Swift type.
19+
init(cdeclToSwift swiftType: SwiftType) throws {
20+
// If there is a 1:1 mapping between this Swift type and a C type, then
21+
// there is no translation to do.
22+
if let cType = try? CType(cdeclType: swiftType) {
23+
_ = cType
24+
self = .placeholder
25+
return
26+
}
27+
28+
switch swiftType {
29+
case .function, .optional:
30+
throw LoweringError.unhandledType(swiftType)
31+
32+
case .metatype(let instanceType):
33+
self = .unsafeCastPointer(
34+
.placeholder,
35+
swiftType: instanceType
36+
)
37+
38+
case .nominal(let nominal):
39+
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType {
40+
// Typed pointers
41+
if let firstGenericArgument = nominal.genericArguments?.first {
42+
switch knownType {
43+
case .unsafePointer, .unsafeMutablePointer:
44+
self = .typedPointer(
45+
.explodedComponent(.placeholder, component: "pointer"),
46+
swiftType: firstGenericArgument
47+
)
48+
return
49+
50+
case .unsafeBufferPointer, .unsafeMutableBufferPointer:
51+
self = .initialize(
52+
swiftType,
53+
arguments: [
54+
LabeledArgument(
55+
label: "start",
56+
argument: .typedPointer(
57+
.explodedComponent(.placeholder, component: "pointer"),
58+
swiftType: firstGenericArgument)
59+
),
60+
LabeledArgument(
61+
label: "count",
62+
argument: .explodedComponent(.placeholder, component: "count")
63+
)
64+
]
65+
)
66+
return
67+
68+
default:
69+
break
70+
}
71+
}
72+
}
73+
74+
// Arbitrary nominal types.
75+
switch nominal.nominalTypeDecl.kind {
76+
case .actor, .class:
77+
// For actor and class, we pass around the pointer directly.
78+
self = .unsafeCastPointer(.placeholder, swiftType: swiftType)
79+
case .enum, .struct, .protocol:
80+
// For enums, structs, and protocol types, we pass around the
81+
// values indirectly.
82+
self = .passIndirectly(
83+
.pointee(.typedPointer(.placeholder, swiftType: swiftType))
84+
)
85+
}
86+
87+
case .tuple(let elements):
88+
self = .tuplify(try elements.map { try ConversionStep(cdeclToSwift: $0) })
89+
}
90+
}
91+
92+
/// Produce a conversion that takes in a value that would be available in a
93+
/// Swift function and convert that to the corresponding cdecl values.
94+
///
95+
/// This conversion goes in the opposite direction of init(cdeclToSwift:), and
96+
/// is used for (e.g.) returning the Swift value from a cdecl function. When
97+
/// there are multiple cdecl values that correspond to this one Swift value,
98+
/// the result will be a tuple that can be assigned to a tuple of the cdecl
99+
/// values, e.g., (<placeholder>.baseAddress, <placeholder>.count).
100+
init(
101+
swiftToCDecl swiftType: SwiftType,
102+
stdlibTypes: SwiftStandardLibraryTypes
103+
) throws {
104+
// If there is a 1:1 mapping between this Swift type and a C type, then
105+
// there is no translation to do.
106+
if let cType = try? CType(cdeclType: swiftType) {
107+
_ = cType
108+
self = .placeholder
109+
return
110+
}
111+
112+
switch swiftType {
113+
case .function, .optional:
114+
throw LoweringError.unhandledType(swiftType)
115+
116+
case .metatype:
117+
self = .unsafeCastPointer(
118+
.placeholder,
119+
swiftType: .nominal(
120+
SwiftNominalType(
121+
nominalTypeDecl: stdlibTypes[.unsafeRawPointer]
122+
)
123+
)
124+
)
125+
return
126+
127+
case .nominal(let nominal):
128+
if let knownType = nominal.nominalTypeDecl.knownStandardLibraryType {
129+
// Typed pointers
130+
if nominal.genericArguments?.first != nil {
131+
switch knownType {
132+
case .unsafePointer, .unsafeMutablePointer:
133+
let isMutable = knownType == .unsafeMutablePointer
134+
self = ConversionStep(
135+
initializeRawPointerFromTyped: .placeholder,
136+
isMutable: isMutable,
137+
isPartOfBufferPointer: false,
138+
stdlibTypes: stdlibTypes
139+
)
140+
return
141+
142+
case .unsafeBufferPointer, .unsafeMutableBufferPointer:
143+
let isMutable = knownType == .unsafeMutableBufferPointer
144+
self = .tuplify(
145+
[
146+
ConversionStep(
147+
initializeRawPointerFromTyped: .explodedComponent(
148+
.placeholder,
149+
component: "pointer"
150+
),
151+
isMutable: isMutable,
152+
isPartOfBufferPointer: true,
153+
stdlibTypes: stdlibTypes
154+
),
155+
.explodedComponent(.placeholder, component: "count")
156+
]
157+
)
158+
return
159+
160+
default:
161+
break
162+
}
163+
}
164+
}
165+
166+
// Arbitrary nominal types.
167+
switch nominal.nominalTypeDecl.kind {
168+
case .actor, .class:
169+
// For actor and class, we pass around the pointer directly. Case to
170+
// the unsafe raw pointer type we use to represent it in C.
171+
self = .unsafeCastPointer(
172+
.placeholder,
173+
swiftType: .nominal(
174+
SwiftNominalType(
175+
nominalTypeDecl: stdlibTypes[.unsafeRawPointer]
176+
)
177+
)
178+
)
179+
180+
case .enum, .struct, .protocol:
181+
// For enums, structs, and protocol types, we leave the value alone.
182+
// The indirection will be handled by the caller.
183+
self = .placeholder
184+
}
185+
186+
case .tuple(let elements):
187+
// Convert all of the elements.
188+
self = .tuplify(
189+
try elements.map { element in
190+
try ConversionStep(swiftToCDecl: element, stdlibTypes: stdlibTypes)
191+
}
192+
)
193+
}
194+
}
195+
}

0 commit comments

Comments
 (0)