From 84620c854c423a9bb7f141f28f0b8e7a4a2354af Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Sun, 16 Aug 2026 09:45:09 +0300 Subject: [PATCH 1/5] support extracting `class` members --- Sources/SwiftExtract/ExtractedDecls.swift | 7 +++++ .../SwiftTypes/SwiftFunctionSignature.swift | 20 ++++++++++--- .../AnalysisResultTests.swift | 29 +++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/Sources/SwiftExtract/ExtractedDecls.swift b/Sources/SwiftExtract/ExtractedDecls.swift index 12371beb3..4a237d7c3 100644 --- a/Sources/SwiftExtract/ExtractedDecls.swift +++ b/Sources/SwiftExtract/ExtractedDecls.swift @@ -324,6 +324,13 @@ public final class ExtractedFunc: ExtractedSwiftDecl, CustomStringConvertible { return false } + public var isClass: Bool { + if case .classMethod = functionSignature.selfParameter { + return true + } + return false + } + public var isInitializer: Bool { if case .initializer = functionSignature.selfParameter { return true diff --git a/Sources/SwiftExtract/SwiftTypes/SwiftFunctionSignature.swift b/Sources/SwiftExtract/SwiftTypes/SwiftFunctionSignature.swift index 6d9445fa3..e8c4b87d1 100644 --- a/Sources/SwiftExtract/SwiftTypes/SwiftFunctionSignature.swift +++ b/Sources/SwiftExtract/SwiftTypes/SwiftFunctionSignature.swift @@ -74,6 +74,10 @@ public enum SwiftSelfParameter: Equatable { /// form the call. case staticMethod(SwiftType) + /// 'self' is the type for a call to a class method. We only need the type + /// to form the call. + case classMethod(SwiftType) + /// 'self' is the type for a call to an initializer. We only need the type /// to form the call. case initializer(SwiftType) @@ -81,7 +85,8 @@ public enum SwiftSelfParameter: Equatable { public var selfType: SwiftType { get { switch self { - case .instance(_, let swiftType), .staticMethod(let swiftType), .initializer(let swiftType): + case .instance(_, let swiftType), .staticMethod(let swiftType), .classMethod(let swiftType), + .initializer(let swiftType): return swiftType } } @@ -91,6 +96,8 @@ public enum SwiftSelfParameter: Equatable { self = .instance(convention: convention, swiftType: newValue) case .staticMethod: self = .staticMethod(newValue) + case .classMethod: + self = .classMethod(newValue) case .initializer: self = .initializer(newValue) } @@ -173,18 +180,21 @@ extension SwiftFunctionSignature { var isMutating = false var isConsuming = false var isStatic = false + var isClass = false for modifier in node.modifiers { switch modifier.name.tokenKind { case .keyword(.mutating): isMutating = true case .keyword(.static): isStatic = true + case .keyword(.class): isClass = true case .keyword(.consuming): isConsuming = true - case .keyword(.class): throw SwiftFunctionTranslationError.classMethod(modifier.name) default: break } } if isStatic { selfParameter = .staticMethod(enclosingType) + } else if isClass { + selfParameter = .classMethod(enclosingType) } else { selfParameter = .instance( convention: isMutating ? .inout : isConsuming ? .consuming : .byValue, @@ -437,16 +447,19 @@ extension SwiftFunctionSignature { // type and convention for the self parameter. if let enclosingType { var isStatic = false + var isClass = false for modifier in modifiers { switch modifier.name.tokenKind { case .keyword(.static): isStatic = true - case .keyword(.class): throw SwiftFunctionTranslationError.classMethod(modifier.name) + case .keyword(.class): isClass = true default: break } } if isStatic { return .staticMethod(enclosingType) + } else if isClass { + return .classMethod(enclosingType) } else { return .instance( convention: isSet && !enclosingType.isReferenceType ? .inout : .byValue, @@ -524,7 +537,6 @@ extension AccessorBlockSyntax { public enum SwiftFunctionTranslationError: Error { case `throws`(ThrowsClauseSyntax) case async(TokenSyntax) - case classMethod(TokenSyntax) case missingEnclosingType(InitializerDeclSyntax) case failableInitializer(InitializerDeclSyntax) case multipleBindings(VariableDeclSyntax) diff --git a/Tests/SwiftExtractTests/AnalysisResultTests.swift b/Tests/SwiftExtractTests/AnalysisResultTests.swift index e5371ef34..19e042ce5 100644 --- a/Tests/SwiftExtractTests/AnalysisResultTests.swift +++ b/Tests/SwiftExtractTests/AnalysisResultTests.swift @@ -569,4 +569,33 @@ struct AnalysisResultSuite { #expect(param.isIsolated) } + + // ==== ----------------------------------------------------------------------- + // MARK: `class` functions and variables + + @Test func classMembers() throws { + let result = try analyze( + sources: [ + ( + "/fake/Source.swift", + """ + public class FishTank { + public init() {} + public class func maxCapacity() -> Int64 { 100 } + public class var count: Int64 { 0 } + } + """ + ) + ], + moduleName: "Aquarium" + ) + + let fishTank = try #require(result.extractedTypes["FishTank"]) + + let maxCapacity = try #require(fishTank.methods.first { $0.name == "maxCapacity" }) + #expect(maxCapacity.isClass) + + let count = try #require(fishTank.variables.first { $0.name == "count" }) + #expect(count.isClass) + } } From a712e34934ad4aa1bf5133caf72090971909d728 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Tue, 18 Aug 2026 13:16:23 +0300 Subject: [PATCH 2/5] jextract: Support class methods and variables in FFM and JNI mode --- ...Swift2JavaGenerator+FunctionLowering.swift | 4 +- ...t2JavaGenerator+JavaBindingsPrinting.swift | 2 +- ...t2JavaGenerator+JavaBindingsPrinting.swift | 4 +- ...ISwift2JavaGenerator+JavaTranslation.swift | 4 +- ...wift2JavaGenerator+NativeTranslation.swift | 2 +- ...ift2JavaGenerator+SwiftThunkPrinting.swift | 2 +- .../JNI/JNISwift2JavaGenerator.swift | 2 +- .../FFM/FFMClassMemberTests.swift | 94 +++++++++++++++++++ .../JNI/JNIClassTests.swift | 89 ++++++++++++++++++ 9 files changed, 193 insertions(+), 10 deletions(-) create mode 100644 Tests/JExtractSwiftTests/FFM/FFMClassMemberTests.swift diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index 9c6d3728d..38f11125c 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -107,7 +107,7 @@ struct CdeclLowering { genericParameters: signature.genericParameters, genericRequirements: signature.genericRequirements, ) - case nil, .initializer(_), .staticMethod(_): + case nil, .initializer(_), .staticMethod(_), .classMethod(_): nil } @@ -1005,7 +1005,7 @@ extension LoweredFunctionSignature { placeholder: "self", bodyItems: &bodyItems, ) - case .staticMethod(let selfType), .initializer(let selfType): + case .staticMethod(let selfType), .classMethod(let selfType), .initializer(let selfType): selfExpr = "\(raw: selfType.description)" case .none: selfExpr = nil diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift index 46ddc38bb..4dc611028 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift @@ -388,7 +388,7 @@ extension FFMSwift2JavaGenerator { var modifiers = "public" switch decl.functionSignature.selfParameter { - case .staticMethod, .initializer, nil: + case .staticMethod, .classMethod, .initializer, nil: modifiers.append(" static") default: break diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index c4cd6bb04..2db3a2062 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -412,7 +412,7 @@ extension JNISwift2JavaGenerator { } for method in decl.methods { - if isEffectivelyGeneric && method.isStatic { + if isEffectivelyGeneric && (method.isStatic || method.isClass) { self.logger.debug("Skipping static method '\(method.name)' on unspecialized generic type '\(decl.effectiveJavaName)'") continue } @@ -421,7 +421,7 @@ extension JNISwift2JavaGenerator { } for variable in decl.variables { - if isEffectivelyGeneric && variable.isStatic { + if isEffectivelyGeneric && (variable.isStatic || variable.isClass) { self.logger.debug("Skipping static property '\(variable.name)' on unspecialized generic type '\(decl.effectiveJavaName)'") continue } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index ed5aa581d..fe2ff2a4e 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -227,7 +227,7 @@ extension JNISwift2JavaGenerator { return TranslatedFunctionDecl( name: javaName, - isStatic: decl.isStatic || !decl.hasParent || decl.isInitializer, + isStatic: decl.isStatic || decl.isClass || !decl.hasParent || decl.isInitializer, isThrowing: decl.isThrowing, isAsync: decl.isAsync, isIsolated: decl.isIsolated, @@ -391,7 +391,7 @@ extension JNISwift2JavaGenerator { parameter: JavaParameter(name: "selfTypePointer", type: .long), conversion: .typeMetadataAddress(.placeholder), ) - case .staticMethod, .initializer: + case .staticMethod, .classMethod, .initializer: return TranslatedParameter( parameter: JavaParameter(name: "selfTypePointer", type: .long), conversion: .constant("$typeMetadataAddressDowncall()"), diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index c8c6e952a..8e644a7c7 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -79,7 +79,7 @@ extension JNISwift2JavaGenerator { genericRequirements: functionSignature.genericRequirements ) } - case nil, .initializer(_), .staticMethod(_): + case nil, .initializer(_), .staticMethod(_), .classMethod(_): nil } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 3f553a902..c0151622d 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -684,7 +684,7 @@ extension JNISwift2JavaGenerator { &printer, "selfPointer", ) - case .staticMethod(let selfType), .initializer(let selfType): + case .staticMethod(let selfType), .classMethod(let selfType), .initializer(let selfType): "\(selfType)" case .none: swiftModuleName diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index 4accdc0a3..539d59baa 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -172,7 +172,7 @@ extension JNISwift2JavaGenerator { /// doesn't translate (e.g. referencing `Self`/associated types). func supportedProtocolRequirements(of type: ExtractedNominalType) -> [ExtractedFunc] { (type.methods + type.variables).filter { requirement in - !requirement.isStatic && self.translatedDecl(for: requirement) != nil + !requirement.isStatic && !requirement.isClass && self.translatedDecl(for: requirement) != nil } } diff --git a/Tests/JExtractSwiftTests/FFM/FFMClassMemberTests.swift b/Tests/JExtractSwiftTests/FFM/FFMClassMemberTests.swift new file mode 100644 index 000000000..7f90c1d5e --- /dev/null +++ b/Tests/JExtractSwiftTests/FFM/FFMClassMemberTests.swift @@ -0,0 +1,94 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 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 + +@Suite +struct FFMClassMemberTests { + let source = """ + public class MyClass { + public class func classMethod() -> Int64 { 42 } + public class var classVariable: Int64 { 7 } + } + """ + + @Test + func classMethod_javaBindings() throws { + try assertOutput( + input: source, + .ffm, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public class func classMethod() -> Int64 + * } + */ + public static long classMethod() { + return swiftjava_SwiftModule_MyClass_classMethod.call(); + } + """ + ] + ) + } + + @Test + func classVariable_javaBindings() throws { + try assertOutput( + input: source, + .ffm, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public class var classVariable: Int64 + * } + */ + public static long getClassVariable() { + return swiftjava_SwiftModule_MyClass_classVariable$get.call(); + } + """ + ] + ) + } + + @Test + func classMembers_swiftThunks() throws { + try assertOutput( + input: source, + .ffm, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("swiftjava_SwiftModule_MyClass_classMethod") + public func swiftjava_SwiftModule_MyClass_classMethod() -> Int64 { + return MyClass.classMethod() + } + """, + """ + @_cdecl("swiftjava_SwiftModule_MyClass_classVariable$get") + public func swiftjava_SwiftModule_MyClass_classVariable$get() -> Int64 { + return MyClass.classVariable + } + """, + ] + ) + } +} diff --git a/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift b/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift index e2ac9830a..366d0598b 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIClassTests.swift @@ -24,6 +24,9 @@ struct JNIClassTests { public static func method() {} + public class func classMethod() -> Int64 { 42 } + public class var classVariable: Int64 { 7 } + public init(x: Int64, y: Int64) { self.x = y self.y = y @@ -142,6 +145,92 @@ struct JNIClassTests { ) } + @Test + func classMethod_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public class func classMethod() -> Int64 + * } + */ + public static long classMethod() { + return MyClass.$classMethod(); + } + """, + """ + private static native long $classMethod(); + """, + ] + ) + } + + @Test + func classMethod_swiftThunks() throws { + try assertOutput( + input: source, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_MyClass__00024classMethod__") + public func Java_com_example_swift_MyClass__00024classMethod__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + return MyClass.classMethod().getJNILocalRefValue(in: environment) + } + """ + ] + ) + } + + @Test + func classVariable_javaBindings() throws { + try assertOutput( + input: source, + .jni, + .java, + expectedChunks: [ + """ + /** + * Downcall to Swift: + * {@snippet lang=swift : + * public class var classVariable: Int64 + * } + */ + public static long getClassVariable() { + return MyClass.$getClassVariable(); + } + """, + """ + private static native long $getClassVariable(); + """, + ] + ) + } + + @Test + func classVariable_swiftThunks() throws { + try assertOutput( + input: source, + .jni, + .swift, + detectChunkByInitialLines: 1, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_MyClass__00024getClassVariable__") + public func Java_com_example_swift_MyClass__00024getClassVariable__(environment: UnsafeMutablePointer!, thisClass: jclass) -> jlong { + return MyClass.classVariable.getJNILocalRefValue(in: environment) + } + """ + ] + ) + } + @Test func initializer_javaBindings() throws { try assertOutput( From bdfb2557f5b204d04b8c802e0ac191403946b3ec Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Tue, 18 Aug 2026 13:34:34 +0300 Subject: [PATCH 3/5] jextract: Add runtime tests --- .../Sources/MySwiftLibrary/MySwiftClass.swift | 8 ++++++++ .../test/java/com/example/swift/MySwiftClassTest.java | 10 ++++++++++ .../Sources/MySwiftLibrary/MySwiftClass.swift | 10 ++++++++++ .../test/java/com/example/swift/MySwiftClassTest.java | 10 ++++++++++ 4 files changed, 38 insertions(+) diff --git a/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftClass.swift b/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftClass.swift index 5a83a0c6c..c60d1b970 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftClass.swift +++ b/Samples/SwiftJavaExtractFFMSampleApp/Sources/MySwiftLibrary/MySwiftClass.swift @@ -39,6 +39,14 @@ public class MySwiftClass { MySwiftClass(x: x, y: y) } + public class func classMethod(x: Int) -> Int { + x * 2 + } + + public class var classVariable: Int { + 42 + } + public func sum() -> Int { x + y } diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java index 958df0ddc..6198fae2c 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java @@ -77,6 +77,16 @@ void test_MySwiftClass_describe() { } } + @Test + void test_MySwiftClass_classMethod() { + assertEquals(24, MySwiftClass.classMethod(12)); + } + + @Test + void test_MySwiftClass_classVariable() { + assertEquals(42, MySwiftClass.getClassVariable()); + } + @Test @Disabled // TODO: Need var mangled names in interfaces void test_MySwiftClass_property_x() { diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftClass.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftClass.swift index 5ca632526..5193165fe 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftClass.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/MySwiftClass.swift @@ -55,6 +55,16 @@ public class MySwiftClass { public static func method() { } + // snippet.classMembers + public class func classMethod(x: Int64) -> Int64 { + x * 2 + } + + public class var classVariable: Int64 { + 42 + } + // snippet.end + // snippet.initializers public init(x: Int64, y: Int64) { self.x = x diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java index b2598602e..241846874 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java @@ -64,6 +64,16 @@ void sum() { // snippet.end } + @Test + void classMethod() { + assertEquals(24, MySwiftClass.classMethod(12)); + } + + @Test + void classVariable() { + assertEquals(42, MySwiftClass.getClassVariable()); + } + @Test void xMultiplied() { try (var arena = SwiftArena.ofConfined()) { From 1044ae134348e15648aee66cf172d5dd337dc74d Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Tue, 18 Aug 2026 16:14:00 +0300 Subject: [PATCH 4/5] jextract: add docs --- .../java/com/example/swift/MySwiftClassTest.java | 8 +++----- .../java/com/example/swift/MySwiftClassTest.java | 8 +++----- .../Documentation.docc/FeaturesJextract.md | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java index 6198fae2c..403f74af2 100644 --- a/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftJavaExtractFFMSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java @@ -78,13 +78,11 @@ void test_MySwiftClass_describe() { } @Test - void test_MySwiftClass_classMethod() { + void test_MySwiftClass_classMembers() { + // snippet.classMembersUsageJava assertEquals(24, MySwiftClass.classMethod(12)); - } - - @Test - void test_MySwiftClass_classVariable() { assertEquals(42, MySwiftClass.getClassVariable()); + // snippet.end } @Test diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java index 241846874..d3bb6246b 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/MySwiftClassTest.java @@ -65,13 +65,11 @@ void sum() { } @Test - void classMethod() { + void classMembers() { + // snippet.classMembersUsageJava assertEquals(24, MySwiftClass.classMethod(12)); - } - - @Test - void classVariable() { assertEquals(42, MySwiftClass.getClassVariable()); + // snippet.end } @Test diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/FeaturesJextract.md b/Sources/SwiftJavaDocumentation/Documentation.docc/FeaturesJextract.md index 5b5802b75..8c933096c 100644 --- a/Sources/SwiftJavaDocumentation/Documentation.docc/FeaturesJextract.md +++ b/Sources/SwiftJavaDocumentation/Documentation.docc/FeaturesJextract.md @@ -25,6 +25,22 @@ For guidance on choosing between JNI and FFM mode, see . Classes and structs can both have initializers imported. +### Class members + +Swift `class` methods and computed properties are imported as `static` methods on the generated Java wrapper type. + +@TabNavigator { + @Tab("Swift") { + @Snippet(path: "Snippets/ClassesSwift.swift", slice: "classMembers") + } + @Tab("Java (JNI)") { + @Snippet(path: "Snippets/MySwiftClassTest", slice: "classMembersUsageJava") + } + @Tab("Java (FFM)") { + @Snippet(path: "Snippets/MySwiftClassTest", slice: "classMembersUsageJava") + } +} + ### Optional initializers / Throwing initializers Optional and throwing initializers are supported in JNI mode. From 9f8741bc4e92cfd679eab6911b0f2a72eda2cf31 Mon Sep 17 00:00:00 2001 From: AbdAlRahman Gad Date: Tue, 18 Aug 2026 16:30:30 +0300 Subject: [PATCH 5/5] fix typo --- .../Documentation.docc/FeaturesJextract.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/FeaturesJextract.md b/Sources/SwiftJavaDocumentation/Documentation.docc/FeaturesJextract.md index 8c933096c..0ef4e6429 100644 --- a/Sources/SwiftJavaDocumentation/Documentation.docc/FeaturesJextract.md +++ b/Sources/SwiftJavaDocumentation/Documentation.docc/FeaturesJextract.md @@ -34,10 +34,10 @@ Swift `class` methods and computed properties are imported as `static` methods o @Snippet(path: "Snippets/ClassesSwift.swift", slice: "classMembers") } @Tab("Java (JNI)") { - @Snippet(path: "Snippets/MySwiftClassTest", slice: "classMembersUsageJava") + @Snippet(path: "Snippets/ClassesJavaJNI", slice: "classMembersUsageJava") } @Tab("Java (FFM)") { - @Snippet(path: "Snippets/MySwiftClassTest", slice: "classMembersUsageJava") + @Snippet(path: "Snippets/ClassesJavaFFM", slice: "classMembersUsageJava") } }