Skip to content

Commit c769b1a

Browse files
authored
Add Support for Threadsafe and NotThreadSafe annotations (#188)
1 parent a26cb1a commit c769b1a

File tree

6 files changed

+80
-1
lines changed

6 files changed

+80
-1
lines changed

Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ extension HelloSwift: HelloSwiftNativeMethods {
7676
print("Caught Java error: \(error)")
7777
}
7878

79+
// Make sure that the thread safe class is sendable
80+
let threadSafe: Sendable = ThreadSafeHelperClass(environment: javaEnvironment)
81+
7982
return i * j
8083
}
8184

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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 java.lang.annotation.Retention;
18+
import java.lang.annotation.RetentionPolicy;
19+
20+
@Retention(RetentionPolicy.RUNTIME)
21+
public @interface ThreadSafe {
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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+
@ThreadSafe
18+
public class ThreadSafeHelperClass {
19+
public ThreadSafeHelperClass() { }
20+
}

Samples/JavaKitSampleApp/Sources/JavaKitExample/swift-java.config

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"classes" : {
33
"com.example.swift.HelloSwift" : "HelloSwift",
44
"com.example.swift.HelloSubclass" : "HelloSubclass",
5-
"com.example.swift.JavaKitSampleMain" : "JavaKitSampleMain"
5+
"com.example.swift.JavaKitSampleMain" : "JavaKitSampleMain",
6+
"com.example.swift.ThreadSafeHelperClass" : "ThreadSafeHelperClass"
67
}
78
}

Sources/Java2SwiftLib/JavaClassTranslator.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ struct JavaClassTranslator {
5252
/// The Swift names of the interfaces that this class implements.
5353
let swiftInterfaces: [String]
5454

55+
/// The annotations of the Java class
56+
let annotations: [Annotation]
57+
5558
/// The (instance) fields of the Java class.
5659
var fields: [Field] = []
5760

@@ -164,6 +167,8 @@ struct JavaClassTranslator {
164167
}
165168
}
166169

170+
self.annotations = javaClass.getAnnotations().compactMap(\.self)
171+
167172
// Collect all of the class members that we will need to translate.
168173
// TODO: Switch over to "declared" versions of these whenever we don't need
169174
// to see inherited members.
@@ -274,6 +279,7 @@ extension JavaClassTranslator {
274279
if let nativeMethodsProtocol = renderNativeMethodsProtocol() {
275280
allDecls.append(nativeMethodsProtocol)
276281
}
282+
allDecls.append(contentsOf: renderAnnotationExtensions())
277283
return allDecls
278284
}
279285

@@ -483,6 +489,30 @@ extension JavaClassTranslator {
483489
return protocolDecl.formatted(using: translator.format).cast(DeclSyntax.self)
484490
}
485491

492+
func renderAnnotationExtensions() -> [DeclSyntax] {
493+
var extensions: [DeclSyntax] = []
494+
495+
for annotation in annotations {
496+
let annotationName = annotation.annotationType().getName().splitSwiftTypeName().name
497+
if annotationName == "ThreadSafe" || annotationName == "Immutable" { // If we are threadsafe, mark as unchecked Sendable
498+
extensions.append(
499+
"""
500+
extension \(raw: swiftTypeName): @unchecked Swift.Sendable { }
501+
"""
502+
)
503+
} else if annotationName == "NotThreadSafe" { // If we are _not_ threadsafe, mark sendable unavailable
504+
extensions.append(
505+
"""
506+
@available(unavailable, *)
507+
extension \(raw: swiftTypeName): Swift.Sendable { }
508+
"""
509+
)
510+
}
511+
}
512+
513+
return extensions
514+
}
515+
486516
/// Render the given Java constructor as a Swift initializer.
487517
package func renderConstructor(
488518
_ javaConstructor: Constructor<some AnyJavaObject>

Sources/JavaKitReflection/JavaClass+Reflection.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,7 @@ extension JavaClass {
4343

4444
@JavaMethod
4545
public func getGenericInterfaces() -> [Type?]
46+
47+
@JavaMethod
48+
public func getAnnotations() -> [Annotation?]
4649
}

0 commit comments

Comments
 (0)