From 6438c7112b7ce9b74ba60d6a90d6309d04fa3192 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 25 Mar 2026 10:35:00 +0900 Subject: [PATCH 1/9] Add generic parameter to generated java class --- ...t2JavaGenerator+JavaBindingsPrinting.swift | 20 ++++++++++---- ...ISwift2JavaGenerator+JavaTranslation.swift | 11 ++++++-- .../JNI/JNIGenericTypeTests.swift | 26 ++++++++++++++----- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 759aa741..8b8b0cbe 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -234,6 +234,11 @@ extension JNISwift2JavaGenerator { ) } printer.println() + let genericClause = if decl.swiftNominal.isGeneric { + "<\(decl.swiftNominal.genericParameters.map(\.name).joined(separator: ", "))>" + } else { + "" + } printer.print( """ /** @@ -245,12 +250,12 @@ extension JNISwift2JavaGenerator { *
  • This operation does not copy, or retain, the pointed at pointer, so its lifetime must be ensured manually to be valid when wrapping.
  • * */ - public static \(decl.swiftNominal.name) wrapMemoryAddressUnsafe(\(swiftPointerArg), SwiftArena swiftArena) { - return new \(decl.swiftNominal.name)(\(swiftPointerParams.joined(separator: ", ")), swiftArena); + public static\(genericClause) \(decl.swiftNominal.name)\(genericClause) wrapMemoryAddressUnsafe(\(swiftPointerArg), SwiftArena swiftArena) { + return new \(decl.swiftNominal.name)\(genericClause)(\(swiftPointerParams.joined(separator: ", ")), swiftArena); } - public static \(decl.swiftNominal.name) wrapMemoryAddressUnsafe(\(swiftPointerArg)) { - return new \(decl.swiftNominal.name)(\(swiftPointerParams.joined(separator: ", ")), SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); + public static\(genericClause) \(decl.swiftNominal.name)\(genericClause) wrapMemoryAddressUnsafe(\(swiftPointerArg)) { + return new \(decl.swiftNominal.name)\(genericClause)(\(swiftPointerParams.joined(separator: ", ")), SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); } """ ) @@ -384,8 +389,13 @@ extension JNISwift2JavaGenerator { .filter { $0.kind == .protocol } .map(\.name) let implementsClause = implements.joined(separator: ", ") + let genericClause = if decl.swiftNominal.isGeneric { + "<\(decl.swiftNominal.genericParameters.map(\.name).joined(separator: ", "))>" + } else { + "" + } printer.printBraceBlock( - "\(modifiers.joined(separator: " ")) class \(decl.swiftNominal.name) implements \(implementsClause)" + "\(modifiers.joined(separator: " ")) class \(decl.swiftNominal.name)\(genericClause) implements \(implementsClause)" ) { printer in body(&printer) } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 966230a5..9308c00c 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -173,7 +173,7 @@ extension JNISwift2JavaGenerator { ), parameters: [], resultType: TranslatedResult( - javaType: .class(package: nil, name: "Optional<\(caseName)>"), + javaType: .class(package: nil, name: "Optional", typeParameters: [.class(package: nil, name: caseName)]), outParameters: conversions.flatMap(\.translated.outParameters), conversion: enumCase.parameters.isEmpty ? constructRecordConversion @@ -949,7 +949,14 @@ extension JNISwift2JavaGenerator { } // We assume this is a JExtract class. - let javaType = JavaType.class(package: nil, name: nominalType.nominalTypeDecl.qualifiedName) + let javaType = JavaType.class( + package: nil, + name: nominalType.nominalTypeDecl.qualifiedName, + typeParameters: try nominalType.genericArguments?.map { swiftType in + let translated = try translate(swiftResult: .init(convention: .direct, type: swiftType)) + return translated.javaType.boxedType + } ?? [] + ) if nominalType.nominalTypeDecl.isGeneric { return TranslatedResult( diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift index d0cddec3..74968fec 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift @@ -32,6 +32,10 @@ struct JNIGenericTypeTests { public func makeStringID(_ value: String) -> MyID { return MyID(value) } + + public func makeIntID(_ value: Int) -> MyID { + return MyID(value) + } """# @Test @@ -43,18 +47,18 @@ struct JNIGenericTypeTests { detectChunkByInitialLines: 2, expectedChunks: [ """ - public final class MyID implements JNISwiftInstance { + public final class MyID implements JNISwiftInstance { """, """ private MyID(long selfPointer, long selfTypePointer, SwiftArena swiftArena) { """, """ - public static MyID wrapMemoryAddressUnsafe(long selfPointer, long selfTypePointer, SwiftArena swiftArena) { - return new MyID(selfPointer, selfTypePointer, swiftArena); + public static MyID wrapMemoryAddressUnsafe(long selfPointer, long selfTypePointer, SwiftArena swiftArena) { + return new MyID(selfPointer, selfTypePointer, swiftArena); } - public static MyID wrapMemoryAddressUnsafe(long selfPointer, long selfTypePointer) { - return new MyID(selfPointer, selfTypePointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); + public static MyID wrapMemoryAddressUnsafe(long selfPointer, long selfTypePointer) { + return new MyID(selfPointer, selfTypePointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA); } """, """ @@ -150,7 +154,7 @@ struct JNIGenericTypeTests { detectChunkByInitialLines: 2, expectedChunks: [ """ - public static MyID makeStringID(java.lang.String value, SwiftArena swiftArena) { + public static MyID makeStringID(java.lang.String value, SwiftArena swiftArena) { org.swift.swiftkit.core._OutSwiftGenericInstance instance = new org.swift.swiftkit.core._OutSwiftGenericInstance(); SwiftModule.$makeStringID(value, instance); return MyID.wrapMemoryAddressUnsafe(instance.selfPointer, instance.selfTypePointer, swiftArena); @@ -159,6 +163,16 @@ struct JNIGenericTypeTests { """ private static native void $makeStringID(java.lang.String value, org.swift.swiftkit.core._OutSwiftGenericInstance out); """, + """ + public static MyID makeIntID(long value, SwiftArena swiftArena) throws SwiftIntegerOverflowException { + org.swift.swiftkit.core._OutSwiftGenericInstance instance = new org.swift.swiftkit.core._OutSwiftGenericInstance(); + SwiftModule.$makeIntID(value, instance); + return MyID.wrapMemoryAddressUnsafe(instance.selfPointer, instance.selfTypePointer, swiftArena); + } + """, + """ + private static native void $makeIntID(long value, org.swift.swiftkit.core._OutSwiftGenericInstance out); + """, ] ) } From 22c3987b126deb92c6617c5531b543f55423c4f4 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 25 Mar 2026 14:19:00 +0900 Subject: [PATCH 2/9] Add test for functions taking concrete generic type argument --- .../JNI/JNIGenericTypeTests.swift | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift index 74968fec..49d1630c 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift @@ -33,8 +33,8 @@ struct JNIGenericTypeTests { return MyID(value) } - public func makeIntID(_ value: Int) -> MyID { - return MyID(value) + public func takeIntID(_ value: MyID) -> Int { + return value.rawValue } """# @@ -164,14 +164,12 @@ struct JNIGenericTypeTests { private static native void $makeStringID(java.lang.String value, org.swift.swiftkit.core._OutSwiftGenericInstance out); """, """ - public static MyID makeIntID(long value, SwiftArena swiftArena) throws SwiftIntegerOverflowException { - org.swift.swiftkit.core._OutSwiftGenericInstance instance = new org.swift.swiftkit.core._OutSwiftGenericInstance(); - SwiftModule.$makeIntID(value, instance); - return MyID.wrapMemoryAddressUnsafe(instance.selfPointer, instance.selfTypePointer, swiftArena); + public static long takeIntID(MyID value) { + return SwiftModule.$takeIntID(value.$memoryAddress()); } """, """ - private static native void $makeIntID(long value, org.swift.swiftkit.core._OutSwiftGenericInstance out); + private static native long $takeIntID(long value); """, ] ) @@ -197,6 +195,18 @@ struct JNIGenericTypeTests { environment.interface.SetLongField(environment, out, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) return } + """, + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024takeIntID__J") + public func Java_com_example_swift_SwiftModule__00024takeIntID__J(environment: UnsafeMutablePointer!, thisClass: jclass, value: jlong) -> jlong { + assert(value != 0, "value memory address was null") + let valueBits$ = Int(Int64(fromJNI: value, in: environment)) + let value$ = UnsafeMutablePointer>(bitPattern: valueBits$) + guard let value$ else { + fatalError("value memory address was null in call to \\(#function)!") + } + return Int64(SwiftModule.takeIntID(value$.pointee)).getJNILocalRefValue(in: environment) + } """ ] ) From 211160fa6a5f4d94d2652ae49a01c11e13c0e43a Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 25 Mar 2026 14:47:23 +0900 Subject: [PATCH 3/9] Add roundtrip test code --- .../Sources/MySwiftLibrary/GenericType.swift | 8 ++++++++ .../java/com/example/swift/GenericTypeTest.java | 14 ++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift index 4e0041fb..d4597327 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift @@ -30,6 +30,14 @@ public func makeStringID(_ value: String) -> MyID { MyID(value) } +public func takeIntValue(from value: MyID) -> Int { + value.rawValue +} + +public func takeStringValue(from value: MyID) -> String { + value.rawValue +} + public struct MyEntity { public var id: MyID public var name: String diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java index fa0d76da..c152c74b 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java @@ -21,21 +21,23 @@ public class GenericTypeTest { @Test - void returnsGenericType() { + void genericTypeValueRoundtrip() { try (var arena = SwiftArena.ofConfined()) { - MyID stringId = MySwiftLibrary.makeStringID("Java", arena); + var stringId = MySwiftLibrary.makeStringID("Java", arena); assertEquals("Java", stringId.getDescription()); + assertEquals("Java", MySwiftLibrary.takeStringValue(stringId)); - MyID intId = MySwiftLibrary.makeIntID(42, arena); + var intId = MySwiftLibrary.makeIntID(42, arena); assertEquals("42", intId.getDescription()); + assertEquals(42, MySwiftLibrary.takeIntValue(intId)); } } @Test void genericTypeProperty() { try (var arena = SwiftArena.ofConfined()) { - MyID intId = MySwiftLibrary.makeIntID(42, arena); - MyEntity entity = MyEntity.init(intId, "name", arena); + var intId = MySwiftLibrary.makeIntID(42, arena); + var entity = MyEntity.init(intId, "name", arena); assertEquals("42", entity.getId(arena).getDescription()); } } @@ -43,7 +45,7 @@ void genericTypeProperty() { @Test void genericEnum() { try (var arena = SwiftArena.ofConfined()) { - GenericEnum value = MySwiftLibrary.makeIntGenericEnum(arena); + var value = MySwiftLibrary.makeIntGenericEnum(arena); switch (value.getCase()) { case GenericEnum.Foo _ -> assertTrue(value.getAsFoo().isPresent()); case GenericEnum.Bar _ -> assertTrue(value.getAsBar().isPresent()); From 01424facea962ab8b5cf88ae73620279983dcf73 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 25 Mar 2026 14:47:54 +0900 Subject: [PATCH 4/9] Considering generic argument value --- ...ISwift2JavaGenerator+JavaTranslation.swift | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 9308c00c..5ece838c 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -528,11 +528,28 @@ extension JNISwift2JavaGenerator { ) } + let javaType = JavaType.class( + package: nil, + name: nominalTypeName, + typeParameters: try nominalType.genericArguments?.map { swiftType in + let translated = try translateParameter( + swiftType: swiftType, + parameterName: parameterName, + methodName: methodName, + parentName: parentName, + genericParameters: genericParameters, + genericRequirements: genericRequirements, + parameterPosition: parameterPosition + ) + return translated.parameter.type.javaType.boxedType + } ?? [] + ) + // We assume this is a JExtract class. return TranslatedParameter( parameter: JavaParameter( name: parameterName, - type: .concrete(.class(package: nil, name: nominalTypeName)), + type: .concrete(javaType), annotations: parameterAnnotations ), conversion: .valueMemoryAddress(.placeholder) @@ -948,7 +965,6 @@ extension JNISwift2JavaGenerator { throw JavaTranslationError.unsupportedSwiftType(swiftType) } - // We assume this is a JExtract class. let javaType = JavaType.class( package: nil, name: nominalType.nominalTypeDecl.qualifiedName, @@ -958,6 +974,7 @@ extension JNISwift2JavaGenerator { } ?? [] ) + // We assume this is a JExtract class. if nominalType.nominalTypeDecl.isGeneric { return TranslatedResult( javaType: javaType, @@ -1156,7 +1173,7 @@ extension JNISwift2JavaGenerator { } // We assume this is a JExtract class. - let returnType = JavaType.class(package: nil, name: "Optional<\(nominalTypeName)>") + let returnType = JavaType.class(package: nil, name: "Optional", typeParameters: [.class(package: nil, name: nominalTypeName)]) return TranslatedResult( javaType: returnType, annotations: parameterAnnotations, From 552c1b61eea9ff2f8bffaa0505176968edd41c35 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 25 Mar 2026 16:33:46 +0900 Subject: [PATCH 5/9] Add translateGenericTypeParameter --- ...ISwift2JavaGenerator+JavaTranslation.swift | 455 ++++++++++++++---- 1 file changed, 362 insertions(+), 93 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 5ece838c..f7f34001 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -355,7 +355,11 @@ extension JNISwift2JavaGenerator { exceptions.append(.integerOverflow) } - let resultType = try translate(swiftResult: functionSignature.result) + let resultType = try translate( + swiftResult: functionSignature.result, + genericParameters: functionSignature.genericParameters, + genericRequirements: functionSignature.genericRequirements + ) return TranslatedFunctionSignature( selfParameter: selfParameter, @@ -462,7 +466,9 @@ extension JNISwift2JavaGenerator { } return try translateOptionalParameter( wrappedType: genericArgs[0], - parameterName: parameterName + parameterName: parameterName, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .array: @@ -471,7 +477,9 @@ extension JNISwift2JavaGenerator { } return try translateArrayParameter( elementType: elementType, - parameterName: parameterName + parameterName: parameterName, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .dictionary: @@ -481,7 +489,9 @@ extension JNISwift2JavaGenerator { return try translateDictionaryParameter( keyType: genericArgs[0], valueType: genericArgs[1], - parameterName: parameterName + parameterName: parameterName, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .set: @@ -490,7 +500,9 @@ extension JNISwift2JavaGenerator { } return try translateSetParameter( elementType: genericArgs[0], - parameterName: parameterName + parameterName: parameterName, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .foundationDate, .essentialsDate: @@ -532,16 +544,11 @@ extension JNISwift2JavaGenerator { package: nil, name: nominalTypeName, typeParameters: try nominalType.genericArguments?.map { swiftType in - let translated = try translateParameter( - swiftType: swiftType, - parameterName: parameterName, - methodName: methodName, - parentName: parentName, + try translateGenericTypeParameter( + swiftType, genericParameters: genericParameters, - genericRequirements: genericRequirements, - parameterPosition: parameterPosition + genericRequirements: genericRequirements ) - return translated.parameter.type.javaType.boxedType } ?? [] ) @@ -574,7 +581,9 @@ extension JNISwift2JavaGenerator { case .optional(let wrapped): return try translateOptionalParameter( wrappedType: wrapped, - parameterName: parameterName + parameterName: parameterName, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .opaque(let proto), .existential(let proto): @@ -585,7 +594,9 @@ extension JNISwift2JavaGenerator { return try translateProtocolParameter( protocolType: proto, parameterName: parameterName, - javaGenericName: "_T\(parameterPosition)" + javaGenericName: "_T\(parameterPosition)", + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .genericParameter(let generic): @@ -596,7 +607,9 @@ extension JNISwift2JavaGenerator { return try translateProtocolParameter( protocolType: concreteTy, parameterName: parameterName, - javaGenericName: generic.name + javaGenericName: generic.name, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) } @@ -605,20 +618,26 @@ extension JNISwift2JavaGenerator { case .array(let elementType): return try translateArrayParameter( elementType: elementType, - parameterName: parameterName + parameterName: parameterName, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .dictionary(let keyType, let valueType): return try translateDictionaryParameter( keyType: keyType, valueType: valueType, - parameterName: parameterName + parameterName: parameterName, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .set(let elementType): return try translateSetParameter( elementType: elementType, - parameterName: parameterName + parameterName: parameterName, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .metatype: @@ -757,21 +776,27 @@ extension JNISwift2JavaGenerator { func translateProtocolParameter( protocolType: SwiftType, parameterName: String, - javaGenericName: String + javaGenericName: String, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedParameter { switch protocolType { case .nominal: return try translateProtocolParameter( protocolTypes: [protocolType], parameterName: parameterName, - javaGenericName: javaGenericName + javaGenericName: javaGenericName, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .composite(let types): return try translateProtocolParameter( protocolTypes: types, parameterName: parameterName, - javaGenericName: javaGenericName + javaGenericName: javaGenericName, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) default: @@ -782,17 +807,16 @@ extension JNISwift2JavaGenerator { private func translateProtocolParameter( protocolTypes: [SwiftType], parameterName: String, - javaGenericName: String + javaGenericName: String, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedParameter { let javaProtocolTypes = try protocolTypes.map { - switch $0 { - case .nominal(let nominalType): - let nominalTypeName = nominalType.nominalTypeDecl.name - return JavaType.class(package: nil, name: nominalTypeName) - - default: - throw JavaTranslationError.unsupportedSwiftType($0) - } + try translateGenericTypeParameter( + $0, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) } // We just pass down the jobject @@ -808,13 +832,15 @@ extension JNISwift2JavaGenerator { func translateOptionalParameter( wrappedType swiftType: SwiftType, - parameterName: String + parameterName: String, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedParameter { let parameterAnnotations: [JavaAnnotation] = getTypeAnnotations(swiftType: swiftType, config: config) switch swiftType { case .nominal(let nominalType): - let nominalTypeName = nominalType.nominalTypeDecl.name + let nominalTypeName = nominalType.nominalTypeDecl.qualifiedName if let knownType = nominalType.nominalTypeDecl.knownTypeKind { switch knownType { @@ -870,14 +896,19 @@ extension JNISwift2JavaGenerator { } // Assume JExtract imported class + let javaType = try translateGenericTypeParameter( + swiftType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) return TranslatedParameter( parameter: JavaParameter( name: parameterName, - type: .class(package: nil, name: "Optional<\(nominalTypeName)>"), + type: .class(package: nil, name: "Optional", typeParameters: [javaType]), annotations: parameterAnnotations ), conversion: .method( - .method(.placeholder, function: "map", arguments: [.constant("\(nominalType)::$memoryAddress")]), + .method(.placeholder, function: "map", arguments: [.constant("\(javaType)::$memoryAddress")]), function: "orElse", arguments: [.constant("0L")] ) @@ -887,7 +918,12 @@ extension JNISwift2JavaGenerator { } } - func translate(swiftResult: SwiftResult, resultName: String = "result") throws -> TranslatedResult { + func translate( + swiftResult: SwiftResult, + resultName: String = "result", + genericParameters: [SwiftGenericParameterDeclaration] = [], + genericRequirements: [SwiftGenericRequirement] = [] + ) throws -> TranslatedResult { let swiftType = swiftResult.type // If the result type should cause any annotations on the method, include them here. @@ -901,14 +937,21 @@ extension JNISwift2JavaGenerator { guard let genericArgs = nominalType.genericArguments, genericArgs.count == 1 else { throw JavaTranslationError.unsupportedSwiftType(swiftType) } - return try translateOptionalResult(wrappedType: genericArgs[0], resultName: resultName) + return try translateOptionalResult( + wrappedType: genericArgs[0], + resultName: resultName, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) case .array: guard let elementType = nominalType.genericArguments?.first else { throw JavaTranslationError.unsupportedSwiftType(swiftType) } return try translateArrayResult( - elementType: elementType + elementType: elementType, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .dictionary: @@ -917,7 +960,9 @@ extension JNISwift2JavaGenerator { } return try translateDictionaryResult( keyType: genericArgs[0], - valueType: genericArgs[1] + valueType: genericArgs[1], + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .set: @@ -925,7 +970,9 @@ extension JNISwift2JavaGenerator { throw JavaTranslationError.setRequiresElementType(swiftType) } return try translateSetResult( - elementType: genericArgs[0] + elementType: genericArgs[0], + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .foundationDate, .essentialsDate: @@ -969,8 +1016,11 @@ extension JNISwift2JavaGenerator { package: nil, name: nominalType.nominalTypeDecl.qualifiedName, typeParameters: try nominalType.genericArguments?.map { swiftType in - let translated = try translate(swiftResult: .init(convention: .direct, type: swiftType)) - return translated.javaType.boxedType + try translateGenericTypeParameter( + swiftType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) } ?? [] ) @@ -1007,35 +1057,201 @@ extension JNISwift2JavaGenerator { return TranslatedResult(javaType: .void, outParameters: [], conversion: .placeholder) case .optional(let wrapped): - return try translateOptionalResult(wrappedType: wrapped, resultName: resultName) + return try translateOptionalResult( + wrappedType: wrapped, + resultName: resultName, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) case .array(let elementType): return try translateArrayResult( - elementType: elementType + elementType: elementType, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .dictionary(let keyType, let valueType): return try translateDictionaryResult( keyType: keyType, - valueType: valueType + valueType: valueType, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .set(let elementType): return try translateSetResult( - elementType: elementType + elementType: elementType, + genericParameters: genericParameters, + genericRequirements: genericRequirements ) case .tuple(let elements) where !elements.isEmpty: - return try translateTupleResult(elements: elements, resultName: resultName) + return try translateTupleResult( + elements: elements, + resultName: resultName, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) case .metatype, .tuple, .function, .existential, .opaque, .genericParameter, .composite: throw JavaTranslationError.unsupportedSwiftType(swiftType) } } + private func translateGenericTypeParameter( + _ swiftType: SwiftType, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] + ) throws -> JavaType { + switch swiftType { + case .nominal(let nominalType): + let nominalTypeName = nominalType.nominalTypeDecl.qualifiedName + + if let knownType = nominalType.nominalTypeDecl.knownTypeKind { + switch knownType { + case .optional: + guard let genericArgs = nominalType.genericArguments, genericArgs.count == 1 else { + throw JavaTranslationError.unsupportedSwiftType(swiftType) + } + let wrappedType = try translateGenericTypeParameter( + genericArgs[0], + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + return .class(package: "java.util", name: "Optional", typeParameters: [wrappedType]) + + case .array: + guard let elementType = nominalType.genericArguments?.first else { + throw JavaTranslationError.unsupportedSwiftType(swiftType) + } + let elementJavaType = try translateGenericTypeParameter( + elementType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + return .array(elementJavaType) + + case .dictionary: + guard let genericArgs = nominalType.genericArguments, genericArgs.count == 2 else { + throw JavaTranslationError.dictionaryRequiresKeyAndValueTypes(swiftType) + } + let keyJavaType = try translateGenericTypeParameter( + genericArgs[0], + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + let valueJavaType = try translateGenericTypeParameter( + genericArgs[1], + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + return .swiftDictionaryMap(keyJavaType, valueJavaType) + + case .set: + guard let genericArgs = nominalType.genericArguments, genericArgs.count == 1 else { + throw JavaTranslationError.setRequiresElementType(swiftType) + } + let elementJavaType = try translateGenericTypeParameter( + genericArgs[0], + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + return .swiftSet(elementJavaType) + + case .foundationUUID, .essentialsUUID: + return .javaUtilUUID + + default: + guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { + throw JavaTranslationError.unsupportedSwiftType(swiftType) + } + return javaType.boxedType + } + } + + if nominalType.isSwiftJavaWrapper { + guard let javaType = nominalTypeName.parseJavaClassFromSwiftJavaName(in: self.javaClassLookupTable) else { + throw JavaTranslationError.wrappedJavaClassTranslationNotProvided(swiftType) + } + return javaType + } + + // We assume this is a JExtract class. + let typeParameters = try nominalType.genericArguments?.map { swiftType in + try translateGenericTypeParameter( + swiftType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + } ?? [] + + return .class( + package: nil, + name: nominalTypeName, + typeParameters: typeParameters + ) + + case .genericParameter(let generic): + if let concreteTy = swiftType.typeIn( + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) { + return try translateGenericTypeParameter( + concreteTy, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + } + return .class(package: nil, name: generic.name) + + case .optional(let wrapped): + let wrappedType = try translateGenericTypeParameter( + wrapped, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + return .class(package: "java.util", name: "Optional", typeParameters: [wrappedType]) + + case .array(let elementType): + let elementJavaType = try translateGenericTypeParameter( + elementType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + return .array(elementJavaType) + + case .dictionary(let keyType, let valueType): + let keyJavaType = try translateGenericTypeParameter( + keyType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + let valueJavaType = try translateGenericTypeParameter( + valueType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + return .swiftDictionaryMap(keyJavaType, valueJavaType) + + case .set(let elementType): + let elementJavaType = try translateGenericTypeParameter( + elementType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + return .swiftSet(elementJavaType) + + case .metatype, .tuple, .function, .existential, .opaque, .composite: + throw JavaTranslationError.unsupportedSwiftType(swiftType) + } + } + func translateTupleResult( elements: [SwiftTupleElement], - resultName: String = "result" + resultName: String = "result", + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedResult { let arity = elements.count var outParameters: [OutParameter] = [] @@ -1047,7 +1263,11 @@ extension JNISwift2JavaGenerator { let outParamName = "\(resultName)_\(idx)$" // Determine the Java type for this element - let (javaType, elementConversion) = try translateTupleElementResult(type: element.type) + let (javaType, elementConversion) = try translateTupleElementResult( + type: element.type, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) let arrayType: JavaType = .array(javaType) outParameters.append( @@ -1075,7 +1295,11 @@ extension JNISwift2JavaGenerator { } /// Translate a single element type for tuple results on the Java side. - private func translateTupleElementResult(type: SwiftType) throws -> (JavaType, JavaNativeConversionStep) { + private func translateTupleElementResult( + type: SwiftType, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] + ) throws -> (JavaType, JavaNativeConversionStep) { switch type { case .nominal(let nominalType): if let knownType = nominalType.nominalTypeDecl.knownTypeKind { @@ -1086,13 +1310,16 @@ extension JNISwift2JavaGenerator { return (javaType, .placeholder) } - let nominalTypeName = nominalType.nominalTypeDecl.name guard !nominalType.isSwiftJavaWrapper else { throw JavaTranslationError.unsupportedSwiftType(type) } + let javaType = try translateGenericTypeParameter( + type, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) // JExtract class: wrap memory address - let javaType: JavaType = .class(package: nil, name: nominalTypeName) return (.long, .constructSwiftValue(.placeholder, javaType)) default: @@ -1102,7 +1329,9 @@ extension JNISwift2JavaGenerator { func translateOptionalResult( wrappedType swiftType: SwiftType, - resultName: String = "result" + resultName: String = "result", + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedResult { let discriminatorName = "\(resultName)$_discriminator$" @@ -1110,8 +1339,6 @@ extension JNISwift2JavaGenerator { switch swiftType { case .nominal(let nominalType): - let nominalTypeName = nominalType.nominalTypeDecl.name - if let knownType = nominalType.nominalTypeDecl.knownTypeKind { switch knownType { case .foundationDate, .essentialsDate: @@ -1173,7 +1400,12 @@ extension JNISwift2JavaGenerator { } // We assume this is a JExtract class. - let returnType = JavaType.class(package: nil, name: "Optional", typeParameters: [.class(package: nil, name: nominalTypeName)]) + let javaType = try translateGenericTypeParameter( + swiftType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + let returnType = JavaType.class(package: nil, name: "Optional", typeParameters: [javaType]) return TranslatedResult( javaType: returnType, annotations: parameterAnnotations, @@ -1184,7 +1416,7 @@ extension JNISwift2JavaGenerator { discriminatorName: .combinedName(component: "discriminator$"), optionalClass: "Optional", javaType: .long, - toValue: .wrapMemoryAddressUnsafe(.placeholder, .class(package: nil, name: nominalTypeName)), + toValue: .wrapMemoryAddressUnsafe(.placeholder, javaType), resultName: resultName ) ) @@ -1196,14 +1428,14 @@ extension JNISwift2JavaGenerator { func translateArrayParameter( elementType: SwiftType, - parameterName: String + parameterName: String, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedParameter { let parameterAnnotations: [JavaAnnotation] = getTypeAnnotations(swiftType: elementType, config: config) switch elementType { case .nominal(let nominalType): - let nominalTypeName = nominalType.nominalTypeDecl.qualifiedName - if let knownType = nominalType.nominalTypeDecl.knownTypeKind { guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { throw JavaTranslationError.unsupportedSwiftType(elementType) @@ -1219,18 +1451,23 @@ extension JNISwift2JavaGenerator { throw JavaTranslationError.unsupportedSwiftType(elementType) } + let javaType = try translateGenericTypeParameter( + elementType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) // Assume JExtract imported class return TranslatedParameter( parameter: JavaParameter( name: parameterName, - type: .array(.class(package: nil, name: nominalTypeName)), + type: .array(javaType), annotations: parameterAnnotations ), conversion: .method( .method( .arraysStream(.requireNonNull(.placeholder, message: "\(parameterName) must not be null")), function: "mapToLong", - arguments: [.constant("\(nominalTypeName)::$memoryAddress")] + arguments: [.constant("\(javaType)::$memoryAddress")] ), function: "toArray", arguments: [] @@ -1243,14 +1480,14 @@ extension JNISwift2JavaGenerator { } func translateArrayResult( - elementType: SwiftType + elementType: SwiftType, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedResult { let annotations: [JavaAnnotation] = getTypeAnnotations(swiftType: elementType, config: config) switch elementType { case .nominal(let nominalType): - let nominalTypeName = nominalType.nominalTypeDecl.qualifiedName - if let knownType = nominalType.nominalTypeDecl.knownTypeKind { guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { throw JavaTranslationError.unsupportedSwiftType(elementType) @@ -1268,10 +1505,14 @@ extension JNISwift2JavaGenerator { throw JavaTranslationError.unsupportedSwiftType(elementType) } - let objectType = JavaType.class(package: nil, name: nominalTypeName) + let javaType = try translateGenericTypeParameter( + elementType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) // We assume this is a JExtract class. return TranslatedResult( - javaType: .array(objectType), + javaType: .array(javaType), annotations: annotations, outParameters: [], conversion: .method( @@ -1281,12 +1522,12 @@ extension JNISwift2JavaGenerator { arguments: [ .lambda( args: ["pointer"], - body: .wrapMemoryAddressUnsafe(.constant("pointer"), objectType) + body: .wrapMemoryAddressUnsafe(.constant("pointer"), javaType) ) ] ), function: "toArray", - arguments: [.constant("\(objectType)[]::new")] + arguments: [.constant("\(javaType)[]::new")] ) ) @@ -1295,29 +1536,35 @@ extension JNISwift2JavaGenerator { } } - func javaTypeForDictionaryComponent(_ swiftType: SwiftType) throws -> JavaType { - switch swiftType { - case .nominal(let nominalType): - if let knownType = nominalType.nominalTypeDecl.knownTypeKind { - guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } - return javaType - } - throw JavaTranslationError.unsupportedSwiftType(swiftType) - - default: - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } + func javaTypeForDictionaryComponent( + _ swiftType: SwiftType, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] + ) throws -> JavaType { + try translateGenericTypeParameter( + swiftType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) } func translateDictionaryParameter( keyType: SwiftType, valueType: SwiftType, - parameterName: String + parameterName: String, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedParameter { - let keyJavaType = try javaTypeForDictionaryComponent(keyType) - let valueJavaType = try javaTypeForDictionaryComponent(valueType) + let keyJavaType = try javaTypeForDictionaryComponent( + keyType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + let valueJavaType = try javaTypeForDictionaryComponent( + valueType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) let dictType = JavaType.swiftDictionaryMap(keyJavaType, valueJavaType) return TranslatedParameter( @@ -1332,10 +1579,20 @@ extension JNISwift2JavaGenerator { func translateDictionaryResult( keyType: SwiftType, - valueType: SwiftType + valueType: SwiftType, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedResult { - let keyJavaType = try javaTypeForDictionaryComponent(keyType) - let valueJavaType = try javaTypeForDictionaryComponent(valueType) + let keyJavaType = try javaTypeForDictionaryComponent( + keyType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + let valueJavaType = try javaTypeForDictionaryComponent( + valueType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) let dictType = JavaType.swiftDictionaryMap(keyJavaType, valueJavaType) return TranslatedResult( @@ -1347,9 +1604,15 @@ extension JNISwift2JavaGenerator { func translateSetParameter( elementType: SwiftType, - parameterName: String + parameterName: String, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedParameter { - let elementJavaType = try javaTypeForDictionaryComponent(elementType) + let elementJavaType = try javaTypeForDictionaryComponent( + elementType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) let setType = JavaType.swiftSet(elementJavaType) return TranslatedParameter( @@ -1363,9 +1626,15 @@ extension JNISwift2JavaGenerator { } func translateSetResult( - elementType: SwiftType + elementType: SwiftType, + genericParameters: [SwiftGenericParameterDeclaration], + genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedResult { - let elementJavaType = try javaTypeForDictionaryComponent(elementType) + let elementJavaType = try javaTypeForDictionaryComponent( + elementType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) let setType = JavaType.swiftSet(elementJavaType) return TranslatedResult( From 013c5e662c47896169ff6c1c3e29282a2d9c5b57 Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 26 Mar 2026 12:50:32 +0900 Subject: [PATCH 6/9] Add Date and Date support --- .../JNI/JNISwift2JavaGenerator+JavaTranslation.swift | 6 ++++++ Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index f7f34001..dae53a06 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -1159,6 +1159,12 @@ extension JNISwift2JavaGenerator { ) return .swiftSet(elementJavaType) + case .foundationDate, .essentialsDate: + return .class(package: nil, name: "Date") + + case .foundationData, .essentialsData: + return .class(package: nil, name: "Data") + case .foundationUUID, .essentialsUUID: return .javaUtilUUID diff --git a/Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift b/Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift index 7a5d6988..68f8cc7f 100644 --- a/Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift +++ b/Sources/JExtractSwiftLib/JavaTypes/JavaType+SwiftKit.swift @@ -44,4 +44,8 @@ extension JavaType { .class(package: "org.swift.swiftkit.core.collections", name: "SwiftSet", typeParameters: [E.boxedType]) } + /// A container for receiving Swift generic instances. + static var _OutSwiftGenericInstance: JavaType { + .class(package: "org.swift.swiftkit.core", name: "_OutSwiftGenericInstance") + } } From afc146aced3387927aca497c708e80dfc4cdd7a7 Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 26 Mar 2026 14:11:30 +0900 Subject: [PATCH 7/9] Update document --- .../Documentation.docc/SupportedFeatures.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md index 95e97f4b..f1799af5 100644 --- a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md +++ b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md @@ -374,7 +374,6 @@ or set the `asyncFuncMode` configuration value in `swift-java.config` > Note: Generic types are currently only supported in JNI mode. Support for generic types is still work-in-progress and limited. -The generated Java classes do not have generic signatures. Any members containing type parameters (such as T) are not exported. ```swift @@ -404,11 +403,11 @@ public func makeIntID() -> MyID { will be exported as ```java -public final class MyID implements JNISwiftInstance { +public final class MyID implements JNISwiftInstance { public String getDescription(); } public final class MySwiftLibrary { - public static MyID makeIntID(); + public static MyID makeIntID(); } ``` From 1159b63b412ae5416d1ab9864e79e94c12a0ba4a Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 26 Mar 2026 14:14:59 +0900 Subject: [PATCH 8/9] format --- ...t2JavaGenerator+JavaBindingsPrinting.swift | 22 ++++++++++--------- ...ISwift2JavaGenerator+JavaTranslation.swift | 15 +++++++------ .../JNI/JNIGenericTypeTests.swift | 4 ++-- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 8b8b0cbe..6c99d3de 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -234,11 +234,12 @@ extension JNISwift2JavaGenerator { ) } printer.println() - let genericClause = if decl.swiftNominal.isGeneric { - "<\(decl.swiftNominal.genericParameters.map(\.name).joined(separator: ", "))>" - } else { - "" - } + let genericClause = + if decl.swiftNominal.isGeneric { + "<\(decl.swiftNominal.genericParameters.map(\.name).joined(separator: ", "))>" + } else { + "" + } printer.print( """ /** @@ -389,11 +390,12 @@ extension JNISwift2JavaGenerator { .filter { $0.kind == .protocol } .map(\.name) let implementsClause = implements.joined(separator: ", ") - let genericClause = if decl.swiftNominal.isGeneric { - "<\(decl.swiftNominal.genericParameters.map(\.name).joined(separator: ", "))>" - } else { - "" - } + let genericClause = + if decl.swiftNominal.isGeneric { + "<\(decl.swiftNominal.genericParameters.map(\.name).joined(separator: ", "))>" + } else { + "" + } printer.printBraceBlock( "\(modifiers.joined(separator: " ")) class \(decl.swiftNominal.name)\(genericClause) implements \(implementsClause)" ) { printer in diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index dae53a06..2563c1a5 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -1184,13 +1184,14 @@ extension JNISwift2JavaGenerator { } // We assume this is a JExtract class. - let typeParameters = try nominalType.genericArguments?.map { swiftType in - try translateGenericTypeParameter( - swiftType, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - } ?? [] + let typeParameters = + try nominalType.genericArguments?.map { swiftType in + try translateGenericTypeParameter( + swiftType, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + } ?? [] return .class( package: nil, diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift index 49d1630c..cf5beded 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift @@ -32,7 +32,7 @@ struct JNIGenericTypeTests { public func makeStringID(_ value: String) -> MyID { return MyID(value) } - + public func takeIntID(_ value: MyID) -> Int { return value.rawValue } @@ -207,7 +207,7 @@ struct JNIGenericTypeTests { } return Int64(SwiftModule.takeIntID(value$.pointee)).getJNILocalRefValue(in: environment) } - """ + """, ] ) } From 7d16e290a127652690451dab93e5e774a3d49d51 Mon Sep 17 00:00:00 2001 From: Iceman Date: Thu, 26 Mar 2026 14:27:21 +0900 Subject: [PATCH 9/9] Add explicit type --- .../test/java/com/example/swift/GenericTypeTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java index c152c74b..bedf0eca 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java @@ -23,11 +23,11 @@ public class GenericTypeTest { @Test void genericTypeValueRoundtrip() { try (var arena = SwiftArena.ofConfined()) { - var stringId = MySwiftLibrary.makeStringID("Java", arena); + MyID stringId = MySwiftLibrary.makeStringID("Java", arena); assertEquals("Java", stringId.getDescription()); assertEquals("Java", MySwiftLibrary.takeStringValue(stringId)); - var intId = MySwiftLibrary.makeIntID(42, arena); + MyID intId = MySwiftLibrary.makeIntID(42, arena); assertEquals("42", intId.getDescription()); assertEquals(42, MySwiftLibrary.takeIntValue(intId)); } @@ -36,8 +36,8 @@ void genericTypeValueRoundtrip() { @Test void genericTypeProperty() { try (var arena = SwiftArena.ofConfined()) { - var intId = MySwiftLibrary.makeIntID(42, arena); - var entity = MyEntity.init(intId, "name", arena); + MyID intId = MySwiftLibrary.makeIntID(42, arena); + MyEntity entity = MyEntity.init(intId, "name", arena); assertEquals("42", entity.getId(arena).getDescription()); } } @@ -45,7 +45,7 @@ void genericTypeProperty() { @Test void genericEnum() { try (var arena = SwiftArena.ofConfined()) { - var value = MySwiftLibrary.makeIntGenericEnum(arena); + GenericEnum value = MySwiftLibrary.makeIntGenericEnum(arena); switch (value.getCase()) { case GenericEnum.Foo _ -> assertTrue(value.getAsFoo().isPresent()); case GenericEnum.Bar _ -> assertTrue(value.getAsBar().isPresent());