From 5caa74fc7b3eb58798e82f1b425e234153b5b9c9 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Fri, 13 Feb 2026 15:29:57 +0900
Subject: [PATCH 01/25] Add test code for generic type
---
.../Sources/MySwiftLibrary/GenericType.swift | 21 ++++++
.../JNI/JNIGenericTypeTests.swift | 69 +++++++++++++++++++
2 files changed, 90 insertions(+)
create mode 100644 Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
create mode 100644 Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
new file mode 100644
index 00000000..c3134ba4
--- /dev/null
+++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2025 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
+//
+//===----------------------------------------------------------------------===//
+
+public struct MyID {
+ public var rawValue: T
+ public init(_ rawValue: T) {
+ self.rawValue = rawValue
+ }
+}
+
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
new file mode 100644
index 00000000..b0acd96a
--- /dev/null
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the Swift.org open source project
+//
+// Copyright (c) 2025 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 JNIGenericTypeTests {
+ let genericFile =
+ #"""
+ public struct MyID {
+ public var rawValue: T
+ public init(_ rawValue: T) {
+ self.rawValue = rawValue
+ }
+ public var description: String {
+ "\(rawValue)"
+ }
+ }
+ """#
+
+ @Test
+ func generateJavaClass() throws {
+ try assertOutput(
+ input: genericFile,
+ .jni,
+ .java,
+ detectChunkByInitialLines: 1,
+ expectedChunks: [
+ """
+ public final class MyID implements JNISwiftInstance {
+ """,
+ """
+ private final long t0MetaPointer;
+ """,
+ """
+ public java.lang.String getDescription() {
+ return MyID.$getDescription(this.$memoryAddress(), this.t0MetaPointer);
+ }
+ private static native java.lang.String $getDescription(long self, long t0MetaPointer);
+ """,
+ """
+ private static native long $typeMetadataAddressDowncall(long t0MetaPointer);
+ @Override
+ public long $typeMetadataAddress() {
+ long self$ = this.$memoryAddress();
+ if (CallTraces.TRACE_DOWNCALLS) {
+ CallTraces.traceDowncall("MyID.$typeMetadataAddress",
+ "this", this,
+ "self", self$);
+ }
+ return MyID.$typeMetadataAddressDowncall(t0MetaPointer);
+ }
+ """
+ ]
+ )
+ }
+}
From d46019f03ef013cc9fe0059369c4c42bea15157f Mon Sep 17 00:00:00 2001
From: Iceman
Date: Mon, 16 Feb 2026 12:47:57 +0900
Subject: [PATCH 02/25] Implement type parameter translations
---
Sources/JExtractSwiftLib/ImportedDecls.swift | 3 +
...t2JavaGenerator+JavaBindingsPrinting.swift | 77 +++++++++++++++----
...ISwift2JavaGenerator+JavaTranslation.swift | 27 ++++++-
...wift2JavaGenerator+NativeTranslation.swift | 38 +++++++++
.../SwiftNominalTypeDeclaration.swift | 6 +-
.../JNI/JNIGenericTypeTests.swift | 9 ++-
6 files changed, 140 insertions(+), 20 deletions(-)
diff --git a/Sources/JExtractSwiftLib/ImportedDecls.swift b/Sources/JExtractSwiftLib/ImportedDecls.swift
index 7bb2f041..b67b4e43 100644
--- a/Sources/JExtractSwiftLib/ImportedDecls.swift
+++ b/Sources/JExtractSwiftLib/ImportedDecls.swift
@@ -43,6 +43,9 @@ package final class ImportedNominalType: ImportedDecl {
package var variables: [ImportedFunc] = []
package var cases: [ImportedEnumCase] = []
var inheritedTypes: [SwiftType]
+ package var genericParameters: [SwiftGenericParameterDeclaration] {
+ self.swiftNominal.genericParameters
+ }
package var parent: SwiftNominalTypeDeclaration?
init(swiftNominal: SwiftNominalTypeDeclaration, lookupContext: SwiftTypeLookupContext) throws {
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
index ffc5fffd..8dbc4e5b 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
@@ -199,14 +199,33 @@ extension JNISwift2JavaGenerator {
* @param selfPointer a pointer to the memory containing the value
* @param swiftArena the arena this object belongs to. When the arena goes out of scope, this value is destroyed.
*/
- private \(decl.swiftNominal.name)(long selfPointer, SwiftArena swiftArena) {
- SwiftObjects.requireNonZero(selfPointer, "selfPointer");
- this.selfPointer = selfPointer;
+ """
+ )
+ var swiftPointerParams = ["selfPointer"]
+ for (index, _) in decl.genericParameters.enumerated() {
+ swiftPointerParams.append("t\(index)MetaPointer")
+ }
+ let swiftPointerArg = swiftPointerParams.map { "long \($0)" }.joined(separator: ", ")
+ printer.printBraceBlock("private \(decl.swiftNominal.name)(\(swiftPointerArg), SwiftArena swiftArena)") { printer in
+ for param in swiftPointerParams {
+ printer.print(
+ """
+ SwiftObjects.requireNonZero(\(param), "\(param)");
+ this.\(param) = \(param);
+ """
+ )
+ }
+ printer.print(
+ """
// Only register once we have fully initialized the object since this will need the object pointer.
swiftArena.register(this);
- }
-
+ """
+ )
+ }
+ printer.println()
+ printer.print(
+ """
/**
* Assume that the passed {@code long} represents a memory address of a {@link \(decl.swiftNominal.name)}.
*
@@ -216,12 +235,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(long selfPointer, SwiftArena swiftArena) {
- return new \(decl.swiftNominal.name)(selfPointer, swiftArena);
+ public static \(decl.swiftNominal.name) wrapMemoryAddressUnsafe(\(swiftPointerArg), SwiftArena swiftArena) {
+ return new \(decl.swiftNominal.name)(\(swiftPointerParams.joined(separator: ", ")), swiftArena);
}
- public static \(decl.swiftNominal.name) wrapMemoryAddressUnsafe(long selfPointer) {
- return new \(decl.swiftNominal.name)(selfPointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA);
+ public static \(decl.swiftNominal.name) wrapMemoryAddressUnsafe(\(swiftPointerArg)) {
+ return new \(decl.swiftNominal.name)(\(swiftPointerParams.joined(separator: ", ")), SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA);
}
"""
)
@@ -245,6 +264,11 @@ extension JNISwift2JavaGenerator {
"""
)
+ for (index, param) in decl.genericParameters.enumerated() {
+ printer.print("/** Pointer to the metatype of type parameter '\(param.name)' */")
+ printer.print("private final long t\(index)MetaPointer;")
+ }
+
printer.println()
if decl.swiftNominal.kind == .enum {
@@ -295,25 +319,34 @@ extension JNISwift2JavaGenerator {
}
private func printToStringMethods(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
+ var arguments = ["selfPointer"]
+ var parameters = ["this.$memoryAddress()"]
+ for (index, _) in decl.genericParameters.enumerated() {
+ arguments.append("t\(index)MetaPointer")
+ parameters.append("this.t\(index)MetaPointer")
+ }
+ let argument = arguments.map { "long \($0)" }.joined(separator: ", ")
+ let parameter = parameters.joined(separator: ", ")
+
printer.printBraceBlock("public String toString()") { printer in
printer.print(
"""
- return $toString(this.$memoryAddress());
+ return $toString(\(parameter));
"""
)
}
- printer.print("private static native java.lang.String $toString(long selfPointer);")
+ printer.print("private static native java.lang.String $toString(\(argument));")
printer.println()
printer.printBraceBlock("public String toDebugString()") { printer in
printer.print(
"""
- return $toDebugString(this.$memoryAddress());
+ return $toDebugString(\(parameter));
"""
)
}
- printer.print("private static native java.lang.String $toDebugString(long selfPointer);")
+ printer.print("private static native java.lang.String $toDebugString(\(argument));")
}
private func printHeader(_ printer: inout CodePrinter) {
@@ -630,6 +663,7 @@ extension JNISwift2JavaGenerator {
if let selfParameter = nativeSignature.selfParameter?.parameters {
parameters += selfParameter
}
+ parameters += nativeSignature.selfTypeParameters.flatMap(\.parameters)
parameters += nativeSignature.result.outParameters
let renderedParameters = parameters.map { javaParameter in
@@ -658,6 +692,12 @@ extension JNISwift2JavaGenerator {
arguments.append(lowered)
}
+ // Generic metadata pointers in Self
+ for typeParameter in translatedFunctionSignature.selfTypeParameters {
+ let lowered = typeParameter.conversion.render(&printer, "this")
+ arguments.append(lowered)
+ }
+
// Indirect return receivers
for outParameter in translatedFunctionSignature.resultType.outParameters {
printer.print(
@@ -682,7 +722,14 @@ extension JNISwift2JavaGenerator {
}
private func printTypeMetadataAddressFunction(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
- printer.print("private static native long $typeMetadataAddressDowncall();")
+ var downcallParams = [String]()
+ if !type.genericParameters.isEmpty {
+ for (index, _) in type.genericParameters.enumerated() {
+ downcallParams.append("t\(index)MetaPointer")
+ }
+ }
+ let downcallArg = downcallParams.map { "long \($0)" }.joined(separator: ", ")
+ printer.print("private static native long $typeMetadataAddressDowncall(\(downcallArg));")
let funcName = "$typeMetadataAddress"
printer.print("@Override")
@@ -695,9 +742,9 @@ extension JNISwift2JavaGenerator {
"this", this,
"self", self$);
}
- return \(type.swiftNominal.name).$typeMetadataAddressDowncall();
"""
)
+ printer.print("return \(type.swiftNominal.name).$typeMetadataAddressDowncall(\(downcallParams.joined(separator: ", ")));")
}
}
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
index 436956d2..c1561329 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
@@ -155,6 +155,7 @@ extension JNISwift2JavaGenerator {
]
)
),
+ selfTypeParameters: [], // TODO: iceman
parameters: [],
resultType: TranslatedResult(
javaType: .class(package: nil, name: "Optional<\(caseName)>"),
@@ -172,6 +173,7 @@ extension JNISwift2JavaGenerator {
indirectConversion: nil,
conversionCheck: nil
),
+ selfTypeParameters: [], // TODO: iceman
parameters: [],
result: NativeResult(
javaType: nativeParametersType,
@@ -321,6 +323,22 @@ extension JNISwift2JavaGenerator {
genericRequirements: functionSignature.genericRequirements
)
+ func translateTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> [TranslatedParameter] {
+ decl.genericParameters.enumerated().map { index, _ in
+ TranslatedParameter(
+ parameter: JavaParameter(name: "t\(index)MetaPointer", type: .long),
+ conversion: .member("t\(index)MetaPointer")
+ )
+ }
+ }
+ let typeParameters: [TranslatedParameter] = switch functionSignature.selfParameter {
+ case .instance(let selfParameter):
+ selfParameter.type.asNominalTypeDeclaration.map(translateTypeParameters) ?? []
+ case .initializer(let swiftType), .staticMethod(let swiftType):
+ swiftType.asNominalTypeDeclaration.map(translateTypeParameters) ?? []
+ case nil: []
+ }
+
var exceptions: [JavaExceptionType] = []
if functionSignature.parameters.contains(where: \.type.isArchDependingInteger) {
@@ -331,6 +349,7 @@ extension JNISwift2JavaGenerator {
return TranslatedFunctionSignature(
selfParameter: selfParameter,
+ selfTypeParameters: typeParameters,
parameters: parameters,
resultType: resultType,
exceptions: exceptions
@@ -1049,6 +1068,7 @@ extension JNISwift2JavaGenerator {
struct TranslatedFunctionSignature {
var selfParameter: TranslatedParameter?
+ var selfTypeParameters: [TranslatedParameter]
var parameters: [TranslatedParameter]
var resultType: TranslatedResult
var exceptions: [JavaExceptionType]
@@ -1148,6 +1168,8 @@ extension JNISwift2JavaGenerator {
indirect case call(JavaNativeConversionStep, function: String)
+ case member(String)
+
indirect case method(JavaNativeConversionStep, function: String, arguments: [JavaNativeConversionStep] = [])
case isOptionalPresent
@@ -1258,6 +1280,9 @@ extension JNISwift2JavaGenerator {
let inner = inner.render(&printer, placeholder)
return "\(function)(\(inner))"
+ case .member(let member):
+ return "\(placeholder).\(member)"
+
case .isOptionalPresent:
return "(byte) (\(placeholder).isPresent() ? 1 : 0)"
@@ -1365,7 +1390,7 @@ extension JNISwift2JavaGenerator {
/// Whether the conversion uses SwiftArena.
var requiresSwiftArena: Bool {
switch self {
- case .placeholder, .constant, .isOptionalPresent, .combinedName:
+ case .placeholder, .constant, .isOptionalPresent, .combinedName, .member:
return false
case .constructSwiftValue, .wrapMemoryAddressUnsafe:
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
index 5bbeff4e..ca82cae5 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
@@ -61,10 +61,31 @@ extension JNISwift2JavaGenerator {
nil
}
+ func translateTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> [NativeParameter] {
+ decl.genericParameters.enumerated().map { index, _ in
+ NativeParameter(
+ parameters: [
+ JavaParameter(name: "t\(index)MetaPointer", type: .long)
+ ],
+ conversion: .genericMetadataPointer(.placeholder),
+ indirectConversion: nil,
+ conversionCheck: nil
+ )
+ }
+ }
+ let typeParameters: [NativeParameter] = switch functionSignature.selfParameter {
+ case .instance(let selfParameter):
+ selfParameter.type.asNominalTypeDeclaration.map(translateTypeParameters) ?? []
+ case .initializer(let swiftType), .staticMethod(let swiftType):
+ swiftType.asNominalTypeDeclaration.map(translateTypeParameters) ?? []
+ case nil: []
+ }
+
let result = try translate(swiftResult: functionSignature.result)
return NativeFunctionSignature(
selfParameter: nativeSelf,
+ selfTypeParameters: typeParameters,
parameters: parameters,
result: result
)
@@ -768,6 +789,7 @@ extension JNISwift2JavaGenerator {
struct NativeFunctionSignature {
let selfParameter: NativeParameter?
+ var selfTypeParameters: [NativeParameter]
var parameters: [NativeParameter]
var result: NativeResult
}
@@ -836,6 +858,8 @@ extension JNISwift2JavaGenerator {
convertLongFromJNI: Bool = true
)
+ indirect case genericMetadataPointer(NativeSwiftConversionStep)
+
/// Allocate memory for a Swift value and outputs the pointer
indirect case allocateSwiftValue(NativeSwiftConversionStep, name: String, swiftType: SwiftType)
@@ -1034,6 +1058,20 @@ extension JNISwift2JavaGenerator {
}
return pointerName
+ case .genericMetadataPointer(let inner):
+ let inner = inner.render(&printer, placeholder)
+ let bitsName = "\(inner)Bits$"
+ let pointerName = "\(inner)Pointer$"
+ printer.print(
+ """
+ let \(bitsName) = Int(Int64(fromJNI: \(inner), in: environment))
+ guard let \(pointerName) = UnsafeRawPointer(bitPattern: \(bitsName)) else {
+ fatalError("\(inner) metadata address was null")
+ }
+ """
+ )
+ return "unsafeBitCast(\(pointerName), to: Any.Type.self)"
+
case .allocateSwiftValue(let inner, let name, let swiftType):
let inner = inner.render(&printer, placeholder)
let pointerName = "\(name)$"
diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift
index 4c6b8b0c..d7bd8a58 100644
--- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift
+++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift
@@ -73,7 +73,8 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration {
/// MyCollection.Iterator.
let parent: SwiftNominalTypeDeclaration?
- // TODO: Generic parameters.
+ /// The generic parameters of this nominal type.
+ let genericParameters: [SwiftGenericParameterDeclaration]
/// Identify this nominal declaration as one of the known standard library
/// types, like 'Swift.Int[.
@@ -91,6 +92,9 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration {
) {
self.parent = parent
self.syntax = node
+ self.genericParameters = node.asProtocol(WithGenericParametersSyntax.self)?.genericParameterClause?.parameters.map {
+ SwiftGenericParameterDeclaration(sourceFilePath: sourceFilePath, moduleName: moduleName, node: $0)
+ } ?? []
// Determine the kind from the syntax node.
switch Syntax(node).as(SyntaxEnum.self) {
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index b0acd96a..8a78a459 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -51,14 +51,17 @@ struct JNIGenericTypeTests {
private static native java.lang.String $getDescription(long self, long t0MetaPointer);
""",
"""
+ private static native java.lang.String $toString(long selfPointer, long t0MetaPointer);
+ """,
+ """
private static native long $typeMetadataAddressDowncall(long t0MetaPointer);
@Override
public long $typeMetadataAddress() {
long self$ = this.$memoryAddress();
if (CallTraces.TRACE_DOWNCALLS) {
- CallTraces.traceDowncall("MyID.$typeMetadataAddress",
- "this", this,
- "self", self$);
+ CallTraces.traceDowncall("MyID.$typeMetadataAddress",
+ "this", this,
+ "self", self$);
}
return MyID.$typeMetadataAddressDowncall(t0MetaPointer);
}
From f935933a7ce09d27ed411d49af2c035f65d9950d Mon Sep 17 00:00:00 2001
From: Iceman
Date: Mon, 16 Feb 2026 14:28:57 +0900
Subject: [PATCH 03/25] Keep type parameter names short in arguments
---
...t2JavaGenerator+JavaBindingsPrinting.swift | 20 ++++++++++---------
...ISwift2JavaGenerator+JavaTranslation.swift | 2 +-
...wift2JavaGenerator+NativeTranslation.swift | 2 +-
.../JNI/JNIGenericTypeTests.swift | 11 ++++++----
4 files changed, 20 insertions(+), 15 deletions(-)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
index 8dbc4e5b..2a7cc853 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
@@ -322,7 +322,7 @@ extension JNISwift2JavaGenerator {
var arguments = ["selfPointer"]
var parameters = ["this.$memoryAddress()"]
for (index, _) in decl.genericParameters.enumerated() {
- arguments.append("t\(index)MetaPointer")
+ arguments.append("t\(index)Meta")
parameters.append("this.t\(index)MetaPointer")
}
let argument = arguments.map { "long \($0)" }.joined(separator: ", ")
@@ -722,14 +722,16 @@ extension JNISwift2JavaGenerator {
}
private func printTypeMetadataAddressFunction(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
- var downcallParams = [String]()
- if !type.genericParameters.isEmpty {
- for (index, _) in type.genericParameters.enumerated() {
- downcallParams.append("t\(index)MetaPointer")
- }
+ var arguments = [String]()
+ var parameters = [String]()
+ for (index, _) in type.genericParameters.enumerated() {
+ arguments.append("t\(index)Meta")
+ parameters.append("this.t\(index)MetaPointer")
}
- let downcallArg = downcallParams.map { "long \($0)" }.joined(separator: ", ")
- printer.print("private static native long $typeMetadataAddressDowncall(\(downcallArg));")
+ let argument = arguments.map { "long \($0)" }.joined(separator: ", ")
+ let parameter = parameters.joined(separator: ", ")
+
+ printer.print("private static native long $typeMetadataAddressDowncall(\(argument));")
let funcName = "$typeMetadataAddress"
printer.print("@Override")
@@ -744,7 +746,7 @@ extension JNISwift2JavaGenerator {
}
"""
)
- printer.print("return \(type.swiftNominal.name).$typeMetadataAddressDowncall(\(downcallParams.joined(separator: ", ")));")
+ printer.print("return \(type.swiftNominal.name).$typeMetadataAddressDowncall(\(parameter));")
}
}
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
index c1561329..4568afdf 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
@@ -326,7 +326,7 @@ extension JNISwift2JavaGenerator {
func translateTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> [TranslatedParameter] {
decl.genericParameters.enumerated().map { index, _ in
TranslatedParameter(
- parameter: JavaParameter(name: "t\(index)MetaPointer", type: .long),
+ parameter: JavaParameter(name: "t\(index)Meta", type: .long),
conversion: .member("t\(index)MetaPointer")
)
}
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
index ca82cae5..f209d906 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
@@ -65,7 +65,7 @@ extension JNISwift2JavaGenerator {
decl.genericParameters.enumerated().map { index, _ in
NativeParameter(
parameters: [
- JavaParameter(name: "t\(index)MetaPointer", type: .long)
+ JavaParameter(name: "t\(index)Meta", type: .long)
],
conversion: .genericMetadataPointer(.placeholder),
indirectConversion: nil,
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index 8a78a459..1bf1a2f3 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -48,13 +48,16 @@ struct JNIGenericTypeTests {
public java.lang.String getDescription() {
return MyID.$getDescription(this.$memoryAddress(), this.t0MetaPointer);
}
- private static native java.lang.String $getDescription(long self, long t0MetaPointer);
+ private static native java.lang.String $getDescription(long self, long t0Meta);
""",
"""
- private static native java.lang.String $toString(long selfPointer, long t0MetaPointer);
+ public String toString() {
+ return $toString(this.$memoryAddress(), this.t0MetaPointer);
+ }
+ private static native java.lang.String $toString(long selfPointer, long t0Meta);
""",
"""
- private static native long $typeMetadataAddressDowncall(long t0MetaPointer);
+ private static native long $typeMetadataAddressDowncall(long t0Meta);
@Override
public long $typeMetadataAddress() {
long self$ = this.$memoryAddress();
@@ -63,7 +66,7 @@ struct JNIGenericTypeTests {
"this", this,
"self", self$);
}
- return MyID.$typeMetadataAddressDowncall(t0MetaPointer);
+ return MyID.$typeMetadataAddressDowncall(this.t0MetaPointer);
}
"""
]
From 4ff9e07ac329bf5a10a264e45f7d12909c25364d Mon Sep 17 00:00:00 2001
From: Iceman
Date: Mon, 16 Feb 2026 17:27:51 +0900
Subject: [PATCH 04/25] Changed to only handle metatypes for Self
---
...t2JavaGenerator+JavaBindingsPrinting.swift | 58 +++++++++----------
...ISwift2JavaGenerator+JavaTranslation.swift | 27 ++++-----
...wift2JavaGenerator+NativeTranslation.swift | 21 +++----
.../JNI/JNIGenericTypeTests.swift | 20 +++----
4 files changed, 62 insertions(+), 64 deletions(-)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
index 2a7cc853..1a988e4a 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
@@ -202,8 +202,8 @@ extension JNISwift2JavaGenerator {
"""
)
var swiftPointerParams = ["selfPointer"]
- for (index, _) in decl.genericParameters.enumerated() {
- swiftPointerParams.append("t\(index)MetaPointer")
+ if !decl.genericParameters.isEmpty {
+ swiftPointerParams.append("selfTypePointer")
}
let swiftPointerArg = swiftPointerParams.map { "long \($0)" }.joined(separator: ", ")
printer.printBraceBlock("private \(decl.swiftNominal.name)(\(swiftPointerArg), SwiftArena swiftArena)") { printer in
@@ -264,9 +264,9 @@ extension JNISwift2JavaGenerator {
"""
)
- for (index, param) in decl.genericParameters.enumerated() {
- printer.print("/** Pointer to the metatype of type parameter '\(param.name)' */")
- printer.print("private final long t\(index)MetaPointer;")
+ if !decl.genericParameters.isEmpty {
+ printer.print("/** Pointer to the metatype of Self */")
+ printer.print("private final long selfTypePointer;")
}
printer.println()
@@ -321,9 +321,9 @@ extension JNISwift2JavaGenerator {
private func printToStringMethods(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
var arguments = ["selfPointer"]
var parameters = ["this.$memoryAddress()"]
- for (index, _) in decl.genericParameters.enumerated() {
- arguments.append("t\(index)Meta")
- parameters.append("this.t\(index)MetaPointer")
+ if !decl.genericParameters.isEmpty {
+ arguments.append("selfType")
+ parameters.append("this.$typeMetadataAddress()")
}
let argument = arguments.map { "long \($0)" }.joined(separator: ", ")
let parameter = parameters.joined(separator: ", ")
@@ -663,7 +663,9 @@ extension JNISwift2JavaGenerator {
if let selfParameter = nativeSignature.selfParameter?.parameters {
parameters += selfParameter
}
- parameters += nativeSignature.selfTypeParameters.flatMap(\.parameters)
+ if let selfTypeParameter = nativeSignature.selfTypeParameter?.parameters {
+ parameters += selfTypeParameter
+ }
parameters += nativeSignature.result.outParameters
let renderedParameters = parameters.map { javaParameter in
@@ -692,9 +694,9 @@ extension JNISwift2JavaGenerator {
arguments.append(lowered)
}
- // Generic metadata pointers in Self
- for typeParameter in translatedFunctionSignature.selfTypeParameters {
- let lowered = typeParameter.conversion.render(&printer, "this")
+ // 'Self' metatype.
+ if let selfTypeParameter = translatedFunctionSignature.selfTypeParameter {
+ let lowered = selfTypeParameter.conversion.render(&printer, "this")
arguments.append(lowered)
}
@@ -722,21 +724,18 @@ extension JNISwift2JavaGenerator {
}
private func printTypeMetadataAddressFunction(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
- var arguments = [String]()
- var parameters = [String]()
- for (index, _) in type.genericParameters.enumerated() {
- arguments.append("t\(index)Meta")
- parameters.append("this.t\(index)MetaPointer")
- }
- let argument = arguments.map { "long \($0)" }.joined(separator: ", ")
- let parameter = parameters.joined(separator: ", ")
-
- printer.print("private static native long $typeMetadataAddressDowncall(\(argument));")
-
- let funcName = "$typeMetadataAddress"
- printer.print("@Override")
- printer.printBraceBlock("public long $typeMetadataAddress()") { printer in
- printer.print(
+ if !type.genericParameters.isEmpty {
+ printer.print("@Override")
+ printer.printBraceBlock("public long $typeMetadataAddress()") { printer in
+ printer.print("return this.selfTypePointer;")
+ }
+ } else {
+ printer.print("private static native long $typeMetadataAddressDowncall();")
+
+ let funcName = "$typeMetadataAddress"
+ printer.print("@Override")
+ printer.printBraceBlock("public long $typeMetadataAddress()") { printer in
+ printer.print(
"""
long self$ = this.$memoryAddress();
if (CallTraces.TRACE_DOWNCALLS) {
@@ -745,8 +744,9 @@ extension JNISwift2JavaGenerator {
"self", self$);
}
"""
- )
- printer.print("return \(type.swiftNominal.name).$typeMetadataAddressDowncall(\(parameter));")
+ )
+ printer.print("return \(type.swiftNominal.name).$typeMetadataAddressDowncall();")
+ }
}
}
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
index 4568afdf..a0b64167 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
@@ -155,7 +155,7 @@ extension JNISwift2JavaGenerator {
]
)
),
- selfTypeParameters: [], // TODO: iceman
+ selfTypeParameter: nil, // TODO: iceman
parameters: [],
resultType: TranslatedResult(
javaType: .class(package: nil, name: "Optional<\(caseName)>"),
@@ -173,7 +173,7 @@ extension JNISwift2JavaGenerator {
indirectConversion: nil,
conversionCheck: nil
),
- selfTypeParameters: [], // TODO: iceman
+ selfTypeParameter: nil, // TODO: iceman
parameters: [],
result: NativeResult(
javaType: nativeParametersType,
@@ -323,20 +323,21 @@ extension JNISwift2JavaGenerator {
genericRequirements: functionSignature.genericRequirements
)
- func translateTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> [TranslatedParameter] {
- decl.genericParameters.enumerated().map { index, _ in
- TranslatedParameter(
- parameter: JavaParameter(name: "t\(index)Meta", type: .long),
- conversion: .member("t\(index)MetaPointer")
+ func translateSelfTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> TranslatedParameter? {
+ if !decl.genericParameters.isEmpty {
+ return TranslatedParameter(
+ parameter: JavaParameter(name: "selfType", type: .long),
+ conversion: .typeMetadataAddress(.placeholder)
)
}
+ return nil
}
- let typeParameters: [TranslatedParameter] = switch functionSignature.selfParameter {
+ let selfTypeParameter: TranslatedParameter? = switch functionSignature.selfParameter {
case .instance(let selfParameter):
- selfParameter.type.asNominalTypeDeclaration.map(translateTypeParameters) ?? []
+ selfParameter.type.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
case .initializer(let swiftType), .staticMethod(let swiftType):
- swiftType.asNominalTypeDeclaration.map(translateTypeParameters) ?? []
- case nil: []
+ swiftType.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
+ case nil: nil
}
var exceptions: [JavaExceptionType] = []
@@ -349,7 +350,7 @@ extension JNISwift2JavaGenerator {
return TranslatedFunctionSignature(
selfParameter: selfParameter,
- selfTypeParameters: typeParameters,
+ selfTypeParameter: selfTypeParameter,
parameters: parameters,
resultType: resultType,
exceptions: exceptions
@@ -1068,7 +1069,7 @@ extension JNISwift2JavaGenerator {
struct TranslatedFunctionSignature {
var selfParameter: TranslatedParameter?
- var selfTypeParameters: [TranslatedParameter]
+ var selfTypeParameter: TranslatedParameter?
var parameters: [TranslatedParameter]
var resultType: TranslatedResult
var exceptions: [JavaExceptionType]
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
index f209d906..f99b5cf6 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
@@ -61,31 +61,32 @@ extension JNISwift2JavaGenerator {
nil
}
- func translateTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> [NativeParameter] {
- decl.genericParameters.enumerated().map { index, _ in
- NativeParameter(
+ func translateSelfTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> NativeParameter? {
+ if !decl.genericParameters.isEmpty {
+ return NativeParameter(
parameters: [
- JavaParameter(name: "t\(index)Meta", type: .long)
+ JavaParameter(name: "selfType", type: .long)
],
conversion: .genericMetadataPointer(.placeholder),
indirectConversion: nil,
conversionCheck: nil
)
}
+ return nil
}
- let typeParameters: [NativeParameter] = switch functionSignature.selfParameter {
+ let selfTypeParameter: NativeParameter? = switch functionSignature.selfParameter {
case .instance(let selfParameter):
- selfParameter.type.asNominalTypeDeclaration.map(translateTypeParameters) ?? []
+ selfParameter.type.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
case .initializer(let swiftType), .staticMethod(let swiftType):
- swiftType.asNominalTypeDeclaration.map(translateTypeParameters) ?? []
- case nil: []
+ swiftType.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
+ case nil: nil
}
let result = try translate(swiftResult: functionSignature.result)
return NativeFunctionSignature(
selfParameter: nativeSelf,
- selfTypeParameters: typeParameters,
+ selfTypeParameter: selfTypeParameter,
parameters: parameters,
result: result
)
@@ -789,7 +790,7 @@ extension JNISwift2JavaGenerator {
struct NativeFunctionSignature {
let selfParameter: NativeParameter?
- var selfTypeParameters: [NativeParameter]
+ var selfTypeParameter: NativeParameter?
var parameters: [NativeParameter]
var result: NativeResult
}
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index 1bf1a2f3..ee970bae 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -36,35 +36,31 @@ struct JNIGenericTypeTests {
input: genericFile,
.jni,
.java,
- detectChunkByInitialLines: 1,
+ detectChunkByInitialLines: 2,
expectedChunks: [
"""
public final class MyID implements JNISwiftInstance {
""",
"""
- private final long t0MetaPointer;
+ private final long selfTypePointer;
""",
"""
public java.lang.String getDescription() {
- return MyID.$getDescription(this.$memoryAddress(), this.t0MetaPointer);
+ return MyID.$getDescription(this.$memoryAddress(), this.$typeMetadataAddress());
}
- private static native java.lang.String $getDescription(long self, long t0Meta);
+ private static native java.lang.String $getDescription(long self, long selfType);
""",
"""
public String toString() {
- return $toString(this.$memoryAddress(), this.t0MetaPointer);
+ return $toString(this.$memoryAddress(), this.$typeMetadataAddress());
}
- private static native java.lang.String $toString(long selfPointer, long t0Meta);
+ private static native java.lang.String $toString(long selfPointer, long selfType);
""",
"""
- private static native long $typeMetadataAddressDowncall(long t0Meta);
@Override
public long $typeMetadataAddress() {
- long self$ = this.$memoryAddress();
- if (CallTraces.TRACE_DOWNCALLS) {
- CallTraces.traceDowncall("MyID.$typeMetadataAddress",
- "this", this,
- "self", self$);
+ return this.selfTypePointer;
+ }
}
return MyID.$typeMetadataAddressDowncall(this.t0MetaPointer);
}
From 86ce9ac05f186682703914ac6b219b596b2cb0cc Mon Sep 17 00:00:00 2001
From: Iceman
Date: Mon, 16 Feb 2026 17:51:19 +0900
Subject: [PATCH 05/25] Add test case for swift side generated code
---
.../JNI/JNIGenericTypeTests.swift | 69 ++++++++++++++++++-
1 file changed, 68 insertions(+), 1 deletion(-)
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index ee970bae..eb642eba 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -61,10 +61,77 @@ struct JNIGenericTypeTests {
public long $typeMetadataAddress() {
return this.selfTypePointer;
}
+ """,
+ ]
+ )
+ }
+
+ @Test
+ func generateSwiftThunk() throws {
+ try assertOutput(
+ input: genericFile,
+ .jni,
+ .swift,
+ detectChunkByInitialLines: 2,
+ expectedChunks: [
+ """
+ protocol _SwiftModule_MyID_opener {
+ static func _get_description(selfBits$: Int) -> String
+ static func _toString(selfBits$: Int) -> String
+ static func _toDebugString(selfBits$: Int) -> String
+ }
+ """,
+ """
+ extension MyID: _SwiftModule_MyID_opener {
+ static func _get_description(selfBits$: Int) -> String {
+ let self$ = UnsafeMutablePointer(bitPattern: selfBits$)
+ guard let self$ else {
+ fatalError("self memory address was null in call to \\(#function)!")
+ }
+ return self$.pointee.description
+ }
+ static func _toString(selfBits$: Int) -> String {
+ let self$ = UnsafeMutablePointer(bitPattern: selfBits$)
+ guard let self$ else {
+ fatalError("self memory address was null in call to \\(#function)!")
+ }
+ return String(describing: self$.pointee)
+ }
+ static func _toDebugString(selfBits$: Int) -> String {
+ let self$ = UnsafeMutablePointer(bitPattern: selfBits$)
+ guard let self$ else {
+ fatalError("self memory address was null in call to \\(#function)!")
+ }
+ return String(reflecting: self$.pointee)
+ }
+ }
+ """,
+ """
+ @_cdecl("Java_com_example_swift_MyID__00024getDescription__J")
+ public func Java_com_example_swift_MyID__00024getDescription__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong, selfType: jlong) -> jstring? {
+ assert(self != 0, "self memory address was null")
+ let selfBits$ = Int(Int64(fromJNI: self, in: environment))
+ guard let selfTypeMetadataPointer$ = UnsafeRawPointer(bitPattern: Int(Int64(fromJNI: selfType, in: environment))) else {
+ fatalError("selfType memory address was null")
}
- return MyID.$typeMetadataAddressDowncall(this.t0MetaPointer);
+ let erasedSelfType$: Any.Type = unsafeBitCast(selfTypeMetadataPointer$, to: Any.Type.self)
+ let openerType = erasedSelfType$ as! (any _SwiftModule_MyID_opener.Type)
+ return openerType._get_description(selfBits$: selfBits$).getJNIValue(in: environment)
}
+ """,
"""
+ @_cdecl("Java_com_example_swift_MyID__00024toString__J")
+ public func Java_com_example_swift_MyID__00024toString__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong, selfType: jlong) -> jstring? {
+ assert(self != 0, "self memory address was null")
+ let selfBits$ = Int(Int64(fromJNI: self, in: environment))
+ guard let selfTypeMetadataPointer$ = UnsafeRawPointer(bitPattern: Int(Int64(fromJNI: selfType, in: environment))) else {
+ fatalError("selfType memory address was null")
+ }
+ let erasedSelfType$: Any.Type = unsafeBitCast(selfTypeMetadataPointer$, to: Any.Type.self)
+ let openerType = erasedSelfType$ as! (any _SwiftModule_MyID_opener.Type)
+ return openerType._toString(selfBits$: selfBits$).getJNIValue(in: environment)
+ }
+ """,
]
)
}
From 49fbd08a3448aef8771e626e438a4c2b987e4d6c Mon Sep 17 00:00:00 2001
From: Iceman
Date: Mon, 16 Feb 2026 17:53:16 +0900
Subject: [PATCH 06/25] Remove unused step
---
.../JNI/JNISwift2JavaGenerator+JavaTranslation.swift | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
index a0b64167..2b9137de 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
@@ -1169,8 +1169,6 @@ extension JNISwift2JavaGenerator {
indirect case call(JavaNativeConversionStep, function: String)
- case member(String)
-
indirect case method(JavaNativeConversionStep, function: String, arguments: [JavaNativeConversionStep] = [])
case isOptionalPresent
@@ -1281,9 +1279,6 @@ extension JNISwift2JavaGenerator {
let inner = inner.render(&printer, placeholder)
return "\(function)(\(inner))"
- case .member(let member):
- return "\(placeholder).\(member)"
-
case .isOptionalPresent:
return "(byte) (\(placeholder).isPresent() ? 1 : 0)"
@@ -1391,7 +1386,7 @@ extension JNISwift2JavaGenerator {
/// Whether the conversion uses SwiftArena.
var requiresSwiftArena: Bool {
switch self {
- case .placeholder, .constant, .isOptionalPresent, .combinedName, .member:
+ case .placeholder, .constant, .isOptionalPresent, .combinedName:
return false
case .constructSwiftValue, .wrapMemoryAddressUnsafe:
From 32d99789611afea288a825cc75e88a4c44c592b2 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Wed, 18 Feb 2026 10:40:13 +0900
Subject: [PATCH 07/25] Print opener thunk
---
.../Sources/MySwiftLibrary/GenericType.swift | 23 ++
Sources/JExtractSwiftLib/ImportedDecls.swift | 3 -
...t2JavaGenerator+JavaBindingsPrinting.swift | 8 +-
...ISwift2JavaGenerator+JavaTranslation.swift | 2 +-
...wift2JavaGenerator+NativeTranslation.swift | 16 +-
...ift2JavaGenerator+SwiftThunkPrinting.swift | 203 ++++++++++++++++--
.../SwiftNominalTypeDeclaration.swift | 4 +
.../JNI/JNIGenericTypeTests.swift | 67 ++----
8 files changed, 249 insertions(+), 77 deletions(-)
diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
index c3134ba4..a8444637 100644
--- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
+++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
@@ -17,5 +17,28 @@ public struct MyID {
public init(_ rawValue: T) {
self.rawValue = rawValue
}
+ public var description: String {
+ "\(rawValue)"
+ }
+}
+
+public func makeIntID(_ value: Int) -> MyID {
+ return MyID(value)
+}
+
+public func makeStringID(_ value: String) -> MyID {
+ return MyID(value)
}
+public struct MyEntity {
+ public var id: MyID
+ public var name: String
+ public init(id: MyID, name: String) {
+ self.id = id
+ self.name = name
+ }
+}
+
+public func takeMyEntity() -> MyEntity {
+ return MyEntity(id: MyID(42), name: "Example")
+}
diff --git a/Sources/JExtractSwiftLib/ImportedDecls.swift b/Sources/JExtractSwiftLib/ImportedDecls.swift
index b67b4e43..7bb2f041 100644
--- a/Sources/JExtractSwiftLib/ImportedDecls.swift
+++ b/Sources/JExtractSwiftLib/ImportedDecls.swift
@@ -43,9 +43,6 @@ package final class ImportedNominalType: ImportedDecl {
package var variables: [ImportedFunc] = []
package var cases: [ImportedEnumCase] = []
var inheritedTypes: [SwiftType]
- package var genericParameters: [SwiftGenericParameterDeclaration] {
- self.swiftNominal.genericParameters
- }
package var parent: SwiftNominalTypeDeclaration?
init(swiftNominal: SwiftNominalTypeDeclaration, lookupContext: SwiftTypeLookupContext) throws {
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
index 1a988e4a..2f92d93c 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
@@ -202,7 +202,7 @@ extension JNISwift2JavaGenerator {
"""
)
var swiftPointerParams = ["selfPointer"]
- if !decl.genericParameters.isEmpty {
+ if decl.swiftNominal.isGeneric {
swiftPointerParams.append("selfTypePointer")
}
let swiftPointerArg = swiftPointerParams.map { "long \($0)" }.joined(separator: ", ")
@@ -264,7 +264,7 @@ extension JNISwift2JavaGenerator {
"""
)
- if !decl.genericParameters.isEmpty {
+ if decl.swiftNominal.isGeneric {
printer.print("/** Pointer to the metatype of Self */")
printer.print("private final long selfTypePointer;")
}
@@ -321,7 +321,7 @@ extension JNISwift2JavaGenerator {
private func printToStringMethods(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
var arguments = ["selfPointer"]
var parameters = ["this.$memoryAddress()"]
- if !decl.genericParameters.isEmpty {
+ if decl.swiftNominal.isGeneric {
arguments.append("selfType")
parameters.append("this.$typeMetadataAddress()")
}
@@ -724,7 +724,7 @@ extension JNISwift2JavaGenerator {
}
private func printTypeMetadataAddressFunction(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
- if !type.genericParameters.isEmpty {
+ if type.swiftNominal.isGeneric {
printer.print("@Override")
printer.printBraceBlock("public long $typeMetadataAddress()") { printer in
printer.print("return this.selfTypePointer;")
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
index 2b9137de..ce370718 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
@@ -324,7 +324,7 @@ extension JNISwift2JavaGenerator {
)
func translateSelfTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> TranslatedParameter? {
- if !decl.genericParameters.isEmpty {
+ if decl.isGeneric {
return TranslatedParameter(
parameter: JavaParameter(name: "selfType", type: .long),
conversion: .typeMetadataAddress(.placeholder)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
index f99b5cf6..dad9b7de 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
@@ -62,7 +62,7 @@ extension JNISwift2JavaGenerator {
}
func translateSelfTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> NativeParameter? {
- if !decl.genericParameters.isEmpty {
+ if decl.isGeneric {
return NativeParameter(
parameters: [
JavaParameter(name: "selfType", type: .long)
@@ -1047,7 +1047,12 @@ extension JNISwift2JavaGenerator {
} else {
printer.print("let \(inner)Bits$ = Int(\(inner))")
}
- printer.print("let \(pointerName) = UnsafeMutablePointer<\(swiftType)>(bitPattern: \(inner)Bits$)")
+ let typeName = if swiftType.asNominalTypeDeclaration?.isGeneric == true {
+ "Self"
+ } else {
+ swiftType.description
+ }
+ printer.print("let \(pointerName) = UnsafeMutablePointer<\(typeName)>(bitPattern: \(inner)Bits$)")
if !allowNil {
printer.print(
"""
@@ -1061,12 +1066,11 @@ extension JNISwift2JavaGenerator {
case .genericMetadataPointer(let inner):
let inner = inner.render(&printer, placeholder)
- let bitsName = "\(inner)Bits$"
- let pointerName = "\(inner)Pointer$"
+ let pointerName = "\(inner)$"
printer.print(
"""
- let \(bitsName) = Int(Int64(fromJNI: \(inner), in: environment))
- guard let \(pointerName) = UnsafeRawPointer(bitPattern: \(bitsName)) else {
+ let \(inner)Bits$ = Int(Int64(fromJNI: \(inner), in: environment))
+ guard let \(pointerName) = UnsafeRawPointer(bitPattern: \(inner)Bits$) else {
fatalError("\(inner) metadata address was null")
}
"""
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
index c12080ee..b91264a5 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
@@ -246,6 +246,11 @@ extension JNISwift2JavaGenerator {
}
private func printConcreteTypeThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
+ if type.swiftNominal.isGeneric {
+ printOpenerProtocol(&printer, type)
+ printer.println()
+ }
+
for initializer in type.initializers {
printSwiftFunctionThunk(&printer, initializer)
printer.println()
@@ -295,17 +300,25 @@ extension JNISwift2JavaGenerator {
javaMethodName: "$toString",
parentName: type.swiftNominal.qualifiedName,
parameters: [
- selfPointerParam
+ selfPointerParam,
+ JavaParameter(name: "selfType", type: .long)
],
resultType: .javaLangString
) { printer in
- let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam)
-
- printer.print(
- """
- return String(describing: \(selfVar).pointee).getJNIValue(in: environment)
- """
- )
+ if type.swiftNominal.isGeneric {
+ printer.print("let selfBits$ = Int(Int64(fromJNI: selfPointer, in: environment))")
+ printer.printBraceBlock(
+ "guard let selfTypeMetadataPointer$ = UnsafeRawPointer(bitPattern: Int(Int64(fromJNI: selfType, in: environment))) else"
+ ) { printer in
+ printer.print("fatalError(\"selfType memory address was null\")")
+ }
+ printer.print("let erasedSelfType$: Any.Type = unsafeBitCast(selfTypeMetadataPointer$, to: Any.Type.self)")
+ printer.print("let openerType = erasedSelfType$ as! (any \(openerProtocolName(for: type.swiftNominal)).Type)")
+ printer.print("return openerType._toString(selfBits$: selfBits$).getJNIValue(in: environment)")
+ } else {
+ let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam)
+ printer.print("return String(describing: \(selfVar).pointee).getJNIValue(in: environment)")
+ }
}
printer.println()
@@ -315,17 +328,25 @@ extension JNISwift2JavaGenerator {
javaMethodName: "$toDebugString",
parentName: type.swiftNominal.qualifiedName,
parameters: [
- selfPointerParam
+ selfPointerParam,
+ JavaParameter(name: "selfType", type: .long)
],
resultType: .javaLangString
) { printer in
- let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam)
-
- printer.print(
- """
- return String(reflecting: \(selfVar).pointee).getJNIValue(in: environment)
- """
- )
+ if type.swiftNominal.isGeneric {
+ printer.print("let selfBits$ = Int(Int64(fromJNI: selfPointer, in: environment))")
+ printer.printBraceBlock(
+ "guard let selfTypeMetadataPointer$ = UnsafeRawPointer(bitPattern: Int(Int64(fromJNI: selfType, in: environment))) else"
+ ) { printer in
+ printer.print("fatalError(\"selfType memory address was null\")")
+ }
+ printer.print("let erasedSelfType$: Any.Type = unsafeBitCast(selfTypeMetadataPointer$, to: Any.Type.self)")
+ printer.print("let openerType = erasedSelfType$ as! (any \(openerProtocolName(for: type.swiftNominal)).Type)")
+ printer.print("return openerType._toDebugString(selfBits$: selfBits$).getJNIValue(in: environment)")
+ } else {
+ let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam)
+ printer.print("return String(reflecting: \(selfVar).pointee).getJNIValue(in: environment)")
+ }
}
}
@@ -445,7 +466,11 @@ extension JNISwift2JavaGenerator {
&printer,
translatedDecl
) { printer in
- self.printFunctionDowncall(&printer, decl)
+ if let parent = decl.parentType?.asNominalType, parent.nominalTypeDecl.isGeneric {
+ self.printFunctionOpenerCall(&printer, decl)
+ } else {
+ self.printFunctionDowncall(&printer, decl)
+ }
}
}
@@ -712,7 +737,9 @@ extension JNISwift2JavaGenerator {
if let selfParameter = nativeSignature.selfParameter {
parameters += selfParameter.parameters
}
-
+ if let selfTypeParameter = nativeSignature.selfTypeParameter {
+ parameters += selfTypeParameter.parameters
+ }
parameters += nativeSignature.result.outParameters
printCDecl(
@@ -886,6 +913,135 @@ extension JNISwift2JavaGenerator {
}
}
+ private func printFunctionOpenerCall(_ printer: inout CodePrinter, _ decl: ImportedFunc) {
+ guard let translatedDecl = self.translatedDecl(for: decl) else {
+ fatalError("Cannot print function opener for a function that can't be translated: \(decl)")
+ }
+ guard let parentNominalType = decl.parentType?.asNominalType else {
+ fatalError("Only functions with nominal type parents can have openers")
+ }
+ let nativeSignature = translatedDecl.nativeFunctionSignature
+
+ let selfTypePointer = nativeSignature.selfTypeParameter!.conversion.render(&printer, "selfType")
+ let openerName = openerProtocolName(for: parentNominalType.nominalTypeDecl)
+ printer.print("let openerType = \(selfTypePointer) as! (any \(openerName).Type)")
+
+ var parameters = nativeSignature.parameters.flatMap(\.parameters)
+ if let selfParameter = nativeSignature.selfParameter {
+ parameters += selfParameter.parameters
+ }
+ parameters += nativeSignature.result.outParameters
+
+ let openerArguments =
+ [
+ "environment: environment",
+ "thisClass: thisClass",
+ ] + parameters.map { javaParameter in
+ "\(javaParameter.name): \(javaParameter.name)"
+ }
+ let call = "openerType.\(decl.openerMethodName)(\(openerArguments.joined(separator: ", ")))"
+
+ if !decl.functionSignature.result.type.isVoid {
+ printer.print("return \(call)")
+ } else {
+ printer.print(call)
+ }
+ }
+
+ private func openerProtocolName(for type: SwiftNominalTypeDeclaration) -> String {
+ "_\(swiftModuleName)_\(type.name)_opener"
+ }
+
+ private func printOpenerProtocol(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
+ let protocolName = openerProtocolName(for: type.swiftNominal)
+
+ func printFunctionDecl(_ printer: inout CodePrinter, decl: ImportedFunc, skipMethodBody: Bool) {
+ guard let translatedDecl = self.translatedDecl(for: decl) else { return }
+ let nativeSignature = translatedDecl.nativeFunctionSignature
+
+ var parameters = nativeSignature.parameters.flatMap(\.parameters)
+ if let selfParameter = nativeSignature.selfParameter {
+ parameters += selfParameter.parameters
+ }
+ parameters += nativeSignature.result.outParameters
+
+ let resultType = nativeSignature.result.javaType
+
+ let translatedParameters = parameters.map {
+ "\($0.name): \($0.type.jniTypeName)"
+ }
+
+ let thunkParameters =
+ [
+ "environment: UnsafeMutablePointer!",
+ "thisClass: jclass",
+ ] + translatedParameters
+ let thunkReturnType = resultType != .void ? " -> \(resultType.jniTypeName)" : ""
+
+ let signature = #"static func \#(decl.openerMethodName)(\#(thunkParameters.joined(separator: ", ")))\#(thunkReturnType)"#
+ if !skipMethodBody {
+ printer.printBraceBlock(signature) { printer in
+ printFunctionDowncall(&printer, decl)
+ }
+ } else {
+ printer.print(signature)
+ }
+ }
+
+ printer.printBraceBlock("protocol \(protocolName)") { printer in
+ for variable in type.variables {
+ if variable.isStatic {
+ logger.debug("Skipping \(variable.name), static variables in generic type is not yet supported.")
+ continue
+ }
+ printFunctionDecl(&printer, decl: variable, skipMethodBody: true)
+ }
+
+ for method in type.methods {
+ if method.isStatic {
+ logger.debug("Skipping \(method.name), static methods in generic type is not yet supported.")
+ continue
+ }
+ printFunctionDecl(&printer, decl: method, skipMethodBody: true)
+ }
+
+ printer.print("static func _toString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring?")
+ printer.print("static func _toDebugString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring?")
+ }
+ printer.println()
+ printer.printBraceBlock("extension \(type.swiftNominal.name): \(protocolName)") { printer in
+ for variable in type.variables {
+ if variable.isStatic { continue }
+ printFunctionDecl(&printer, decl: variable, skipMethodBody: false)
+ }
+
+ for method in type.methods {
+ if method.isStatic { continue }
+ printFunctionDecl(&printer, decl: method, skipMethodBody: false)
+ }
+
+ printer.printBraceBlock("static func _toString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> String") { printer in
+ printer.print(#"assert(self != 0, "self memory address was null")"#)
+ printer.print("let selfBits$ = Int(Int64(fromJNI: self, in: environment))")
+ printer.print("let self$ = UnsafeMutablePointer(bitPattern: selfBits$)")
+ printer.printBraceBlock("guard let self$ else") { printer in
+ printer.print("fatalError(\"self memory address was null in call to \\(#function)!\")")
+ }
+ printer.print("return String(describing: self$.pointee)")
+ }
+
+ printer.printBraceBlock("static func _toDebugString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> String") { printer in
+ printer.print(#"assert(self != 0, "self memory address was null")"#)
+ printer.print("let selfBits$ = Int(Int64(fromJNI: self, in: environment))")
+ printer.print("let self$ = UnsafeMutablePointer(bitPattern: selfBits$)")
+ printer.printBraceBlock("guard let self$ else") { printer in
+ printer.print("fatalError(\"self memory address was null in call to \\(#function)!\")")
+ }
+ printer.print("return String(reflecting: self$.pointee)")
+ }
+ }
+ }
+
/// Print the necessary conversion logic to go from a `jlong` to a `UnsafeMutablePointer`
///
/// - Returns: name of the created "self" variable
@@ -951,3 +1107,14 @@ extension SwiftNominalTypeDeclaration {
return "Java\(self.name)"
}
}
+
+extension ImportedFunc {
+ fileprivate var openerMethodName: String {
+ let prefix = switch apiKind {
+ case .getter: "_get_"
+ case .setter: "_set_"
+ default: "_"
+ }
+ return "\(prefix)\(name)"
+ }
+}
diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift
index d7bd8a58..45d305d8 100644
--- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift
+++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift
@@ -162,6 +162,10 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration {
return false
}
}
+
+ var isGeneric: Bool {
+ return !genericParameters.isEmpty
+ }
}
package class SwiftGenericParameterDeclaration: SwiftTypeDeclaration {
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index eb642eba..4a5d291c 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -28,6 +28,10 @@ struct JNIGenericTypeTests {
"\(rawValue)"
}
}
+
+ public func makeStringID(_ value: String) -> MyID {
+ return MyID(value)
+ }
"""#
@Test
@@ -76,62 +80,35 @@ struct JNIGenericTypeTests {
expectedChunks: [
"""
protocol _SwiftModule_MyID_opener {
- static func _get_description(selfBits$: Int) -> String
- static func _toString(selfBits$: Int) -> String
- static func _toDebugString(selfBits$: Int) -> String
+ static func _get_description(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring?
+ ...
}
""",
- """
+ #"""
extension MyID: _SwiftModule_MyID_opener {
- static func _get_description(selfBits$: Int) -> String {
- let self$ = UnsafeMutablePointer(bitPattern: selfBits$)
- guard let self$ else {
- fatalError("self memory address was null in call to \\(#function)!")
- }
- return self$.pointee.description
- }
- static func _toString(selfBits$: Int) -> String {
- let self$ = UnsafeMutablePointer(bitPattern: selfBits$)
- guard let self$ else {
- fatalError("self memory address was null in call to \\(#function)!")
- }
- return String(describing: self$.pointee)
- }
- static func _toDebugString(selfBits$: Int) -> String {
+ static func _get_description(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring? {
+ assert(self != 0, "self memory address was null")
+ let selfBits$ = Int(Int64(fromJNI: self, in: environment))
let self$ = UnsafeMutablePointer(bitPattern: selfBits$)
guard let self$ else {
- fatalError("self memory address was null in call to \\(#function)!")
+ fatalError("self memory address was null in call to \(#function)!")
}
- return String(reflecting: self$.pointee)
- }
- }
- """,
- """
- @_cdecl("Java_com_example_swift_MyID__00024getDescription__J")
- public func Java_com_example_swift_MyID__00024getDescription__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong, selfType: jlong) -> jstring? {
- assert(self != 0, "self memory address was null")
- let selfBits$ = Int(Int64(fromJNI: self, in: environment))
- guard let selfTypeMetadataPointer$ = UnsafeRawPointer(bitPattern: Int(Int64(fromJNI: selfType, in: environment))) else {
- fatalError("selfType memory address was null")
+ return self$.pointee.description.getJNIValue(in: environment)
}
- let erasedSelfType$: Any.Type = unsafeBitCast(selfTypeMetadataPointer$, to: Any.Type.self)
- let openerType = erasedSelfType$ as! (any _SwiftModule_MyID_opener.Type)
- return openerType._get_description(selfBits$: selfBits$).getJNIValue(in: environment)
+ ...
}
- """,
+ """#,
"""
- @_cdecl("Java_com_example_swift_MyID__00024toString__J")
- public func Java_com_example_swift_MyID__00024toString__J(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong, selfType: jlong) -> jstring? {
- assert(self != 0, "self memory address was null")
- let selfBits$ = Int(Int64(fromJNI: self, in: environment))
- guard let selfTypeMetadataPointer$ = UnsafeRawPointer(bitPattern: Int(Int64(fromJNI: selfType, in: environment))) else {
- fatalError("selfType memory address was null")
+ @_cdecl("Java_com_example_swift_MyID__00024getDescription__JJ")
+ public func Java_com_example_swift_MyID__00024getDescription__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong, selfType: jlong) -> jstring? {
+ let selfTypeBits$ = Int(Int64(fromJNI: selfType, in: environment))
+ guard let selfType$ = UnsafeRawPointer(bitPattern: selfTypeBits$) else {
+ fatalError("selfType metadata address was null")
}
- let erasedSelfType$: Any.Type = unsafeBitCast(selfTypeMetadataPointer$, to: Any.Type.self)
- let openerType = erasedSelfType$ as! (any _SwiftModule_MyID_opener.Type)
- return openerType._toString(selfBits$: selfBits$).getJNIValue(in: environment)
+ let openerType = unsafeBitCast(selfType$, to: Any.Type.self) as! (any _SwiftModule_MyID_opener.Type)
+ return openerType._get_description(environment: environment, thisClass: thisClass, self: self)
}
- """,
+ """
]
)
}
From 55465d174851afc2c844f4e388a018d3f3f5dd4b Mon Sep 17 00:00:00 2001
From: Iceman
Date: Thu, 19 Feb 2026 15:16:40 +0900
Subject: [PATCH 08/25] Indirect generic return value
---
...t2JavaGenerator+JavaBindingsPrinting.swift | 9 ++-
...ISwift2JavaGenerator+JavaTranslation.swift | 37 +++++++++--
...wift2JavaGenerator+NativeTranslation.swift | 46 ++++++++++---
...ift2JavaGenerator+SwiftThunkPrinting.swift | 8 +--
Sources/JavaTypes/JavaType.swift | 6 ++
.../JNIMethodIDCaches.swift | 29 +++++++++
.../_JNIMethodIDCache.swift | 36 +++++++++-
.../core/OutSwiftGenericInstance.java | 20 ++++++
.../JNI/JNIGenericTypeTests.swift | 65 +++++++++++++++++--
9 files changed, 226 insertions(+), 30 deletions(-)
create mode 100644 SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
index 2f92d93c..20e5e329 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
@@ -496,16 +496,16 @@ extension JNISwift2JavaGenerator {
_ decl: ImportedFunc,
skipMethodBody: Bool = false
) {
- guard translatedDecl(for: decl) != nil else {
+ guard let translatedDecl = self.translatedDecl(for: decl) else {
// Failed to translate. Skip.
return
}
printer.printSeparator(decl.displayName)
- printJavaBindingWrapperHelperClass(&printer, decl)
+ printJavaBindingWrapperHelperClass(&printer, translatedDecl)
- printJavaBindingWrapperMethod(&printer, decl, skipMethodBody: skipMethodBody)
+ printJavaBindingWrapperMethod(&printer, translatedDecl, skipMethodBody: skipMethodBody)
}
/// Print the helper type container for a user-facing Java API.
@@ -513,9 +513,8 @@ extension JNISwift2JavaGenerator {
/// * User-facing functional interfaces.
private func printJavaBindingWrapperHelperClass(
_ printer: inout CodePrinter,
- _ decl: ImportedFunc
+ _ translated: TranslatedFunctionDecl
) {
- let translated = self.translatedDecl(for: decl)!
if translated.functionTypes.isEmpty {
return
}
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
index ce370718..39e7abda 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
@@ -793,12 +793,28 @@ extension JNISwift2JavaGenerator {
// We assume this is a JExtract class.
let javaType = JavaType.class(package: nil, name: nominalType.nominalTypeDecl.name)
- return TranslatedResult(
- javaType: javaType,
- annotations: resultAnnotations,
- outParameters: [],
- conversion: .wrapMemoryAddressUnsafe(.placeholder, javaType)
- )
+
+ if nominalType.nominalTypeDecl.isGeneric {
+ return TranslatedResult(
+ javaType: javaType,
+ annotations: resultAnnotations,
+ outParameters: [.init(name: "instance", type: .OutSwiftGenericInstance, allocation: .new) ],
+ conversion: .aggregate(variable: nil, [
+ .print(.placeholder),
+ .wrapMemoryAddressUnsafe(.commaSeparated([
+ .member(.constant("instance"), field: "selfPointer"),
+ .member(.constant("instance"), field: "selfTypePointer"),
+ ]), javaType)
+ ])
+ )
+ } else {
+ return TranslatedResult(
+ javaType: javaType,
+ annotations: resultAnnotations,
+ outParameters: [],
+ conversion: .wrapMemoryAddressUnsafe(.placeholder, javaType)
+ )
+ }
case .tuple([]):
return TranslatedResult(javaType: .void, outParameters: [], conversion: .placeholder)
@@ -1171,6 +1187,8 @@ extension JNISwift2JavaGenerator {
indirect case method(JavaNativeConversionStep, function: String, arguments: [JavaNativeConversionStep] = [])
+ indirect case member(JavaNativeConversionStep, field: String)
+
case isOptionalPresent
indirect case combinedValueToOptional(
@@ -1288,6 +1306,10 @@ extension JNISwift2JavaGenerator {
let argsStr = args.joined(separator: ", ")
return "\(inner).\(methodName)(\(argsStr))"
+ case .member(let inner, let fieldName):
+ let inner = inner.render(&printer, placeholder)
+ return "\(inner).\(fieldName)"
+
case .combinedValueToOptional(
let combined,
let combinedType,
@@ -1407,6 +1429,9 @@ extension JNISwift2JavaGenerator {
case .method(let inner, _, let args):
return inner.requiresSwiftArena || args.contains(where: \.requiresSwiftArena)
+ case .member(let inner, _):
+ return inner.requiresSwiftArena
+
case .combinedValueToOptional(let inner, _, _, _, _, _):
return inner.requiresSwiftArena
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
index dad9b7de..b9b7ae0b 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
@@ -67,7 +67,7 @@ extension JNISwift2JavaGenerator {
parameters: [
JavaParameter(name: "selfType", type: .long)
],
- conversion: .genericMetadataPointer(.placeholder),
+ conversion: .extractMetatypeValue(.placeholder),
indirectConversion: nil,
conversionCheck: nil
)
@@ -653,11 +653,23 @@ extension JNISwift2JavaGenerator {
throw JavaTranslationError.unsupportedSwiftType(swiftResult.type)
}
- return NativeResult(
- javaType: .long,
- conversion: .getJNIValue(.allocateSwiftValue(.placeholder, name: resultName, swiftType: swiftResult.type)),
- outParameters: []
- )
+ if nominalType.nominalTypeDecl.isGeneric {
+ return NativeResult(
+ javaType: .void,
+ conversion: .genericValueIndirectReturn(
+ .getJNIValue(.allocateSwiftValue(.placeholder, name: resultName, swiftType: swiftResult.type)),
+ swiftFunctionResultType: swiftResult.type,
+ outArgumentName: "out"
+ ),
+ outParameters: [.init(name: "out", type: .OutSwiftGenericInstance)]
+ )
+ } else {
+ return NativeResult(
+ javaType: .long,
+ conversion: .getJNIValue(.allocateSwiftValue(.placeholder, name: resultName, swiftType: swiftResult.type)),
+ outParameters: []
+ )
+ }
case .tuple([]):
return NativeResult(
@@ -859,7 +871,7 @@ extension JNISwift2JavaGenerator {
convertLongFromJNI: Bool = true
)
- indirect case genericMetadataPointer(NativeSwiftConversionStep)
+ indirect case extractMetatypeValue(NativeSwiftConversionStep)
/// Allocate memory for a Swift value and outputs the pointer
indirect case allocateSwiftValue(NativeSwiftConversionStep, name: String, swiftType: SwiftType)
@@ -898,6 +910,12 @@ extension JNISwift2JavaGenerator {
placeholderValue: NativeSwiftConversionStep
)
+ indirect case genericValueIndirectReturn(
+ NativeSwiftConversionStep,
+ swiftFunctionResultType: SwiftType,
+ outArgumentName: String
+ )
+
indirect case method(
NativeSwiftConversionStep,
function: String,
@@ -1064,7 +1082,7 @@ extension JNISwift2JavaGenerator {
}
return pointerName
- case .genericMetadataPointer(let inner):
+ case .extractMetatypeValue(let inner):
let inner = inner.render(&printer, placeholder)
let pointerName = "\(inner)$"
printer.print(
@@ -1260,6 +1278,18 @@ extension JNISwift2JavaGenerator {
return "result$"
+ case .genericValueIndirectReturn(let inner, let swiftFunctionResultType, let outArgumentName):
+ let inner = inner.render(&printer, placeholder)
+ printer.print(
+ """
+ environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache.OutSwiftGenericInstance.selfPointer, \(inner))
+ let metadataPointer = unsafeBitCast(\(swiftFunctionResultType).self, to: UnsafeRawPointer.self)
+ let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer))
+ environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache.OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment))
+ """
+ )
+ return ""
+
case .method(let inner, let methodName, let arguments):
let inner = inner.render(&printer, placeholder)
let args = arguments.map { name, value in
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
index b91264a5..5ce21236 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
@@ -314,7 +314,7 @@ extension JNISwift2JavaGenerator {
}
printer.print("let erasedSelfType$: Any.Type = unsafeBitCast(selfTypeMetadataPointer$, to: Any.Type.self)")
printer.print("let openerType = erasedSelfType$ as! (any \(openerProtocolName(for: type.swiftNominal)).Type)")
- printer.print("return openerType._toString(selfBits$: selfBits$).getJNIValue(in: environment)")
+ printer.print("return openerType._toString(environment: environment, thisClass: thisClass, self: self).getJNIValue(in: environment)")
} else {
let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam)
printer.print("return String(describing: \(selfVar).pointee).getJNIValue(in: environment)")
@@ -342,7 +342,7 @@ extension JNISwift2JavaGenerator {
}
printer.print("let erasedSelfType$: Any.Type = unsafeBitCast(selfTypeMetadataPointer$, to: Any.Type.self)")
printer.print("let openerType = erasedSelfType$ as! (any \(openerProtocolName(for: type.swiftNominal)).Type)")
- printer.print("return openerType._toDebugString(selfBits$: selfBits$).getJNIValue(in: environment)")
+ printer.print("return openerType._toDebugString(environment: environment, thisClass: thisClass, self: self).getJNIValue(in: environment)")
} else {
let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam)
printer.print("return String(reflecting: \(selfVar).pointee).getJNIValue(in: environment)")
@@ -922,9 +922,9 @@ extension JNISwift2JavaGenerator {
}
let nativeSignature = translatedDecl.nativeFunctionSignature
- let selfTypePointer = nativeSignature.selfTypeParameter!.conversion.render(&printer, "selfType")
+ let selfType = nativeSignature.selfTypeParameter!.conversion.render(&printer, "selfType")
let openerName = openerProtocolName(for: parentNominalType.nominalTypeDecl)
- printer.print("let openerType = \(selfTypePointer) as! (any \(openerName).Type)")
+ printer.print("let openerType = \(selfType) as! (any \(openerName).Type)")
var parameters = nativeSignature.parameters.flatMap(\.parameters)
if let selfParameter = nativeSignature.selfParameter {
diff --git a/Sources/JavaTypes/JavaType.swift b/Sources/JavaTypes/JavaType.swift
index 803952bf..8f7a3eac 100644
--- a/Sources/JavaTypes/JavaType.swift
+++ b/Sources/JavaTypes/JavaType.swift
@@ -80,3 +80,9 @@ extension JavaType {
}
}
}
+
+extension JavaType {
+ public static var OutSwiftGenericInstance: JavaType {
+ .class(package: "org.swift.swiftkit.core", name: "OutSwiftGenericInstance")
+ }
+}
diff --git a/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift b/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift
index 7be79a9f..402d953d 100644
--- a/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift
+++ b/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift
@@ -120,4 +120,33 @@ extension _JNIMethodIDCache {
cache.methods[typeMetadataAddressMethod]!
}
}
+
+ public enum OutSwiftGenericInstance {
+ private static let selfPointerField = Field(
+ name: "selfPointer",
+ signature: "J"
+ )
+
+ private static let selfTypePointerField = Field(
+ name: "selfTypePointer",
+ signature: "J"
+ )
+
+ private static let cache = _JNIMethodIDCache(
+ className: "org/swift/swiftkit/core/OutSwiftGenericInstance",
+ fields: [selfPointerField, selfTypePointerField]
+ )
+
+ public static var `class`: jclass {
+ cache.javaClass
+ }
+
+ public static var selfPointer: jfieldID {
+ cache.fields[selfPointerField]!
+ }
+
+ public static var selfTypePointer: jfieldID {
+ cache.fields[selfTypePointerField]!
+ }
+ }
}
diff --git a/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift b/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift
index e5ae1a6f..fcb1e3c5 100644
--- a/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift
+++ b/Sources/SwiftJavaRuntimeSupport/_JNIMethodIDCache.swift
@@ -32,8 +32,21 @@ public final class _JNIMethodIDCache: Sendable {
}
}
+ public struct Field: Hashable {
+ public let name: String
+ public let signature: String
+ public let isStatic: Bool
+
+ public init(name: String, signature: String, isStatic: Bool = false) {
+ self.name = name
+ self.signature = signature
+ self.isStatic = isStatic
+ }
+ }
+
nonisolated(unsafe) let _class: jclass?
nonisolated(unsafe) let methods: [Method: jmethodID]
+ nonisolated(unsafe) let fields: [Field: jfieldID]
public var javaClass: jclass {
self._class!
@@ -44,7 +57,7 @@ public final class _JNIMethodIDCache: Sendable {
/// This is to make sure that the underlying reference remains valid
nonisolated(unsafe) private let javaObjectHolder: JavaObjectHolder?
- public init(className: String, methods: [Method]) {
+ public init(className: String, methods: [Method] = [], fields: [Field] = []) {
let environment = try! JavaVirtualMachine.shared().environment()
let clazz: jobject
@@ -89,12 +102,33 @@ public final class _JNIMethodIDCache: Sendable {
}
}
}
+ self.fields = fields.reduce(into: [:]) { (result, field) in
+ if field.isStatic {
+ if let fieldID = environment.interface.GetStaticFieldID(environment, clazz, field.name, field.signature) {
+ result[field] = fieldID
+ } else {
+ fatalError(
+ "Static field \(field.signature) with signature \(field.signature) not found in class \(className)"
+ )
+ }
+ } else {
+ if let fieldID = environment.interface.GetFieldID(environment, clazz, field.name, field.signature) {
+ result[field] = fieldID
+ } else {
+ fatalError("field \(field.signature) with signature \(field.signature) not found in class \(className)")
+ }
+ }
+ }
}
public subscript(_ method: Method) -> jmethodID? {
methods[method]
}
+ public subscript(_ field: Field) -> jfieldID? {
+ fields[field]
+ }
+
public func cleanup(environment: UnsafeMutablePointer!) {
environment.interface.DeleteGlobalRef(environment, self._class)
}
diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
new file mode 100644
index 00000000..68989cad
--- /dev/null
+++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// 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;
+
+public final class OutSwiftGenericInstance {
+ public long selfPointer;
+ public long selfTypePointer;
+}
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index 4a5d291c..8ad7cb75 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -46,6 +46,18 @@ struct JNIGenericTypeTests {
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) {
+ return new MyID(selfPointer, selfTypePointer, SwiftMemoryManagement.DEFAULT_SWIFT_JAVA_AUTO_ARENA);
+ }
+ """,
+ """
private final long selfTypePointer;
""",
"""
@@ -55,12 +67,6 @@ struct JNIGenericTypeTests {
private static native java.lang.String $getDescription(long self, long selfType);
""",
"""
- public String toString() {
- return $toString(this.$memoryAddress(), this.$typeMetadataAddress());
- }
- private static native java.lang.String $toString(long selfPointer, long selfType);
- """,
- """
@Override
public long $typeMetadataAddress() {
return this.selfTypePointer;
@@ -112,4 +118,51 @@ struct JNIGenericTypeTests {
]
)
}
+
+ @Test
+ func returnsGenericValueFuncJava() throws {
+ try assertOutput(
+ input: genericFile,
+ .jni,
+ .java,
+ detectChunkByInitialLines: 2,
+ expectedChunks: [
+ """
+ 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$);
+ }
+ """,
+ """
+ private static native void $makeStringID(java.lang.String value, org.swift.swiftkit.core.OutSwiftGenericInstance out);
+ """
+ ]
+ )
+ }
+
+ @Test
+ func returnsGenericValueFuncSwift() throws {
+ try assertOutput(
+ input: genericFile,
+ .jni,
+ .swift,
+ detectChunkByInitialLines: 2,
+ expectedChunks: [
+ """
+ @_cdecl("Java_com_example_swift_SwiftModule__00024makeStringID__Ljava_lang_String_2Lorg_swift_swiftkit_core_OutSwiftGenericInstance_2")
+ public func Java_com_example_swift_SwiftModule__00024makeStringID__Ljava_lang_String_2Lorg_swift_swiftkit_core_OutSwiftGenericInstance_2(environment: UnsafeMutablePointer!, thisClass: jclass, value: jstring?, out: jobject?) {
+ let result$ = UnsafeMutablePointer>.allocate(capacity: 1)
+ result$.initialize(to: SwiftModule.makeStringID(String(fromJNI: value, in: environment)))
+ let resultBits$ = Int64(Int(bitPattern: result$))
+ environment.interface.SetLongField(environment, out, _JNIMethodIDCache.OutSwiftGenericInstance.selfPointer, resultBits$.getJNIValue(in: environment))
+ let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self)
+ let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer))
+ environment.interface.SetLongField(environment, out, _JNIMethodIDCache.OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment))
+ return
+ }
+ """
+ ]
+ )
+ }
}
From a120d0b87b3d275b3c4a97fb70c8fc7442ecbc14 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Thu, 19 Feb 2026 15:24:44 +0900
Subject: [PATCH 09/25] Skip generating typeMetadataAddressDowncall if the type
is generic
---
.../JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
index 5ce21236..7d79bdd6 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
@@ -811,6 +811,10 @@ extension JNISwift2JavaGenerator {
}
private func printTypeMetadataAddressThunk(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
+ if type.swiftNominal.isGeneric {
+ return
+ }
+
printCDecl(
&printer,
javaMethodName: "$typeMetadataAddressDowncall",
From 0231d72f6c43957ef862a22e8b65031b9373ebd2 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Thu, 19 Feb 2026 16:48:42 +0900
Subject: [PATCH 10/25] Handling the destroy function
---
.../com/example/swift/GenericTypeTest.java | 33 +++++++
...t2JavaGenerator+JavaBindingsPrinting.swift | 22 +++--
...ift2JavaGenerator+SwiftThunkPrinting.swift | 98 +++++++++++++------
.../JNI/JNIGenericTypeTests.swift | 22 +++++
4 files changed, 139 insertions(+), 36 deletions(-)
create mode 100644 Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java
diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java
new file mode 100644
index 00000000..d38ef934
--- /dev/null
+++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 com.example.swift;
+
+import org.junit.jupiter.api.Test;
+import org.swift.swiftkit.core.SwiftArena;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class GenericTypeTest {
+ @Test
+ void returnsGenericType() {
+ try (var arena = SwiftArena.ofConfined()) {
+ MyID stringId = MySwiftLibrary.makeStringID("Java", arena);
+ assertEquals("Java", stringId.getDescription());
+
+ MyID intId = MySwiftLibrary.makeIntID(42, arena);
+ assertEquals("42", intId.getDescription());
+ }
+ }
+}
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
index 20e5e329..99578606 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
@@ -496,16 +496,16 @@ extension JNISwift2JavaGenerator {
_ decl: ImportedFunc,
skipMethodBody: Bool = false
) {
- guard let translatedDecl = self.translatedDecl(for: decl) else {
+ guard translatedDecl(for: decl) != nil else {
// Failed to translate. Skip.
return
}
printer.printSeparator(decl.displayName)
- printJavaBindingWrapperHelperClass(&printer, translatedDecl)
+ printJavaBindingWrapperHelperClass(&printer, decl)
- printJavaBindingWrapperMethod(&printer, translatedDecl, skipMethodBody: skipMethodBody)
+ printJavaBindingWrapperMethod(&printer, decl, skipMethodBody: skipMethodBody)
}
/// Print the helper type container for a user-facing Java API.
@@ -513,8 +513,9 @@ extension JNISwift2JavaGenerator {
/// * User-facing functional interfaces.
private func printJavaBindingWrapperHelperClass(
_ printer: inout CodePrinter,
- _ translated: TranslatedFunctionDecl
+ _ decl: ImportedFunc
) {
+ let translated = self.translatedDecl(for: decl)!
if translated.functionTypes.isEmpty {
return
}
@@ -751,11 +752,20 @@ extension JNISwift2JavaGenerator {
/// Prints the destroy function for a `JNISwiftInstance`
private func printDestroyFunction(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
- printer.print("private static native void $destroy(long selfPointer);")
+ let isGeneric = type.swiftNominal.isGeneric
+ if isGeneric {
+ printer.print("private static native void $destroy(long self, long selfType);")
+ } else {
+ printer.print("private static native void $destroy(long self);")
+ }
let funcName = "$createDestroyFunction"
printer.print("@Override")
printer.printBraceBlock("public Runnable \(funcName)()") { printer in
+ if isGeneric {
+ printer.print("long selfType$ = this.$typeMetadataAddress();")
+ }
+ let destroyArg = isGeneric ? "self$, selfType$" : "self$"
printer.print(
"""
long self$ = this.$memoryAddress();
@@ -770,7 +780,7 @@ extension JNISwift2JavaGenerator {
if (CallTraces.TRACE_DOWNCALLS) {
CallTraces.traceDowncall("\(type.swiftNominal.name).$destroy", "self", self$);
}
- \(type.swiftNominal.name).$destroy(self$);
+ \(type.swiftNominal.name).$destroy(\(destroyArg));
}
};
"""
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
index 7d79bdd6..e0b7c35c 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
@@ -20,6 +20,8 @@ import FoundationEssentials
import Foundation
#endif
+import SwiftSyntax
+
extension JNISwift2JavaGenerator {
func writeSwiftThunkSources() throws {
var printer = CodePrinter()
@@ -294,18 +296,21 @@ extension JNISwift2JavaGenerator {
private func printToStringMethods(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
let selfPointerParam = JavaParameter(name: "selfPointer", type: .long)
let parentName = type.qualifiedName
+ let isGeneric = type.swiftNominal.isGeneric
+
+ var parameters = [selfPointerParam]
+ if isGeneric {
+ parameters.append(JavaParameter(name: "selfType", type: .long))
+ }
printCDecl(
&printer,
javaMethodName: "$toString",
parentName: type.swiftNominal.qualifiedName,
- parameters: [
- selfPointerParam,
- JavaParameter(name: "selfType", type: .long)
- ],
+ parameters: parameters,
resultType: .javaLangString
) { printer in
- if type.swiftNominal.isGeneric {
+ if isGeneric {
printer.print("let selfBits$ = Int(Int64(fromJNI: selfPointer, in: environment))")
printer.printBraceBlock(
"guard let selfTypeMetadataPointer$ = UnsafeRawPointer(bitPattern: Int(Int64(fromJNI: selfType, in: environment))) else"
@@ -314,7 +319,7 @@ extension JNISwift2JavaGenerator {
}
printer.print("let erasedSelfType$: Any.Type = unsafeBitCast(selfTypeMetadataPointer$, to: Any.Type.self)")
printer.print("let openerType = erasedSelfType$ as! (any \(openerProtocolName(for: type.swiftNominal)).Type)")
- printer.print("return openerType._toString(environment: environment, thisClass: thisClass, self: self).getJNIValue(in: environment)")
+ printer.print("return openerType._toString(environment: environment, thisClass: thisClass, self: selfPointer)")
} else {
let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam)
printer.print("return String(describing: \(selfVar).pointee).getJNIValue(in: environment)")
@@ -327,13 +332,10 @@ extension JNISwift2JavaGenerator {
&printer,
javaMethodName: "$toDebugString",
parentName: type.swiftNominal.qualifiedName,
- parameters: [
- selfPointerParam,
- JavaParameter(name: "selfType", type: .long)
- ],
+ parameters: parameters,
resultType: .javaLangString
) { printer in
- if type.swiftNominal.isGeneric {
+ if isGeneric {
printer.print("let selfBits$ = Int(Int64(fromJNI: selfPointer, in: environment))")
printer.printBraceBlock(
"guard let selfTypeMetadataPointer$ = UnsafeRawPointer(bitPattern: Int(Int64(fromJNI: selfType, in: environment))) else"
@@ -342,7 +344,7 @@ extension JNISwift2JavaGenerator {
}
printer.print("let erasedSelfType$: Any.Type = unsafeBitCast(selfTypeMetadataPointer$, to: Any.Type.self)")
printer.print("let openerType = erasedSelfType$ as! (any \(openerProtocolName(for: type.swiftNominal)).Type)")
- printer.print("return openerType._toDebugString(environment: environment, thisClass: thisClass, self: self).getJNIValue(in: environment)")
+ printer.print("return openerType._toDebugString(environment: environment, thisClass: thisClass, self: selfPointer)")
} else {
let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam)
printer.print("return String(reflecting: \(selfVar).pointee).getJNIValue(in: environment)")
@@ -833,26 +835,50 @@ extension JNISwift2JavaGenerator {
/// Prints the implementation of the destroy function.
private func printDestroyFunctionThunk(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
- let selfPointerParam = JavaParameter(name: "selfPointer", type: .long)
+ let selfPointerParam = JavaParameter(name: "self", type: .long)
+ var parameters = [selfPointerParam]
+ if type.swiftNominal.isGeneric {
+ parameters.append(JavaParameter(name: "selfType", type: .long))
+ }
+
printCDecl(
&printer,
javaMethodName: "$destroy",
parentName: type.swiftNominal.qualifiedName,
- parameters: [
- selfPointerParam
- ],
+ parameters: parameters,
resultType: .void
) { printer in
- let parentName = type.qualifiedName
- let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam)
- // Deinitialize the pointer allocated (which will call the VWT destroy method)
- // then deallocate the memory.
- printer.print(
- """
- \(selfVar).deinitialize(count: 1)
- \(selfVar).deallocate()
- """
- )
+ if type.swiftNominal.isGeneric {
+ let destroyFunctionSignature = SwiftFunctionSignature(
+ selfParameter: .instance(SwiftParameter(convention: .byValue, parameterName: "self", type: type.swiftType)),
+ parameters: [],
+ result: .void,
+ effectSpecifiers: [],
+ genericParameters: [],
+ genericRequirements: []
+ )
+ printFunctionOpenerCall(
+ &printer,
+ .init(
+ module: swiftModuleName,
+ swiftDecl: DeclSyntax("func destory()"),
+ name: "destroy",
+ apiKind: .function,
+ functionSignature: destroyFunctionSignature
+ )
+ )
+ } else {
+ let parentName = type.qualifiedName
+ let selfVar = self.printSelfJLongToUnsafeMutablePointer(&printer, swiftParentName: parentName, selfPointerParam)
+ // Deinitialize the pointer allocated (which will call the VWT destroy method)
+ // then deallocate the memory.
+ printer.print(
+ """
+ \(selfVar).deinitialize(count: 1)
+ \(selfVar).deallocate()
+ """
+ )
+ }
}
}
@@ -1011,6 +1037,7 @@ extension JNISwift2JavaGenerator {
printer.print("static func _toString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring?")
printer.print("static func _toDebugString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring?")
+ printer.print("static func _destroy(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong)")
}
printer.println()
printer.printBraceBlock("extension \(type.swiftNominal.name): \(protocolName)") { printer in
@@ -1024,24 +1051,35 @@ extension JNISwift2JavaGenerator {
printFunctionDecl(&printer, decl: method, skipMethodBody: false)
}
- printer.printBraceBlock("static func _toString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> String") { printer in
+ printer.printBraceBlock("static func _toString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring?") { printer in
+ printer.print(#"assert(self != 0, "self memory address was null")"#)
+ printer.print("let selfBits$ = Int(Int64(fromJNI: self, in: environment))")
+ printer.print("let self$ = UnsafeMutablePointer(bitPattern: selfBits$)")
+ printer.printBraceBlock("guard let self$ else") { printer in
+ printer.print("fatalError(\"self memory address was null in call to \\(#function)!\")")
+ }
+ printer.print("return String(describing: self$.pointee).getJNIValue(in: environment)")
+ }
+
+ printer.printBraceBlock("static func _toDebugString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring?") { printer in
printer.print(#"assert(self != 0, "self memory address was null")"#)
printer.print("let selfBits$ = Int(Int64(fromJNI: self, in: environment))")
printer.print("let self$ = UnsafeMutablePointer(bitPattern: selfBits$)")
printer.printBraceBlock("guard let self$ else") { printer in
printer.print("fatalError(\"self memory address was null in call to \\(#function)!\")")
}
- printer.print("return String(describing: self$.pointee)")
+ printer.print("return String(reflecting: self$.pointee).getJNIValue(in: environment)")
}
- printer.printBraceBlock("static func _toDebugString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> String") { printer in
+ printer.printBraceBlock("static func _destroy(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong)") { printer in
printer.print(#"assert(self != 0, "self memory address was null")"#)
printer.print("let selfBits$ = Int(Int64(fromJNI: self, in: environment))")
printer.print("let self$ = UnsafeMutablePointer(bitPattern: selfBits$)")
printer.printBraceBlock("guard let self$ else") { printer in
printer.print("fatalError(\"self memory address was null in call to \\(#function)!\")")
}
- printer.print("return String(reflecting: self$.pointee)")
+ printer.print("self$.deinitialize(count: 1)")
+ printer.print("self$.deallocate()")
}
}
}
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index 8ad7cb75..154c0859 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -72,6 +72,28 @@ struct JNIGenericTypeTests {
return this.selfTypePointer;
}
""",
+ """
+ private static native void $destroy(long self, long selfType);
+ @Override
+ public Runnable $createDestroyFunction() {
+ long selfType$ = this.$typeMetadataAddress();
+ long self$ = this.$memoryAddress();
+ if (CallTraces.TRACE_DOWNCALLS) {
+ CallTraces.traceDowncall("MyID.$createDestroyFunction",
+ "this", this,
+ "self", self$);
+ }
+ return new Runnable() {
+ @Override
+ public void run() {
+ if (CallTraces.TRACE_DOWNCALLS) {
+ CallTraces.traceDowncall("MyID.$destroy", "self", self$);
+ }
+ MyID.$destroy(self$, selfType$);
+ }
+ };
+ }
+ """
]
)
}
From d7253d28e60044e9a7c2f3a729361af609eef990 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Fri, 20 Feb 2026 09:15:43 +0900
Subject: [PATCH 11/25] A bit refactoring
---
...wift2JavaGenerator+NativeTranslation.swift | 54 ++++++++++---------
.../SwiftTypes/SwiftFunctionSignature.swift | 9 ++++
2 files changed, 39 insertions(+), 24 deletions(-)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
index b9b7ae0b..2c990505 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
@@ -61,26 +61,21 @@ extension JNISwift2JavaGenerator {
nil
}
- func translateSelfTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> NativeParameter? {
- if decl.isGeneric {
- return NativeParameter(
- parameters: [
- JavaParameter(name: "selfType", type: .long)
- ],
- conversion: .extractMetatypeValue(.placeholder),
- indirectConversion: nil,
- conversionCheck: nil
+ let selfTypeParameter: NativeParameter? =
+ if let selfType = functionSignature.selfParameter?.selfType,
+ selfType.asNominalTypeDeclaration?.isGeneric == true
+ {
+ try translateParameter(
+ type: .metatype(selfType),
+ parameterName: "selfType",
+ methodName: methodName,
+ parentName: parentName,
+ genericParameters: functionSignature.genericParameters,
+ genericRequirements: functionSignature.genericRequirements
)
+ } else {
+ nil
}
- return nil
- }
- let selfTypeParameter: NativeParameter? = switch functionSignature.selfParameter {
- case .instance(let selfParameter):
- selfParameter.type.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
- case .initializer(let swiftType), .staticMethod(let swiftType):
- swiftType.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
- case nil: nil
- }
let result = try translate(swiftResult: functionSignature.result)
@@ -311,7 +306,17 @@ extension JNISwift2JavaGenerator {
parameterName: parameterName
)
- case .metatype, .tuple, .composite:
+ case .metatype:
+ return NativeParameter(
+ parameters: [
+ JavaParameter(name: parameterName, type: .long)
+ ],
+ conversion: .extractMetatypeValue(.placeholder),
+ indirectConversion: nil,
+ conversionCheck: nil
+ )
+
+ case .tuple, .composite:
throw JavaTranslationError.unsupportedSwiftType(type)
}
}
@@ -1065,11 +1070,12 @@ extension JNISwift2JavaGenerator {
} else {
printer.print("let \(inner)Bits$ = Int(\(inner))")
}
- let typeName = if swiftType.asNominalTypeDeclaration?.isGeneric == true {
- "Self"
- } else {
- swiftType.description
- }
+ let typeName =
+ if swiftType.asNominalTypeDeclaration?.isGeneric == true {
+ "Self"
+ } else {
+ swiftType.description
+ }
printer.print("let \(pointerName) = UnsafeMutablePointer<\(typeName)>(bitPattern: \(inner)Bits$)")
if !allowNil {
printer.print(
diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift
index 2dab3b96..f4be77f8 100644
--- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift
+++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift
@@ -67,6 +67,15 @@ enum SwiftSelfParameter: Equatable {
/// 'self' is the type for a call to an initializer. We only need the type
/// to form the call.
case initializer(SwiftType)
+
+ var selfType: SwiftType {
+ switch self {
+ case .instance(let swiftParameter):
+ return swiftParameter.type
+ case .staticMethod(let swiftType), .initializer(let swiftType):
+ return swiftType
+ }
+ }
}
extension SwiftFunctionSignature {
From 7a5a7c908cc5462c6c7ca9e0b343063796f2eec6 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Fri, 20 Feb 2026 09:16:56 +0900
Subject: [PATCH 12/25] apply formatter
---
.../Sources/MySwiftLibrary/GenericType.swift | 6 +--
...t2JavaGenerator+JavaBindingsPrinting.swift | 26 ++++++-------
...ISwift2JavaGenerator+JavaTranslation.swift | 37 ++++++++++--------
...ift2JavaGenerator+SwiftThunkPrinting.swift | 38 ++++++++++---------
.../SwiftNominalTypeDeclaration.swift | 9 +++--
.../JNI/JNIGenericTypeTests.swift | 10 ++---
6 files changed, 68 insertions(+), 58 deletions(-)
diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
index a8444637..c8edcb1c 100644
--- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
+++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
@@ -15,7 +15,7 @@
public struct MyID {
public var rawValue: T
public init(_ rawValue: T) {
- self.rawValue = rawValue
+ self.rawValue = rawValue
}
public var description: String {
"\(rawValue)"
@@ -23,11 +23,11 @@ public struct MyID {
}
public func makeIntID(_ value: Int) -> MyID {
- return MyID(value)
+ MyID(value)
}
public func makeStringID(_ value: String) -> MyID {
- return MyID(value)
+ MyID(value)
}
public struct MyEntity {
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
index 99578606..257ab387 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
@@ -209,10 +209,10 @@ extension JNISwift2JavaGenerator {
printer.printBraceBlock("private \(decl.swiftNominal.name)(\(swiftPointerArg), SwiftArena swiftArena)") { printer in
for param in swiftPointerParams {
printer.print(
- """
- SwiftObjects.requireNonZero(\(param), "\(param)");
- this.\(param) = \(param);
- """
+ """
+ SwiftObjects.requireNonZero(\(param), "\(param)");
+ this.\(param) = \(param);
+ """
)
}
printer.print(
@@ -731,19 +731,19 @@ extension JNISwift2JavaGenerator {
}
} else {
printer.print("private static native long $typeMetadataAddressDowncall();")
-
+
let funcName = "$typeMetadataAddress"
printer.print("@Override")
printer.printBraceBlock("public long $typeMetadataAddress()") { printer in
printer.print(
- """
- long self$ = this.$memoryAddress();
- if (CallTraces.TRACE_DOWNCALLS) {
- CallTraces.traceDowncall("\(type.swiftNominal.name).\(funcName)",
- "this", this,
- "self", self$);
- }
- """
+ """
+ long self$ = this.$memoryAddress();
+ if (CallTraces.TRACE_DOWNCALLS) {
+ CallTraces.traceDowncall("\(type.swiftNominal.name).\(funcName)",
+ "this", this,
+ "self", self$);
+ }
+ """
)
printer.print("return \(type.swiftNominal.name).$typeMetadataAddressDowncall();")
}
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
index 39e7abda..fa132386 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
@@ -332,13 +332,14 @@ extension JNISwift2JavaGenerator {
}
return nil
}
- let selfTypeParameter: TranslatedParameter? = switch functionSignature.selfParameter {
- case .instance(let selfParameter):
- selfParameter.type.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
- case .initializer(let swiftType), .staticMethod(let swiftType):
- swiftType.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
- case nil: nil
- }
+ let selfTypeParameter: TranslatedParameter? =
+ switch functionSignature.selfParameter {
+ case .instance(let selfParameter):
+ selfParameter.type.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
+ case .initializer(let swiftType), .staticMethod(let swiftType):
+ swiftType.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
+ case nil: nil
+ }
var exceptions: [JavaExceptionType] = []
@@ -798,14 +799,20 @@ extension JNISwift2JavaGenerator {
return TranslatedResult(
javaType: javaType,
annotations: resultAnnotations,
- outParameters: [.init(name: "instance", type: .OutSwiftGenericInstance, allocation: .new) ],
- conversion: .aggregate(variable: nil, [
- .print(.placeholder),
- .wrapMemoryAddressUnsafe(.commaSeparated([
- .member(.constant("instance"), field: "selfPointer"),
- .member(.constant("instance"), field: "selfTypePointer"),
- ]), javaType)
- ])
+ outParameters: [.init(name: "instance", type: .OutSwiftGenericInstance, allocation: .new)],
+ conversion: .aggregate(
+ variable: nil,
+ [
+ .print(.placeholder),
+ .wrapMemoryAddressUnsafe(
+ .commaSeparated([
+ .member(.constant("instance"), field: "selfPointer"),
+ .member(.constant("instance"), field: "selfTypePointer"),
+ ]),
+ javaType
+ ),
+ ]
+ )
)
} else {
return TranslatedResult(
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
index e0b7c35c..fafe11a7 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
import JavaTypes
+import SwiftSyntax
#if canImport(FoundationEssentials)
import FoundationEssentials
@@ -20,7 +21,6 @@ import FoundationEssentials
import Foundation
#endif
-import SwiftSyntax
extension JNISwift2JavaGenerator {
func writeSwiftThunkSources() throws {
@@ -816,7 +816,7 @@ extension JNISwift2JavaGenerator {
if type.swiftNominal.isGeneric {
return
}
-
+
printCDecl(
&printer,
javaMethodName: "$typeMetadataAddressDowncall",
@@ -963,12 +963,13 @@ extension JNISwift2JavaGenerator {
parameters += nativeSignature.result.outParameters
let openerArguments =
- [
- "environment: environment",
- "thisClass: thisClass",
- ] + parameters.map { javaParameter in
- "\(javaParameter.name): \(javaParameter.name)"
- }
+ [
+ "environment: environment",
+ "thisClass: thisClass",
+ ]
+ + parameters.map { javaParameter in
+ "\(javaParameter.name): \(javaParameter.name)"
+ }
let call = "openerType.\(decl.openerMethodName)(\(openerArguments.joined(separator: ", ")))"
if !decl.functionSignature.result.type.isVoid {
@@ -1000,12 +1001,12 @@ extension JNISwift2JavaGenerator {
let translatedParameters = parameters.map {
"\($0.name): \($0.type.jniTypeName)"
}
-
+
let thunkParameters =
- [
- "environment: UnsafeMutablePointer!",
- "thisClass: jclass",
- ] + translatedParameters
+ [
+ "environment: UnsafeMutablePointer!",
+ "thisClass: jclass",
+ ] + translatedParameters
let thunkReturnType = resultType != .void ? " -> \(resultType.jniTypeName)" : ""
let signature = #"static func \#(decl.openerMethodName)(\#(thunkParameters.joined(separator: ", ")))\#(thunkReturnType)"#
@@ -1152,11 +1153,12 @@ extension SwiftNominalTypeDeclaration {
extension ImportedFunc {
fileprivate var openerMethodName: String {
- let prefix = switch apiKind {
- case .getter: "_get_"
- case .setter: "_set_"
- default: "_"
- }
+ let prefix =
+ switch apiKind {
+ case .getter: "_get_"
+ case .setter: "_set_"
+ default: "_"
+ }
return "\(prefix)\(name)"
}
}
diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift
index 45d305d8..78ae5289 100644
--- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift
+++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift
@@ -92,9 +92,10 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration {
) {
self.parent = parent
self.syntax = node
- self.genericParameters = node.asProtocol(WithGenericParametersSyntax.self)?.genericParameterClause?.parameters.map {
- SwiftGenericParameterDeclaration(sourceFilePath: sourceFilePath, moduleName: moduleName, node: $0)
- } ?? []
+ self.genericParameters =
+ node.asProtocol(WithGenericParametersSyntax.self)?.genericParameterClause?.parameters.map {
+ SwiftGenericParameterDeclaration(sourceFilePath: sourceFilePath, moduleName: moduleName, node: $0)
+ } ?? []
// Determine the kind from the syntax node.
switch Syntax(node).as(SyntaxEnum.self) {
@@ -164,7 +165,7 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration {
}
var isGeneric: Bool {
- return !genericParameters.isEmpty
+ !genericParameters.isEmpty
}
}
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index 154c0859..8c34c4c7 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -28,7 +28,7 @@ struct JNIGenericTypeTests {
"\(rawValue)"
}
}
-
+
public func makeStringID(_ value: String) -> MyID {
return MyID(value)
}
@@ -52,7 +52,7 @@ struct JNIGenericTypeTests {
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);
}
@@ -93,7 +93,7 @@ struct JNIGenericTypeTests {
}
};
}
- """
+ """,
]
)
}
@@ -136,7 +136,7 @@ struct JNIGenericTypeTests {
let openerType = unsafeBitCast(selfType$, to: Any.Type.self) as! (any _SwiftModule_MyID_opener.Type)
return openerType._get_description(environment: environment, thisClass: thisClass, self: self)
}
- """
+ """,
]
)
}
@@ -158,7 +158,7 @@ struct JNIGenericTypeTests {
""",
"""
private static native void $makeStringID(java.lang.String value, org.swift.swiftkit.core.OutSwiftGenericInstance out);
- """
+ """,
]
)
}
From e5f65f3253c87952e6aed81c7362121b3308c9ac Mon Sep 17 00:00:00 2001
From: Iceman
Date: Fri, 20 Feb 2026 10:45:33 +0900
Subject: [PATCH 13/25] Refactor translateSelfTypeParameter
---
...ISwift2JavaGenerator+JavaTranslation.swift | 65 ++++++++++++++-----
1 file changed, 47 insertions(+), 18 deletions(-)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
index fa132386..c492ec9f 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
@@ -323,23 +323,13 @@ extension JNISwift2JavaGenerator {
genericRequirements: functionSignature.genericRequirements
)
- func translateSelfTypeParameters(_ decl: SwiftNominalTypeDeclaration) -> TranslatedParameter? {
- if decl.isGeneric {
- return TranslatedParameter(
- parameter: JavaParameter(name: "selfType", type: .long),
- conversion: .typeMetadataAddress(.placeholder)
- )
- }
- return nil
- }
- let selfTypeParameter: TranslatedParameter? =
- switch functionSignature.selfParameter {
- case .instance(let selfParameter):
- selfParameter.type.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
- case .initializer(let swiftType), .staticMethod(let swiftType):
- swiftType.asNominalTypeDeclaration.flatMap(translateSelfTypeParameters)
- case nil: nil
- }
+ let selfTypeParameter = try self.translateSelfTypeParameter(
+ functionSignature.selfParameter,
+ methodName: methodName,
+ parentName: parentName,
+ genericParameters: functionSignature.genericParameters,
+ genericRequirements: functionSignature.genericRequirements
+ )
var exceptions: [JavaExceptionType] = []
@@ -402,6 +392,39 @@ extension JNISwift2JavaGenerator {
}
}
+ func translateSelfTypeParameter(
+ _ selfParameter: SwiftSelfParameter?,
+ methodName: String,
+ parentName: String,
+ genericParameters: [SwiftGenericParameterDeclaration],
+ genericRequirements: [SwiftGenericRequirement]
+ ) throws -> TranslatedParameter? {
+ guard let selfParameter else {
+ return nil
+ }
+
+ let isGeneric =
+ switch selfParameter {
+ case .instance(let selfParameter):
+ selfParameter.type.asNominalTypeDeclaration?.isGeneric == true
+ case .initializer(let swiftType), .staticMethod(let swiftType):
+ swiftType.asNominalTypeDeclaration?.isGeneric == true
+ }
+ if isGeneric {
+ return try self.translateParameter(
+ swiftType: .metatype(selfParameter.selfType),
+ parameterName: "selfType",
+ methodName: methodName,
+ parentName: parentName,
+ genericParameters: genericParameters,
+ genericRequirements: genericRequirements,
+ parameterPosition: nil
+ )
+ } else {
+ return nil
+ }
+ }
+
func translateParameter(
swiftType: SwiftType,
parameterName: String,
@@ -537,7 +560,13 @@ extension JNISwift2JavaGenerator {
parameterName: parameterName
)
- case .metatype, .tuple, .composite:
+ case .metatype:
+ return TranslatedParameter(
+ parameter: JavaParameter(name: parameterName, type: .long),
+ conversion: .typeMetadataAddress(.placeholder)
+ )
+
+ case .tuple, .composite:
throw JavaTranslationError.unsupportedSwiftType(swiftType)
}
}
From 686b34358cd2d17ca26bf4578e616b957611c14f Mon Sep 17 00:00:00 2001
From: Iceman
Date: Fri, 20 Feb 2026 11:20:24 +0900
Subject: [PATCH 14/25] Add example for generic type property
---
.../Sources/MySwiftLibrary/GenericType.swift | 4 ----
.../src/test/java/com/example/swift/GenericTypeTest.java | 9 +++++++++
.../JNI/JNISwift2JavaGenerator+NativeTranslation.swift | 8 +-------
3 files changed, 10 insertions(+), 11 deletions(-)
diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
index c8edcb1c..60d88c1b 100644
--- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
+++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
@@ -38,7 +38,3 @@ public struct MyEntity {
self.name = name
}
}
-
-public func takeMyEntity() -> MyEntity {
- return MyEntity(id: MyID(42), name: "Example")
-}
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 d38ef934..368ed5fa 100644
--- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java
+++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java
@@ -30,4 +30,13 @@ void returnsGenericType() {
assertEquals("42", intId.getDescription());
}
}
+
+ @Test
+ void genericTypeProperty() {
+ try (var arena = SwiftArena.ofConfined()) {
+ MyID intId = MySwiftLibrary.makeIntID(42, arena);
+ MyEntity entity = MyEntity.init(intId, "name", arena);
+ assertEquals("42", entity.getId(arena).getDescription());
+ }
+ }
}
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
index 2c990505..632d3b98 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
@@ -1070,13 +1070,7 @@ extension JNISwift2JavaGenerator {
} else {
printer.print("let \(inner)Bits$ = Int(\(inner))")
}
- let typeName =
- if swiftType.asNominalTypeDeclaration?.isGeneric == true {
- "Self"
- } else {
- swiftType.description
- }
- printer.print("let \(pointerName) = UnsafeMutablePointer<\(typeName)>(bitPattern: \(inner)Bits$)")
+ printer.print("let \(pointerName) = UnsafeMutablePointer<\(swiftType)>(bitPattern: \(inner)Bits$)")
if !allowNil {
printer.print(
"""
From 00373a7d8738e572be8aad5968e65cd10e87bdc6 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Fri, 20 Feb 2026 11:51:29 +0900
Subject: [PATCH 15/25] Fix some parameter name for test
---
...ISwift2JavaGenerator+JavaBindingsPrinting.swift | 4 ++--
...JNISwift2JavaGenerator+SwiftThunkPrinting.swift | 14 ++++++--------
.../JNI/JNIGenericTypeTests.swift | 4 ++--
3 files changed, 10 insertions(+), 12 deletions(-)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
index 257ab387..69908333 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
@@ -754,9 +754,9 @@ extension JNISwift2JavaGenerator {
private func printDestroyFunction(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
let isGeneric = type.swiftNominal.isGeneric
if isGeneric {
- printer.print("private static native void $destroy(long self, long selfType);")
+ printer.print("private static native void $destroy(long selfPointer, long selfType);")
} else {
- printer.print("private static native void $destroy(long self);")
+ printer.print("private static native void $destroy(long selfPointer);")
}
let funcName = "$createDestroyFunction"
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
index fafe11a7..c4c73788 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
@@ -311,7 +311,6 @@ extension JNISwift2JavaGenerator {
resultType: .javaLangString
) { printer in
if isGeneric {
- printer.print("let selfBits$ = Int(Int64(fromJNI: selfPointer, in: environment))")
printer.printBraceBlock(
"guard let selfTypeMetadataPointer$ = UnsafeRawPointer(bitPattern: Int(Int64(fromJNI: selfType, in: environment))) else"
) { printer in
@@ -336,7 +335,6 @@ extension JNISwift2JavaGenerator {
resultType: .javaLangString
) { printer in
if isGeneric {
- printer.print("let selfBits$ = Int(Int64(fromJNI: selfPointer, in: environment))")
printer.printBraceBlock(
"guard let selfTypeMetadataPointer$ = UnsafeRawPointer(bitPattern: Int(Int64(fromJNI: selfType, in: environment))) else"
) { printer in
@@ -835,7 +833,7 @@ extension JNISwift2JavaGenerator {
/// Prints the implementation of the destroy function.
private func printDestroyFunctionThunk(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
- let selfPointerParam = JavaParameter(name: "self", type: .long)
+ let selfPointerParam = JavaParameter(name: "selfPointer", type: .long)
var parameters = [selfPointerParam]
if type.swiftNominal.isGeneric {
parameters.append(JavaParameter(name: "selfType", type: .long))
@@ -850,7 +848,7 @@ extension JNISwift2JavaGenerator {
) { printer in
if type.swiftNominal.isGeneric {
let destroyFunctionSignature = SwiftFunctionSignature(
- selfParameter: .instance(SwiftParameter(convention: .byValue, parameterName: "self", type: type.swiftType)),
+ selfParameter: .instance(SwiftParameter(convention: .byValue, parameterName: "selfPointer", type: type.swiftType)),
parameters: [],
result: .void,
effectSpecifiers: [],
@@ -1038,7 +1036,7 @@ extension JNISwift2JavaGenerator {
printer.print("static func _toString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring?")
printer.print("static func _toDebugString(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring?")
- printer.print("static func _destroy(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong)")
+ printer.print("static func _destroy(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong)")
}
printer.println()
printer.printBraceBlock("extension \(type.swiftNominal.name): \(protocolName)") { printer in
@@ -1072,9 +1070,9 @@ extension JNISwift2JavaGenerator {
printer.print("return String(reflecting: self$.pointee).getJNIValue(in: environment)")
}
- printer.printBraceBlock("static func _destroy(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong)") { printer in
- printer.print(#"assert(self != 0, "self memory address was null")"#)
- printer.print("let selfBits$ = Int(Int64(fromJNI: self, in: environment))")
+ printer.printBraceBlock("static func _destroy(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong)") { printer in
+ printer.print(#"assert(selfPointer != 0, "self memory address was null")"#)
+ printer.print("let selfBits$ = Int(Int64(fromJNI: selfPointer, in: environment))")
printer.print("let self$ = UnsafeMutablePointer(bitPattern: selfBits$)")
printer.printBraceBlock("guard let self$ else") { printer in
printer.print("fatalError(\"self memory address was null in call to \\(#function)!\")")
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index 8c34c4c7..e46843c3 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -73,7 +73,7 @@ struct JNIGenericTypeTests {
}
""",
"""
- private static native void $destroy(long self, long selfType);
+ private static native void $destroy(long selfPointer, long selfType);
@Override
public Runnable $createDestroyFunction() {
long selfType$ = this.$typeMetadataAddress();
@@ -117,7 +117,7 @@ struct JNIGenericTypeTests {
static func _get_description(environment: UnsafeMutablePointer!, thisClass: jclass, self: jlong) -> jstring? {
assert(self != 0, "self memory address was null")
let selfBits$ = Int(Int64(fromJNI: self, in: environment))
- let self$ = UnsafeMutablePointer(bitPattern: selfBits$)
+ let self$ = UnsafeMutablePointer(bitPattern: selfBits$)
guard let self$ else {
fatalError("self memory address was null in call to \(#function)!")
}
From f4f27ccca27367ed027df877d76528e9d3d59b78 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Fri, 20 Feb 2026 12:52:11 +0900
Subject: [PATCH 16/25] Fix static method skipping way
---
...NISwift2JavaGenerator+SwiftThunkPrinting.swift | 8 --------
Sources/JExtractSwiftLib/Swift2JavaVisitor.swift | 15 +++++++++++++++
2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
index c4c73788..c3624475 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
@@ -1019,18 +1019,10 @@ extension JNISwift2JavaGenerator {
printer.printBraceBlock("protocol \(protocolName)") { printer in
for variable in type.variables {
- if variable.isStatic {
- logger.debug("Skipping \(variable.name), static variables in generic type is not yet supported.")
- continue
- }
printFunctionDecl(&printer, decl: variable, skipMethodBody: true)
}
for method in type.methods {
- if method.isStatic {
- logger.debug("Skipping \(method.name), static methods in generic type is not yet supported.")
- continue
- }
printFunctionDecl(&printer, decl: method, skipMethodBody: true)
}
diff --git a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift
index ff6295bb..44e9c66f 100644
--- a/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift
+++ b/Sources/JExtractSwiftLib/Swift2JavaVisitor.swift
@@ -160,6 +160,11 @@ final class Swift2JavaVisitor {
functionSignature: signature
)
+ if typeContext?.swiftNominal.isGeneric == true && imported.isStatic {
+ log.debug("Skip importing static function in generic type: '\(node.qualifiedNameForDebug)'")
+ return
+ }
+
log.debug("Record imported method \(node.qualifiedNameForDebug)")
if let typeContext {
typeContext.methods.append(imported)
@@ -266,6 +271,11 @@ final class Swift2JavaVisitor {
return
}
+ if typeContext.swiftNominal.isGeneric {
+ log.debug("Skip Importing generic type initializer \(node.kind) '\(node.qualifiedNameForDebug)'")
+ return
+ }
+
self.log.debug("Import initializer: \(node.kind) '\(node.qualifiedNameForDebug)'")
let signature: SwiftFunctionSignature
@@ -363,6 +373,11 @@ final class Swift2JavaVisitor {
functionSignature: signature
)
+ if typeContext?.swiftNominal.isGeneric == true && imported.isStatic {
+ log.debug("Skip importing static accessor in generic type: '\(node.qualifiedNameForDebug)'")
+ return
+ }
+
log.debug(
"Record imported variable accessor \(kind == .getter || kind == .subscriptGetter ? "getter" : "setter"):\(node.qualifiedNameForDebug)"
)
From b5856c3c8a65965e091bbf600490ecd3f8e7fbc8 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Tue, 24 Feb 2026 17:12:41 +0900
Subject: [PATCH 17/25] Fix typo
---
.../JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
index 27df86d2..489acb97 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
@@ -809,7 +809,7 @@ extension JNISwift2JavaGenerator {
&printer,
.init(
module: swiftModuleName,
- swiftDecl: DeclSyntax("func destory()"),
+ swiftDecl: DeclSyntax("func destroy()"),
name: "destroy",
apiKind: .function,
functionSignature: destroyFunctionSignature
From 66018b311138f90058b280ad1ffbfb00941f34ea Mon Sep 17 00:00:00 2001
From: Iceman
Date: Wed, 25 Feb 2026 10:38:01 +0900
Subject: [PATCH 18/25] Add enum support
---
.../Sources/MySwiftLibrary/GenericType.swift | 9 +++
.../com/example/swift/GenericTypeTest.java | 11 +++
...t2JavaGenerator+JavaBindingsPrinting.swift | 18 ++++-
...ISwift2JavaGenerator+JavaTranslation.swift | 18 ++++-
...ift2JavaGenerator+SwiftThunkPrinting.swift | 72 +++++++++++++++----
5 files changed, 111 insertions(+), 17 deletions(-)
diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
index 60d88c1b..02626671 100644
--- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
+++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
@@ -38,3 +38,12 @@ public struct MyEntity {
self.name = name
}
}
+
+public enum GenericEnum {
+ case foo
+ case bar
+}
+
+public func makeIntGenericEnum() -> GenericEnum {
+ if Bool.random() { return .foo } else { return .bar }
+}
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 368ed5fa..2a89d871 100644
--- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java
+++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java
@@ -39,4 +39,15 @@ void genericTypeProperty() {
assertEquals("42", entity.getId(arena).getDescription());
}
}
+
+ @Test
+ void genericEnum() {
+ try (var arena = SwiftArena.ofConfined()) {
+ GenericEnum value = MySwiftLibrary.makeIntGenericEnum(arena);
+ switch (value.getCase()) {
+ case GenericEnum.Foo _ -> assertTrue(value.getAsFoo().isPresent());
+ case GenericEnum.Bar _ -> assertTrue(value.getAsBar().isPresent());
+ }
+ }
+ }
}
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
index 62404e8c..c6d2d8fb 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
@@ -391,10 +391,17 @@ extension JNISwift2JavaGenerator {
}
// TODO: Consider whether all of these "utility" functions can be printed using our existing printing logic.
- printer.printBraceBlock("public Discriminator getDiscriminator()") { printer in
- printer.print("return Discriminator.values()[$getDiscriminator(this.$memoryAddress())];")
+ if decl.swiftNominal.isGeneric {
+ printer.printBraceBlock("public Discriminator getDiscriminator()") { printer in
+ printer.print("return Discriminator.values()[$getDiscriminator(this.$memoryAddress(), this.$typeMetadataAddress())];")
+ }
+ printer.print("private static native int $getDiscriminator(long self, long selfType);")
+ } else {
+ printer.printBraceBlock("public Discriminator getDiscriminator()") { printer in
+ printer.print("return Discriminator.values()[$getDiscriminator(this.$memoryAddress())];")
+ }
+ printer.print("private static native int $getDiscriminator(long self);")
}
- printer.print("private static native int $getDiscriminator(long self);")
}
private func printEnumCaseInterface(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
@@ -423,6 +430,11 @@ extension JNISwift2JavaGenerator {
}
private func printEnumStaticInitializers(_ printer: inout CodePrinter, _ decl: ImportedNominalType) {
+ if !decl.cases.isEmpty && decl.swiftNominal.isGeneric {
+ self.logger.debug("Skipping generic static initializers in '\(decl.swiftNominal.name)'")
+ return
+ }
+
for enumCase in decl.cases {
printFunctionDowncallMethods(&printer, enumCase.caseFunction)
}
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
index 9461d007..ae359520 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
@@ -138,6 +138,8 @@ extension JNISwift2JavaGenerator {
exceptions.append(.integerOverflow)
}
+ let isGenericParent = enumCase.caseFunction.parentType?.asNominalTypeDeclaration?.isGeneric == true
+
let getAsCaseFunction = TranslatedFunctionDecl(
name: getAsCaseName,
isStatic: false,
@@ -159,7 +161,12 @@ extension JNISwift2JavaGenerator {
]
)
),
- selfTypeParameter: nil, // TODO: iceman
+ selfTypeParameter: !isGenericParent
+ ? nil
+ : .init(
+ parameter: JavaParameter(name: "selfType", type: .long),
+ conversion: .typeMetadataAddress(.placeholder)
+ ),
parameters: [],
resultType: TranslatedResult(
javaType: .class(package: nil, name: "Optional<\(caseName)>"),
@@ -177,7 +184,14 @@ extension JNISwift2JavaGenerator {
indirectConversion: nil,
conversionCheck: nil
),
- selfTypeParameter: nil, // TODO: iceman
+ selfTypeParameter: !isGenericParent
+ ? nil
+ : .init(
+ parameters: [JavaParameter(name: "selfType", type: .long)],
+ conversion: .extractMetatypeValue(.placeholder),
+ indirectConversion: nil,
+ conversionCheck: nil
+ ),
parameters: [],
result: NativeResult(
javaType: nativeParametersType,
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
index 489acb97..6e750e28 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift
@@ -262,9 +262,11 @@ extension JNISwift2JavaGenerator {
printEnumDiscriminator(&printer, type)
printer.println()
- for enumCase in type.cases {
- printEnumCase(&printer, enumCase)
- printer.println()
+ if !type.swiftNominal.isGeneric {
+ for enumCase in type.cases {
+ printEnumCase(&printer, enumCase)
+ printer.println()
+ }
}
}
@@ -294,21 +296,48 @@ extension JNISwift2JavaGenerator {
private func printEnumDiscriminator(_ printer: inout CodePrinter, _ type: ImportedNominalType) {
let selfPointerParam = JavaParameter(name: "selfPointer", type: .long)
+ var parameters = [selfPointerParam]
+ if type.swiftNominal.isGeneric {
+ parameters.append(JavaParameter(name: "selfType", type: .long))
+ }
+
printCDecl(
&printer,
javaMethodName: "$getDiscriminator",
parentName: type.swiftNominal.name,
- parameters: [selfPointerParam],
+ parameters: parameters,
resultType: .int
) { printer in
- let selfPointer = self.printSelfJLongToUnsafeMutablePointer(
- &printer,
- swiftParentName: type.swiftNominal.name,
- selfPointerParam
- )
- printer.printBraceBlock("switch (\(selfPointer).pointee)") { printer in
- for (idx, enumCase) in type.cases.enumerated() {
- printer.print("case .\(enumCase.name): return \(idx)")
+ if type.swiftNominal.isGeneric {
+ let knownTypes = SwiftKnownTypes(symbolTable: lookupContext.symbolTable)
+ let discriminatorFunctionSignature = SwiftFunctionSignature(
+ selfParameter: .instance(SwiftParameter(convention: .byValue, parameterName: "selfPointer", type: type.swiftType)),
+ parameters: [],
+ result: .init(convention: .direct, type: knownTypes.int),
+ effectSpecifiers: [],
+ genericParameters: [],
+ genericRequirements: []
+ )
+ printFunctionOpenerCall(
+ &printer,
+ .init(
+ module: swiftModuleName,
+ swiftDecl: DeclSyntax("func getDiscriminator() -> Int"),
+ name: "getDiscriminator",
+ apiKind: .function,
+ functionSignature: discriminatorFunctionSignature
+ )
+ )
+ } else {
+ let selfPointer = self.printSelfJLongToUnsafeMutablePointer(
+ &printer,
+ swiftParentName: type.swiftNominal.name,
+ selfPointerParam
+ )
+ printer.printBraceBlock("switch (\(selfPointer).pointee)") { printer in
+ for (idx, enumCase) in type.cases.enumerated() {
+ printer.print("case .\(enumCase.name): return \(idx)")
+ }
}
}
}
@@ -976,6 +1005,10 @@ extension JNISwift2JavaGenerator {
printFunctionDecl(&printer, decl: method, skipMethodBody: true)
}
+ if type.swiftNominal.kind == .enum {
+ printer.print("static func _getDiscriminator(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jint")
+ }
+
printer.print("static func _destroy(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong)")
}
printer.println()
@@ -990,6 +1023,21 @@ extension JNISwift2JavaGenerator {
printFunctionDecl(&printer, decl: method, skipMethodBody: false)
}
+ if type.swiftNominal.kind == .enum {
+ printer.printBraceBlock("static func _getDiscriminator(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jint") { printer in
+ let selfPointer = self.printSelfJLongToUnsafeMutablePointer(
+ &printer,
+ swiftParentName: "Self",
+ JavaParameter(name: "selfPointer", type: .long)
+ )
+ printer.printBraceBlock("switch (\(selfPointer).pointee)") { printer in
+ for (idx, enumCase) in type.cases.enumerated() {
+ printer.print("case .\(enumCase.name): return \(idx)")
+ }
+ }
+ }
+ }
+
printer.printBraceBlock("static func _destroy(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong)") { printer in
printer.print(#"assert(selfPointer != 0, "self memory address was null")"#)
printer.print("let selfBits$ = Int(Int64(fromJNI: selfPointer, in: environment))")
From d4cd58ed598ac9068c48bd354a6ca000c4085b3d Mon Sep 17 00:00:00 2001
From: Iceman
Date: Wed, 25 Feb 2026 10:57:51 +0900
Subject: [PATCH 19/25] Update supported feature list
---
.../Documentation.docc/SupportedFeatures.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md
index 0789700e..61b8b3bf 100644
--- a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md
+++ b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md
@@ -60,6 +60,9 @@ SwiftJava's `swift-java jextract` tool automates generating Java bindings from S
| Async functions `func async` and properties: `var { get async {} }` | ❌ | ✅ |
| Arrays: `[UInt8]`, `[MyType]`, `Array` etc | ❌ | ✅ |
| Dictionaries: `[String: Int]`, `[K:V]` | ❌ | ❌ |
+| Generic type: `struct S` | ❌ | ✅ |
+| Functions or properties using generic type param: `struct S { func f(_: T) {} }` | ❌ | ❌ |
+| Static functions or properties in generic type | ❌ | ❌ |
| Generic parameters in functions: `func f(x: T)` | ❌ | ✅ |
| Generic return values in functions: `func f() -> T` | ❌ | ❌ |
| Tuples: `(Int, String)`, `(A, B, C)` | ❌ | ❌ |
From 27a7a9a481855f98ae9ef287435ada48af577432 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Wed, 25 Feb 2026 11:23:03 +0900
Subject: [PATCH 20/25] Fix copyright year
---
.../Sources/MySwiftLibrary/GenericType.swift | 2 +-
.../src/test/java/com/example/swift/GenericTypeTest.java | 2 +-
.../java/org/swift/swiftkit/core/OutSwiftGenericInstance.java | 2 +-
Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
index 02626671..4e0041fb 100644
--- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
+++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift
@@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
-// Copyright (c) 2025 Apple Inc. and the Swift.org project authors
+// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
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 2a89d871..fa0d76da 100644
--- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java
+++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java
@@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
-// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
+// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
index 68989cad..3e99a3a7 100644
--- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
+++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
@@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
-// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
+// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index e46843c3..1933cf5a 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -2,7 +2,7 @@
//
// This source file is part of the Swift.org open source project
//
-// Copyright (c) 2025 Apple Inc. and the Swift.org project authors
+// Copyright (c) 2026 Apple Inc. and the Swift.org project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
From 10f9f2a4074bef040723b381178c2e2e34f1885d Mon Sep 17 00:00:00 2001
From: Iceman
Date: Wed, 25 Feb 2026 14:38:38 +0900
Subject: [PATCH 21/25] includes selfType$ in CallTraces.traceDowncall
---
...t2JavaGenerator+JavaBindingsPrinting.swift | 59 ++++++++++++-------
.../JNI/JNIGenericTypeTests.swift | 7 ++-
2 files changed, 43 insertions(+), 23 deletions(-)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
index c6d2d8fb..ca2b1e6a 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift
@@ -740,29 +740,48 @@ extension JNISwift2JavaGenerator {
let funcName = "$createDestroyFunction"
printer.print("@Override")
printer.printBraceBlock("public Runnable \(funcName)()") { printer in
+ printer.print("long self$ = this.$memoryAddress();")
if isGeneric {
- printer.print("long selfType$ = this.$typeMetadataAddress();")
- }
- let destroyArg = isGeneric ? "self$, selfType$" : "self$"
- printer.print(
- """
- long self$ = this.$memoryAddress();
- if (CallTraces.TRACE_DOWNCALLS) {
- CallTraces.traceDowncall("\(type.swiftNominal.name).\(funcName)",
- "this", this,
- "self", self$);
- }
- return new Runnable() {
- @Override
- public void run() {
- if (CallTraces.TRACE_DOWNCALLS) {
- CallTraces.traceDowncall("\(type.swiftNominal.name).$destroy", "self", self$);
+ printer.print(
+ """
+ long selfType$ = this.$typeMetadataAddress();
+ if (CallTraces.TRACE_DOWNCALLS) {
+ CallTraces.traceDowncall("\(type.swiftNominal.name).\(funcName)",
+ "this", this,
+ "self", self$,
+ "selfType", selfType$);
+ }
+ return new Runnable() {
+ @Override
+ public void run() {
+ if (CallTraces.TRACE_DOWNCALLS) {
+ CallTraces.traceDowncall("\(type.swiftNominal.name).$destroy", "self", self$, "selfType", selfType$);
+ }
+ \(type.swiftNominal.name).$destroy(self$, selfType$);
}
- \(type.swiftNominal.name).$destroy(\(destroyArg));
+ };
+ """
+ )
+ } else {
+ printer.print(
+ """
+ if (CallTraces.TRACE_DOWNCALLS) {
+ CallTraces.traceDowncall("\(type.swiftNominal.name).\(funcName)",
+ "this", this,
+ "self", self$);
}
- };
- """
- )
+ return new Runnable() {
+ @Override
+ public void run() {
+ if (CallTraces.TRACE_DOWNCALLS) {
+ CallTraces.traceDowncall("\(type.swiftNominal.name).$destroy", "self", self$);
+ }
+ \(type.swiftNominal.name).$destroy(self$);
+ }
+ };
+ """
+ )
+ }
}
}
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index 1933cf5a..8bc4a0e0 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -76,18 +76,19 @@ struct JNIGenericTypeTests {
private static native void $destroy(long selfPointer, long selfType);
@Override
public Runnable $createDestroyFunction() {
- long selfType$ = this.$typeMetadataAddress();
long self$ = this.$memoryAddress();
+ long selfType$ = this.$typeMetadataAddress();
if (CallTraces.TRACE_DOWNCALLS) {
CallTraces.traceDowncall("MyID.$createDestroyFunction",
"this", this,
- "self", self$);
+ "self", self$,
+ "selfType", selfType$);
}
return new Runnable() {
@Override
public void run() {
if (CallTraces.TRACE_DOWNCALLS) {
- CallTraces.traceDowncall("MyID.$destroy", "self", self$);
+ CallTraces.traceDowncall("MyID.$destroy", "self", self$, "selfType", selfType$);
}
MyID.$destroy(self$, selfType$);
}
From 91bb797189a99f78135993b7e2d5fc6c82f459b2 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Wed, 25 Feb 2026 14:52:45 +0900
Subject: [PATCH 22/25] Add document to OutSwiftGenericInstance
---
.../org/swift/swiftkit/core/OutSwiftGenericInstance.java | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
index 3e99a3a7..018b5108 100644
--- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
+++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
@@ -14,6 +14,14 @@
package org.swift.swiftkit.core;
+/**
+ * A container for receiving Swift generic instances.
+ *
+ * This class acts as an "indirect return" receiver (out-parameter) for
+ * native calls that return Swift generic types. It pairs
+ * the object instance with its corresponding type metadata.
+ *
+ */
public final class OutSwiftGenericInstance {
public long selfPointer;
public long selfTypePointer;
From 016766b1e74b4dc33c2f4c4918e78aebce06099f Mon Sep 17 00:00:00 2001
From: Iceman
Date: Wed, 25 Feb 2026 15:08:52 +0900
Subject: [PATCH 23/25] Add detailed document about generic types
---
.../Documentation.docc/SupportedFeatures.md | 44 +++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md
index 61b8b3bf..ab8f2217 100644
--- a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md
+++ b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md
@@ -357,3 +357,47 @@ This is a mode for legacy platforms, where `CompletableFuture` is not available,
In this mode `async` functions in Swift are extracted as Java methods returning a `java.util.concurrent.Future`.
To enable this mode pass the `--async-func-mode future` command line option,
or set the `asyncFuncMode` configuration value in `swift-java.config`
+
+### Generic types
+
+> Note: Generic types are currently only supported in JNI mode.
+
+Support for generic types is limited.
+The generated Java classes do not have generic signatures.
+Any members containing type parameters (such as T) are not exported.
+
+```swift
+public struct MyID {
+ // Not exported: Contains the type parameter 'T'
+ public var rawValue: T
+
+ // Not exported: The initializer depends on 'T'
+ public init(rawValue: T) {
+ self.rawValue = rawValue
+ }
+
+ // Exported: Does not depend on 'T'
+ public var description: String { "\(rawValue)" }
+
+ // Not exported: Although it doesn't use 'T' directly,
+ // it is a member of a generic context (MyID.foo).
+ public static func foo() -> String { "" }
+}
+
+// Exported: A specialized function with a concrete type (MyID)
+public func makeIntID() -> MyID {
+ ...
+}
+```
+
+will be exported as
+
+```java
+public final class MyID implements JNISwiftInstance {
+ public String getDescription();
+}
+
+public final class MySwiftLibrary {
+ public static MyID makeIntID();
+}
+```
From d1a724773ec4cda3ab63edc17fd3fb56da97b645 Mon Sep 17 00:00:00 2001
From: Iceman
Date: Fri, 27 Feb 2026 17:20:57 +0900
Subject: [PATCH 24/25] rename: OutSwiftGenericInstance ->
_OutSwiftGenericInstance
---
.../JNI/JNISwift2JavaGenerator+JavaTranslation.swift | 2 +-
.../JNISwift2JavaGenerator+NativeTranslation.swift | 6 +++---
Sources/JavaTypes/JavaType.swift | 4 ++--
.../SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift | 4 ++--
...icInstance.java => _OutSwiftGenericInstance.java} | 2 +-
.../JExtractSwiftTests/JNI/JNIGenericTypeTests.swift | 12 ++++++------
6 files changed, 15 insertions(+), 15 deletions(-)
rename SwiftKitCore/src/main/java/org/swift/swiftkit/core/{OutSwiftGenericInstance.java => _OutSwiftGenericInstance.java} (94%)
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
index 525f4215..0ff525ae 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift
@@ -858,7 +858,7 @@ extension JNISwift2JavaGenerator {
return TranslatedResult(
javaType: javaType,
annotations: resultAnnotations,
- outParameters: [.init(name: "instance", type: .OutSwiftGenericInstance, allocation: .new)],
+ outParameters: [.init(name: "instance", type: ._OutSwiftGenericInstance, allocation: .new)],
conversion: .aggregate(
variable: nil,
[
diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
index 077d0800..1a023ae0 100644
--- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
+++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift
@@ -702,7 +702,7 @@ extension JNISwift2JavaGenerator {
swiftFunctionResultType: swiftResult.type,
outArgumentName: "out"
),
- outParameters: [.init(name: "out", type: .OutSwiftGenericInstance)]
+ outParameters: [.init(name: "out", type: ._OutSwiftGenericInstance)]
)
} else {
return NativeResult(
@@ -1321,10 +1321,10 @@ extension JNISwift2JavaGenerator {
let inner = inner.render(&printer, placeholder)
printer.print(
"""
- environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache.OutSwiftGenericInstance.selfPointer, \(inner))
+ environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, \(inner))
let metadataPointer = unsafeBitCast(\(swiftFunctionResultType).self, to: UnsafeRawPointer.self)
let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer))
- environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache.OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment))
+ environment.interface.SetLongField(environment, \(outArgumentName), _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment))
"""
)
return ""
diff --git a/Sources/JavaTypes/JavaType.swift b/Sources/JavaTypes/JavaType.swift
index 8f7a3eac..0e802355 100644
--- a/Sources/JavaTypes/JavaType.swift
+++ b/Sources/JavaTypes/JavaType.swift
@@ -82,7 +82,7 @@ extension JavaType {
}
extension JavaType {
- public static var OutSwiftGenericInstance: JavaType {
- .class(package: "org.swift.swiftkit.core", name: "OutSwiftGenericInstance")
+ public static var _OutSwiftGenericInstance: JavaType {
+ .class(package: "org.swift.swiftkit.core", name: "_OutSwiftGenericInstance")
}
}
diff --git a/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift b/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift
index 402d953d..7c851e8f 100644
--- a/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift
+++ b/Sources/SwiftJavaRuntimeSupport/JNIMethodIDCaches.swift
@@ -121,7 +121,7 @@ extension _JNIMethodIDCache {
}
}
- public enum OutSwiftGenericInstance {
+ public enum _OutSwiftGenericInstance {
private static let selfPointerField = Field(
name: "selfPointer",
signature: "J"
@@ -133,7 +133,7 @@ extension _JNIMethodIDCache {
)
private static let cache = _JNIMethodIDCache(
- className: "org/swift/swiftkit/core/OutSwiftGenericInstance",
+ className: "org/swift/swiftkit/core/_OutSwiftGenericInstance",
fields: [selfPointerField, selfTypePointerField]
)
diff --git a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/_OutSwiftGenericInstance.java
similarity index 94%
rename from SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
rename to SwiftKitCore/src/main/java/org/swift/swiftkit/core/_OutSwiftGenericInstance.java
index 018b5108..84d08269 100644
--- a/SwiftKitCore/src/main/java/org/swift/swiftkit/core/OutSwiftGenericInstance.java
+++ b/SwiftKitCore/src/main/java/org/swift/swiftkit/core/_OutSwiftGenericInstance.java
@@ -22,7 +22,7 @@
* the object instance with its corresponding type metadata.
*
*/
-public final class OutSwiftGenericInstance {
+public final class _OutSwiftGenericInstance {
public long selfPointer;
public long selfTypePointer;
}
diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
index 8bc4a0e0..cc9bdbdb 100644
--- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
+++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift
@@ -152,13 +152,13 @@ struct JNIGenericTypeTests {
expectedChunks: [
"""
public static MyID makeStringID(java.lang.String value, SwiftArena swiftArena$) {
- org.swift.swiftkit.core.OutSwiftGenericInstance instance = new org.swift.swiftkit.core.OutSwiftGenericInstance();
+ org.swift.swiftkit.core._OutSwiftGenericInstance instance = new org.swift.swiftkit.core._OutSwiftGenericInstance();
SwiftModule.$makeStringID(value, instance);
return MyID.wrapMemoryAddressUnsafe(instance.selfPointer, instance.selfTypePointer, swiftArena$);
}
""",
"""
- private static native void $makeStringID(java.lang.String value, org.swift.swiftkit.core.OutSwiftGenericInstance out);
+ private static native void $makeStringID(java.lang.String value, org.swift.swiftkit.core._OutSwiftGenericInstance out);
""",
]
)
@@ -173,15 +173,15 @@ struct JNIGenericTypeTests {
detectChunkByInitialLines: 2,
expectedChunks: [
"""
- @_cdecl("Java_com_example_swift_SwiftModule__00024makeStringID__Ljava_lang_String_2Lorg_swift_swiftkit_core_OutSwiftGenericInstance_2")
- public func Java_com_example_swift_SwiftModule__00024makeStringID__Ljava_lang_String_2Lorg_swift_swiftkit_core_OutSwiftGenericInstance_2(environment: UnsafeMutablePointer!, thisClass: jclass, value: jstring?, out: jobject?) {
+ @_cdecl("Java_com_example_swift_SwiftModule__00024makeStringID__Ljava_lang_String_2Lorg_swift_swiftkit_core__1OutSwiftGenericInstance_2")
+ public func Java_com_example_swift_SwiftModule__00024makeStringID__Ljava_lang_String_2Lorg_swift_swiftkit_core__1OutSwiftGenericInstance_2(environment: UnsafeMutablePointer!, thisClass: jclass, value: jstring?, out: jobject?) {
let result$ = UnsafeMutablePointer>.allocate(capacity: 1)
result$.initialize(to: SwiftModule.makeStringID(String(fromJNI: value, in: environment)))
let resultBits$ = Int64(Int(bitPattern: result$))
- environment.interface.SetLongField(environment, out, _JNIMethodIDCache.OutSwiftGenericInstance.selfPointer, resultBits$.getJNIValue(in: environment))
+ environment.interface.SetLongField(environment, out, _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, resultBits$.getJNIValue(in: environment))
let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self)
let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer))
- environment.interface.SetLongField(environment, out, _JNIMethodIDCache.OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment))
+ environment.interface.SetLongField(environment, out, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment))
return
}
"""
From c201a9c419e783a51f0e8fb4c18160194ec96e01 Mon Sep 17 00:00:00 2001
From: Konrad `ktoso` Malawski
Date: Fri, 27 Feb 2026 17:27:52 +0900
Subject: [PATCH 25/25] Apply suggestions from code review
---
.../Documentation.docc/SupportedFeatures.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md
index ab8f2217..449a4c6b 100644
--- a/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md
+++ b/Sources/SwiftJavaDocumentation/Documentation.docc/SupportedFeatures.md
@@ -362,7 +362,7 @@ 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 limited.
+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.