Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ extension FFMSwift2JavaGenerator {
parentProtocol = "SwiftValue"
}

if decl.swiftNominal.isSendable {
printer.print("@ThreadSafe // Sendable")
}
printer.printBraceBlock("public final class \(decl.swiftNominal.name) extends FFMSwiftInstance implements \(parentProtocol)") {
printer in
// Constants
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ extension JNISwift2JavaGenerator {
private func printNominal(
_ printer: inout CodePrinter, _ decl: ImportedNominalType, body: (inout CodePrinter) -> Void
) {
if decl.swiftNominal.isSendable {
printer.print("@ThreadSafe // Sendable")
}
printer.printBraceBlock("public final class \(decl.swiftNominal.name) extends JNISwiftInstance") { printer in
body(&printer)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,22 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration {
super.init(moduleName: moduleName, name: node.name.text)
}

/// Returns true if this type conforms to `Sendable` and therefore is "threadsafe".
lazy var isSendable: Bool = {
// Check if Sendable is in the inheritance list
guard let inheritanceClause = self.syntax?.inheritanceClause else {
return false
}

for inheritedType in inheritanceClause.inheritedTypes {
if inheritedType.type.trimmedDescription == "Sendable" {
return true
}
}

return false
}()

/// Determine the known standard library type for this nominal type
/// declaration.
private func computeKnownStandardLibraryType() -> SwiftKnownTypeDeclKind? {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

package org.swift.swiftkit.core.annotations;

import jdk.jfr.Description;
import jdk.jfr.Label;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

/**
* Used to mark a type as thread-safe, i.e. no additional synchronization is necessary when accessing it
* from multiple threads.
*
* <p> In SwiftJava specifically, this attribute is applied when an extracted Swift type conforms to the Swift
* {@code Sendable} protocol, which is a compiler enforced mechanism to enforce thread-safety in Swift.
*
* @see <a href="https://developer.apple.com/documentation/Swift/Sendable">Swift Sendable API documentation</a>.
*/
@Documented
@Label("Thread-safe")
@Description("Value should be interpreted as safe to be shared across threads.")
@Target({TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ThreadSafe {
}
56 changes: 56 additions & 0 deletions Tests/JExtractSwiftTests/SendableTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of Swift.org project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import JExtractSwiftLib
import Testing

final class SendableTests {
let source =
"""
public struct SendableStruct: Sendable {}
"""


@Test("Import: Sendable struct (ffm)")
func sendableStruct_ffm() throws {

try assertOutput(
input: source, .ffm, .java,
expectedChunks: [
"""
@ThreadSafe // Sendable
public final class SendableStruct extends FFMSwiftInstance implements SwiftValue {
static final String LIB_NAME = "SwiftModule";
static final Arena LIBRARY_ARENA = Arena.ofAuto();
""",
]
)
}

@Test("Import: Sendable struct (jni)")
func sendableStruct_jni() throws {

try assertOutput(
input: source, .jni, .java,
expectedChunks: [
"""
@ThreadSafe // Sendable
public final class SendableStruct extends JNISwiftInstance {
static final String LIB_NAME = "SwiftModule";
""",
]
)
}

}
Loading