From 1239ed3c85f31bb239b65d59ce656b4219bd5524 Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 25 Mar 2026 16:34:37 +0900 Subject: [PATCH 01/12] Add more generic function pattern testing # Conflicts: # Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java --- .../Sources/MySwiftLibrary/GenericType.swift | 56 +++++++++++++++---- .../com/example/swift/GenericTypeTest.java | 22 +++++++- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift index d4597327..be167fc5 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift @@ -22,20 +22,54 @@ public struct MyID { } } -public func makeIntID(_ value: Int) -> MyID { - MyID(value) -} +public enum MyIDs { + public static func makeIntID(_ value: Int) -> MyID { + MyID(value) + } -public func makeStringID(_ value: String) -> MyID { - MyID(value) -} + public static func takeIntValue(from value: MyID) -> Int { + value.rawValue + } -public func takeIntValue(from value: MyID) -> Int { - value.rawValue -} + public static func makeStringID(_ value: String) -> MyID { + MyID(value) + } + + public static func takeStringValue(from value: MyID) -> String { + value.rawValue + } + + public static func makeIDs(_ stringValue: String, _ intValue: Int) -> (MyID, MyID) { + (MyID(stringValue), MyID(intValue)) + } + + public static func takeValuesFromTuple(_ tuple: (MyID, MyID)) -> (String, Int) { + (tuple.0.rawValue, tuple.1.rawValue) + } + + public static func makeBoolIDArray(_ value: Bool, length: Int) -> [MyID] { + Array(repeating: MyID(value), count: length) + } -public func takeStringValue(from value: MyID) -> String { - value.rawValue + public static func takeBoolValues(from ids: [MyID]) -> [Bool] { + ids.map { $0.rawValue } + } + + public static func makeIntIDOptional(_ value: Int) -> MyID? { + MyID(value) + } + + public static func takeIntValueOptional(from id: MyID?) -> Int? { + id?.rawValue + } + + public static func makeOptionalIntID(_ value: Int?) -> MyID { + MyID(value) + } + + public static func takeOptionalIntValue(from id: MyID) -> Int? { + id.rawValue + } } public struct MyEntity { 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 bedf0eca..8e67cc91 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java @@ -25,11 +25,29 @@ void genericTypeValueRoundtrip() { try (var arena = SwiftArena.ofConfined()) { MyID stringId = MySwiftLibrary.makeStringID("Java", arena); assertEquals("Java", stringId.getDescription()); - assertEquals("Java", MySwiftLibrary.takeStringValue(stringId)); + assertEquals("Java", MyIDs.takeStringValue(stringId)); MyID intId = MySwiftLibrary.makeIntID(42, arena); assertEquals("42", intId.getDescription()); - assertEquals(42, MySwiftLibrary.takeIntValue(intId)); + assertEquals(42, MyIDs.takeIntValue(intId)); + + Tuple2, MyID> ids = MyIDs.makeIDs("Java", 42, arena); + assertEquals("Java", ids.$0.getDescription()); + assertEquals("42", ids.$1.getDescription()); + assertEquals("Java", MyIDs.takeValuesFromTuple(ids).$0); + assertEquals(42, MyIDs.takeValuesFromTuple(ids).$1); + + // MyID[] boolIds = MyIDs.makeBoolIDArray(true, 3, arena); + // assertEquals(List.of(true, true, true), MyIDs.takeBoolValuesFromArray(boolIds)); + + Optional> doubleIdOptional = MyIDs.makeDoubleIDOptional(42.195, arena); + assertTrue(doubleIdOptional.isPresent()); + assertEquals(42.195, MyIDs.takeDoubleValueOptional(doubleIdOptional)); + assertEquals(42.195, MyIDs.takeDoubleValue(doubleIdOptional.get())); + + MyID> optionalIntId = MyIDs.makeOptionalIntID(42, arena); + assertEquals("Optional(42)", optionalIntId.getDescription()); + assertEquals(42, MyIDs.takeOptionalIntValue(optionalIntId)); } } From 23c3ee175a137838c6288278572ff3e3f9b9aa2b Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 30 Mar 2026 10:47:03 +0900 Subject: [PATCH 02/12] Reconsider the logic for wrapping return values --- .../Sources/MySwiftLibrary/GenericType.swift | 18 +- .../com/example/swift/GenericTypeTest.java | 8 +- ...ISwift2JavaGenerator+JavaTranslation.swift | 171 +++++++++++------- ...wift2JavaGenerator+NativeTranslation.swift | 128 ++++++++----- 4 files changed, 196 insertions(+), 129 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift index be167fc5..5226d294 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift @@ -47,19 +47,19 @@ public enum MyIDs { (tuple.0.rawValue, tuple.1.rawValue) } - public static func makeBoolIDArray(_ value: Bool, length: Int) -> [MyID] { - Array(repeating: MyID(value), count: length) - } + // public static func makeBoolIDArray(_ value: Bool, length: Int) -> [MyID] { + // Array(repeating: MyID(value), count: length) + // } - public static func takeBoolValues(from ids: [MyID]) -> [Bool] { - ids.map { $0.rawValue } - } + // public static func takeBoolValues(from ids: [MyID]) -> [Bool] { + // ids.map { $0.rawValue } + // } - public static func makeIntIDOptional(_ value: Int) -> MyID? { + public static func makeDoubleIDOptional(_ value: Double) -> MyID? { MyID(value) } - - public static func takeIntValueOptional(from id: MyID?) -> Int? { + + public static func takeDoubleValueOptional(from id: MyID?) -> Double? { id?.rawValue } 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 8e67cc91..3dc60222 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java @@ -14,8 +14,10 @@ package com.example.swift; +import java.util.Optional; import org.junit.jupiter.api.Test; import org.swift.swiftkit.core.SwiftArena; +import org.swift.swiftkit.core.tuple.Tuple2; import static org.junit.jupiter.api.Assertions.*; @@ -23,11 +25,11 @@ public class GenericTypeTest { @Test void genericTypeValueRoundtrip() { try (var arena = SwiftArena.ofConfined()) { - MyID stringId = MySwiftLibrary.makeStringID("Java", arena); + MyID stringId = MyIDs.makeStringID("Java", arena); assertEquals("Java", stringId.getDescription()); assertEquals("Java", MyIDs.takeStringValue(stringId)); - MyID intId = MySwiftLibrary.makeIntID(42, arena); + MyID intId = MyIDs.makeIntID(42, arena); assertEquals("42", intId.getDescription()); assertEquals(42, MyIDs.takeIntValue(intId)); @@ -54,7 +56,7 @@ void genericTypeValueRoundtrip() { @Test void genericTypeProperty() { try (var arena = SwiftArena.ofConfined()) { - MyID intId = MySwiftLibrary.makeIntID(42, arena); + MyID intId = MyIDs.makeIntID(42, arena); MyEntity entity = MyEntity.init(intId, "name", arena); assertEquals("42", entity.getId(arena).getDescription()); } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 38e891f2..89219b9a 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -984,15 +984,15 @@ extension JNISwift2JavaGenerator { return TranslatedResult( javaType: javaType, annotations: resultAnnotations, - outParameters: [.init(name: "instance", type: ._OutSwiftGenericInstance, allocation: .new)], + outParameters: [.init(name: resultName, type: ._OutSwiftGenericInstance, allocation: .new)], conversion: .aggregate( variable: nil, [ - .print(.placeholder), +// .print(.placeholder), .wrapMemoryAddressUnsafe( .commaSeparated([ - .member(.constant("instance"), field: "selfPointer"), - .member(.constant("instance"), field: "selfTypePointer"), + .member(.constant(resultName), field: "selfPointer"), + .member(.constant(resultName), field: "selfTypePointer"), ]), javaType ), @@ -1158,19 +1158,32 @@ extension JNISwift2JavaGenerator { let outParamName = "\(resultName)_\(idx)$" // Determine the Java type for this element - let (javaType, elementConversion) = try translateTupleElementResult( - type: element.type, + let elementResult = try translate( + swiftResult: .init(convention: .indirect, type: element.type), + resultName: outParamName, genericParameters: genericParameters, genericRequirements: genericRequirements ) - let arrayType: JavaType = .array(javaType) - outParameters.append( - OutParameter(name: outParamName, type: arrayType, allocation: .newArray(javaType, size: 1)) - ) +// let (javaType, elementConversion) = try translateTupleElementResult( +// type: element.type, +// genericParameters: genericParameters, +// genericRequirements: genericRequirements +// ) + let arrayType: JavaType = .array(elementResult.javaType) + + if elementResult.outParameters.isEmpty { + // Use the array as an output parameter instead of a return value. + outParameters.append( + OutParameter(name: outParamName, type: arrayType, allocation: .newArray(elementResult.javaType, size: 1)) + ) + elementConversions.append(elementResult.conversion) + } else { + outParameters.append(contentsOf: elementResult.outParameters) + elementConversions.append(.placeToVar(elementResult.conversion, name: "\(resultName)_\(idx)")) + } elementOutParamNames.append(outParamName) - elementConversions.append(elementConversion) - elementJavaTypes.append(javaType) + elementJavaTypes.append(elementResult.javaType) } let javaResultType: JavaType = .tuple(elementTypes: elementJavaTypes) @@ -1189,42 +1202,42 @@ extension JNISwift2JavaGenerator { ) } - /// Translate a single element type for tuple results on the Java side. - private func translateTupleElementResult( - type: SwiftType, - genericParameters: [SwiftGenericParameterDeclaration], - genericRequirements: [SwiftGenericRequirement] - ) throws -> (JavaType, JavaNativeConversionStep) { - switch type { - case .nominal(let nominalType): - if let knownType = nominalType.nominalTypeDecl.knownTypeKind { - guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { - throw JavaTranslationError.unsupportedSwiftType(type) - } - // Primitives: just read from array - return (javaType, .placeholder) - } - - guard !nominalType.isSwiftJavaWrapper else { - throw JavaTranslationError.unsupportedSwiftType(type) - } - - let javaType = try translateGenericTypeParameter( - type, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - // JExtract class: wrap memory address - return (.long, .constructSwiftValue(.placeholder, javaType)) - - default: - throw JavaTranslationError.unsupportedSwiftType(type) - } - } +// /// Translate a single element type for tuple results on the Java side. +// private func translateTupleElementResult( +// type: SwiftType, +// genericParameters: [SwiftGenericParameterDeclaration], +// genericRequirements: [SwiftGenericRequirement] +// ) throws -> (JavaType, JavaNativeConversionStep) { +// switch type { +// case .nominal(let nominalType): +// if let knownType = nominalType.nominalTypeDecl.knownTypeKind { +// guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { +// throw JavaTranslationError.unsupportedSwiftType(type) +// } +// // Primitives: just read from array +// return (javaType, .placeholder) +// } +// +// guard !nominalType.isSwiftJavaWrapper else { +// throw JavaTranslationError.unsupportedSwiftType(type) +// } +// +// let javaType = try translateGenericTypeParameter( +// type, +// genericParameters: genericParameters, +// genericRequirements: genericRequirements +// ) +// // JExtract class: wrap memory address +// return (.long, .constructSwiftValue(.placeholder, javaType)) +// +// default: +// throw JavaTranslationError.unsupportedSwiftType(type) +// } +// } func translateOptionalResult( wrappedType swiftType: SwiftType, - resultName: String = "result", + resultName: String, genericParameters: [SwiftGenericParameterDeclaration], genericRequirements: [SwiftGenericRequirement] ) throws -> TranslatedResult { @@ -1279,9 +1292,8 @@ extension JNISwift2JavaGenerator { OutParameter(name: discriminatorName, type: .array(.byte), allocation: .newArray(.byte, size: 1)) ], conversion: .toOptionalFromIndirectReturn( - discriminatorName: .combinedName(component: "discriminator$"), + discriminatorName: .constant(discriminatorName), optionalClass: optionalClass, - javaType: javaType, toValue: .placeholder, resultName: resultName ) @@ -1300,18 +1312,29 @@ extension JNISwift2JavaGenerator { genericParameters: genericParameters, genericRequirements: genericRequirements ) + + let wrappedValueResult = try translate( + swiftResult: SwiftResult(convention: .direct, type: swiftType), + resultName: resultName + "Wrapped$", + genericParameters: genericParameters, + genericRequirements: genericRequirements, + ) + let wrappedResultVariable = JavaNativeConversionStep.placeToVar( + wrappedValueResult.conversion, + name: resultName + "Wrapped" + ) + let returnType = JavaType.class(package: nil, name: "Optional", typeParameters: [javaType]) return TranslatedResult( javaType: returnType, annotations: parameterAnnotations, outParameters: [ OutParameter(name: discriminatorName, type: .array(.byte), allocation: .newArray(.byte, size: 1)) - ], + ] + wrappedValueResult.outParameters, conversion: .toOptionalFromIndirectReturn( - discriminatorName: .combinedName(component: "discriminator$"), + discriminatorName: .constant(discriminatorName), optionalClass: "Optional", - javaType: .long, - toValue: .wrapMemoryAddressUnsafe(.placeholder, javaType), + toValue: wrappedResultVariable, resultName: resultName ) ) @@ -1422,7 +1445,7 @@ extension JNISwift2JavaGenerator { ] ), function: "toArray", - arguments: [.constant("\(javaType)[]::new")] + arguments: [.constant("\(javaType.className!)[]::new")] ) ) @@ -1736,22 +1759,16 @@ extension JNISwift2JavaGenerator { static func toOptionalFromIndirectReturn( discriminatorName: JavaNativeConversionStep, optionalClass: String, - javaType: JavaType, toValue valueConversion: JavaNativeConversionStep, resultName: String ) -> JavaNativeConversionStep { - .aggregate( - variable: (name: "\(resultName)$", type: javaType), - [ - .ternary( - .equals( - .subscriptOf(discriminatorName, arguments: [.constant("0")]), - .constant("1") - ), - thenExp: .method(.constant(optionalClass), function: "of", arguments: [valueConversion]), - elseExp: .method(.constant(optionalClass), function: "empty") - ) - ] + .ternary( + .equals( + .subscriptOf(discriminatorName, arguments: [.constant("0")]), + .constant("1") + ), + thenExp: .method(.constant(optionalClass), function: "of", arguments: [valueConversion]), + elseExp: .method(.constant(optionalClass), function: "empty") ) } @@ -1779,6 +1796,8 @@ extension JNISwift2JavaGenerator { /// E.g. `new Tuple2<>(result_0$[0], result_1$[0])` case tupleFromOutParams(tupleClassName: String, elements: [(outParamName: String, elementConversion: JavaNativeConversionStep)]) + indirect case placeToVar(JavaNativeConversionStep, name: String) + /// `Arrays.stream(args)` static func arraysStream(_ argument: JavaNativeConversionStep) -> JavaNativeConversionStep { .method(.constant("Arrays"), function: "stream", arguments: [argument]) @@ -1810,15 +1829,23 @@ extension JNISwift2JavaGenerator { case .constructSwiftValue(let inner, let javaType): let inner = inner.render(&printer, placeholder) - return "new \(javaType.className!)(\(inner), swiftArena)" + return "new \(javaType)(\(inner), swiftArena)" case .wrapMemoryAddressUnsafe(let inner, let javaType): let inner = inner.render(&printer, placeholder) - return "\(javaType.className!).wrapMemoryAddressUnsafe(\(inner), swiftArena)" + guard case .class(_, let className, let typeParameters) = javaType else { + fatalError("\(javaType) is not class.") + } + let genericClause = if !typeParameters.isEmpty { + "<\(typeParameters.map(\.description).joined(separator: ", "))>" + } else { + "" + } + return "\(className).\(genericClause)wrapMemoryAddressUnsafe(\(inner), swiftArena)" case .constructJavaClass(let inner, let javaType): let inner = inner.render(&printer, placeholder) - return "new \(javaType.className!)(\(inner))" + return "new \(javaType)(\(inner))" case .call(let inner, let function): let inner = inner.render(&printer, placeholder) @@ -1939,6 +1966,11 @@ extension JNISwift2JavaGenerator { args.append(converted) } return "\(tupleClassName)(\(args.joined(separator: ", ")))" + + case .placeToVar(let inner, let name): + let inner = inner.render(&printer, placeholder) + printer.print("var \(name) = \(inner);") + return name } } @@ -2004,6 +2036,9 @@ extension JNISwift2JavaGenerator { case .tupleFromOutParams(_, let elements): return elements.contains(where: { $0.elementConversion.requiresSwiftArena }) + + case .placeToVar(let inner, _): + return inner.requiresSwiftArena } } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index 48875f8f..9d44c7d0 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -577,18 +577,26 @@ extension JNISwift2JavaGenerator { throw JavaTranslationError.unsupportedSwiftType(swiftType) } + let wrappedValueResult = try translate( + swiftResult: .init( + convention: .direct, + type: swiftType + ), + resultName: resultName + "Wrapped" + ) + // Assume JExtract imported class return NativeResult( - javaType: .long, + javaType: wrappedValueResult.javaType, conversion: .optionalRaisingIndirectReturn( - .getJNIValue(.allocateSwiftValue(.placeholder, name: "_result", swiftType: swiftType)), - returnType: .long, + wrappedValueResult.conversion, + returnType: wrappedValueResult.javaType, discriminatorParameterName: discriminatorName, placeholderValue: .constant("0") ), outParameters: [ JavaParameter(name: discriminatorName, type: .array(.byte)) - ] + ] + wrappedValueResult.outParameters ) default: @@ -746,9 +754,9 @@ extension JNISwift2JavaGenerator { conversion: .genericValueIndirectReturn( .getJNIValue(.allocateSwiftValue(.placeholder, name: resultName, swiftType: swiftResult.type)), swiftFunctionResultType: swiftResult.type, - outArgumentName: "out" + outArgumentName: resultName + "Out" ), - outParameters: [.init(name: "out", type: ._OutSwiftGenericInstance)] + outParameters: [.init(name: resultName + "Out", type: ._OutSwiftGenericInstance)] ) } else { return NativeResult( @@ -784,12 +792,21 @@ extension JNISwift2JavaGenerator { let outParamName = "\(resultName)_\(idx)$" // Get the JNI type for this element - let elementResult = try translateElementResult(type: element.type) - - outParameters.append( - JavaParameter(name: outParamName, type: .array(elementResult.javaType)) +// let elementResult = try translateElementResult(type: element.type) + let elementResult = try translate( + swiftResult: .init(convention: .indirect, type: element.type), + resultName: outParamName ) + if elementResult.outParameters.isEmpty { + // Use the array as an output parameter instead of a return value. + outParameters.append( + JavaParameter(name: outParamName, type: .array(elementResult.javaType)) + ) + } else { + outParameters.append(contentsOf: elementResult.outParameters) + } + destructureElements.append( ( index: idx, @@ -808,30 +825,30 @@ extension JNISwift2JavaGenerator { ) } - /// Translate a single element type for use in tuple result destructuring. - private func translateElementResult(type: SwiftType) throws -> (javaType: JavaType, conversion: NativeSwiftConversionStep) { - switch type { - case .nominal(let nominalType): - if let knownType = nominalType.nominalTypeDecl.knownTypeKind { - guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config), - javaType.implementsJavaValue - else { - throw JavaTranslationError.unsupportedSwiftType(type) - } - return (javaType: javaType, conversion: .getJNIValue(.placeholder)) - } - - guard !nominalType.isSwiftJavaWrapper else { - throw JavaTranslationError.unsupportedSwiftType(type) - } - - // JExtract class: allocate and return pointer - return (javaType: .long, conversion: .getJNIValue(.allocateSwiftValue(.placeholder, name: "element", swiftType: type))) - - default: - throw JavaTranslationError.unsupportedSwiftType(type) - } - } +// /// Translate a single element type for use in tuple result destructuring. +// private func translateElementResult(type: SwiftType) throws -> (javaType: JavaType, conversion: NativeSwiftConversionStep) { +// switch type { +// case .nominal(let nominalType): +// if let knownType = nominalType.nominalTypeDecl.knownTypeKind { +// guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config), +// javaType.implementsJavaValue +// else { +// throw JavaTranslationError.unsupportedSwiftType(type) +// } +// return (javaType: javaType, conversion: .getJNIValue(.placeholder)) +// } +// +// guard !nominalType.isSwiftJavaWrapper else { +// throw JavaTranslationError.unsupportedSwiftType(type) +// } +// +// // JExtract class: allocate and return pointer +// return (javaType: .long, conversion: .getJNIValue(.allocateSwiftValue(.placeholder, name: "element", swiftType: type))) +// +// default: +// throw JavaTranslationError.unsupportedSwiftType(type) +// } +// } func translateArrayResult( elementType: SwiftType, @@ -1490,12 +1507,16 @@ extension JNISwift2JavaGenerator { let discriminatorParameterName, let placeholderValue ): - printer.print("let result$: \(returnType.jniTypeName)") + if !returnType.isVoid { + printer.print("let result$: \(returnType.jniTypeName)") + } printer.printBraceBlock("if let innerResult$ = \(placeholder)") { printer in let inner = inner.render(&printer, "innerResult$") + if !returnType.isVoid { + printer.print("result$ = \(inner)") + } printer.print( """ - result$ = \(inner) var flag$ = Int8(1) environment.interface.SetByteArrayRegion(environment, \(discriminatorParameterName), 0, 1, &flag$) """ @@ -1503,27 +1524,34 @@ extension JNISwift2JavaGenerator { } printer.printBraceBlock("else") { printer in let placeholderValue = placeholderValue.render(&printer, placeholder) + if !returnType.isVoid { + printer.print("result$ = \(placeholderValue)") + } printer.print( """ - result$ = \(placeholderValue) var flag$ = Int8(0) environment.interface.SetByteArrayRegion(environment, \(discriminatorParameterName), 0, 1, &flag$) """ ) } - - return "result$" + if !returnType.isVoid { + return "result$" + } else { + return "" + } 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)) - """ - ) + printer.printBraceBlock("do") { printer in + 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): @@ -1727,13 +1755,15 @@ extension JNISwift2JavaGenerator { for element in elements { let accessor = element.label ?? "\(element.index)" let converted = element.conversion.render(&printer, "\(tupleVar).\(accessor)") - if element.javaType.isPrimitive { + switch element.javaType { + case .void: break + case .boolean, .byte, .char, .short, .int, .long, .float, .double: let setMethodName = element.javaType.jniSetArrayRegionMethodName printer.print("var element_\(element.index)_jni$ = \(converted)") printer.print( "environment.interface.\(setMethodName)(environment, \(element.outParamName), 0, 1, &element_\(element.index)_jni$)" ) - } else { + case .class, .array: printer.print("let element_\(element.index)_jni$ = \(converted)") printer.print( "environment.interface.SetObjectArrayElement(environment, \(element.outParamName), 0, element_\(element.index)_jni$)" From 4e63833aee62e6948a2c22447c714b2208af6a88 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 30 Mar 2026 12:55:07 +0900 Subject: [PATCH 03/12] Fix generic type name is not printed for boxed type --- .../Sources/MySwiftLibrary/GenericType.swift | 4 ++ .../com/example/swift/GenericTypeTest.java | 3 +- .../Convenience/JavaType+Extensions.swift | 37 +++++++++++++------ 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift index 5226d294..c1e3e947 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift @@ -63,6 +63,10 @@ public enum MyIDs { id?.rawValue } + public static func takeDoubleValue(from value: MyID) -> Double { + value.rawValue + } + public static func makeOptionalIntID(_ value: Int?) -> MyID { MyID(value) } 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 3dc60222..d4cec5e7 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java @@ -15,6 +15,7 @@ package com.example.swift; import java.util.Optional; +import java.util.OptionalLong; import org.junit.jupiter.api.Test; import org.swift.swiftkit.core.SwiftArena; import org.swift.swiftkit.core.tuple.Tuple2; @@ -47,7 +48,7 @@ void genericTypeValueRoundtrip() { assertEquals(42.195, MyIDs.takeDoubleValueOptional(doubleIdOptional)); assertEquals(42.195, MyIDs.takeDoubleValue(doubleIdOptional.get())); - MyID> optionalIntId = MyIDs.makeOptionalIntID(42, arena); + MyID> optionalIntId = MyIDs.makeOptionalIntID(OptionalLong.of(42L), arena); assertEquals("Optional(42)", optionalIntId.getDescription()); assertEquals(42, MyIDs.takeOptionalIntValue(optionalIntId)); } diff --git a/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift b/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift index fd7f6dc5..7fd2bdd1 100644 --- a/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift +++ b/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift @@ -164,18 +164,31 @@ extension JavaType { /// The boxed class name for this type, suitable for use as a generic type argument. var boxedName: String { switch self { - case .boolean: "Boolean" - case .byte: "Byte" - case .char: "Character" - case .short: "Short" - case .int: "Integer" - case .long: "Long" - case .float: "Float" - case .double: "Double" - case .void: "Void" - case .javaLangString: "String" - case .class(_, let name, _): name - case .array: description + case .boolean: return "Boolean" + case .byte: return "Byte" + case .char: return "Character" + case .short: return "Short" + case .int: return "Integer" + case .long: return "Long" + case .float: return "Float" + case .double: return "Double" + case .void: return "Void" + case .javaLangString: return "String" + case .class(let package, let name, let typeParameters): + let packageClause: String = + if let package { + "\(package)." + } else { + "" + } + let genericClause: String = + if !typeParameters.isEmpty { + "<\(typeParameters.map(\.boxedName).joined(separator: ", "))>" + } else { + "" + } + return "\(packageClause)\(name)\(genericClause)" + case .array: return description } } } From 549e554c0f91305be2e6f54056ba37b9a4baf153 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 30 Mar 2026 14:18:20 +0900 Subject: [PATCH 04/12] Fix test failures for simple pattern --- .../JNI/JNIDictionaryTest.swift | 16 +++++++-------- .../JNI/JNIGenericTypeTests.swift | 20 ++++++++++--------- .../JNI/JNIOptionalTests.swift | 8 ++++---- Tests/JExtractSwiftTests/JNI/JNISetTest.swift | 12 +++++------ .../JNI/JNITupleTests.swift | 8 ++++---- 5 files changed, 33 insertions(+), 31 deletions(-) diff --git a/Tests/JExtractSwiftTests/JNI/JNIDictionaryTest.swift b/Tests/JExtractSwiftTests/JNI/JNIDictionaryTest.swift index c09bbaaa..0690ee01 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIDictionaryTest.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIDictionaryTest.swift @@ -28,7 +28,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """, """ @@ -104,7 +104,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(org.swift.swiftkit.core.collections.SwiftDictionaryMap dict, SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress()), swiftArena); + return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress()), swiftArena); } """, """ @@ -142,7 +142,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(org.swift.swiftkit.core.collections.SwiftDictionaryMap dict, SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress()), swiftArena); + return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress()), swiftArena); } """, """ @@ -183,7 +183,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -200,7 +200,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -217,7 +217,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -234,7 +234,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -295,7 +295,7 @@ struct JNIDictionaryTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftDictionaryMap f(org.swift.swiftkit.core.collections.SwiftDictionaryMap dict, java.lang.String key, long value, SwiftArena swiftArena) { - return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress(), key, value), swiftArena); + return SwiftDictionaryMap.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(dict, "dict must not be null").$memoryAddress(), key, value), swiftArena); } """, """ diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift index cf5beded..7c97ad39 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericTypeTests.swift @@ -155,13 +155,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(); - SwiftModule.$makeStringID(value, instance); - return MyID.wrapMemoryAddressUnsafe(instance.selfPointer, instance.selfTypePointer, swiftArena); + org.swift.swiftkit.core._OutSwiftGenericInstance result = new org.swift.swiftkit.core._OutSwiftGenericInstance(); + SwiftModule.$makeStringID(value, result); + return MyID.wrapMemoryAddressUnsafe(result.selfPointer, result.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 resultOut); """, """ public static long takeIntID(MyID value) { @@ -185,14 +185,16 @@ struct JNIGenericTypeTests { expectedChunks: [ """ @_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?) { + public func Java_com_example_swift_SwiftModule__00024makeStringID__Ljava_lang_String_2Lorg_swift_swiftkit_core__1OutSwiftGenericInstance_2(environment: UnsafeMutablePointer!, thisClass: jclass, value: jstring?, resultOut: 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)) + do { + environment.interface.SetLongField(environment, resultOut, _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, resultOut, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) + } return } """, diff --git a/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift b/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift index 90702944..728165bf 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift @@ -184,10 +184,10 @@ struct JNIOptionalTests { let arg$ = UnsafeMutablePointer(bitPattern: argBits$) let result$: jlong if let innerResult$ = SwiftModule.optionalClass(arg$?.pointee) { - let _result$ = UnsafeMutablePointer.allocate(capacity: 1) - _result$.initialize(to: innerResult$) - let _resultBits$ = Int64(Int(bitPattern: _result$)) - result$ = _resultBits$.getJNIValue(in: environment) + let resultWrapped$ = UnsafeMutablePointer.allocate(capacity: 1) + resultWrapped$.initialize(to: innerResult$) + let resultWrappedBits$ = Int64(Int(bitPattern: resultWrapped$)) + result$ = resultWrappedBits$.getJNILocalRefValue(in: environment) var flag$ = Int8(1) environment.interface.SetByteArrayRegion(environment, result_discriminator$, 0, 1, &flag$) } diff --git a/Tests/JExtractSwiftTests/JNI/JNISetTest.swift b/Tests/JExtractSwiftTests/JNI/JNISetTest.swift index 4a70fdcd..e654344d 100644 --- a/Tests/JExtractSwiftTests/JNI/JNISetTest.swift +++ b/Tests/JExtractSwiftTests/JNI/JNISetTest.swift @@ -28,7 +28,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """, """ @@ -104,7 +104,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(org.swift.swiftkit.core.collections.SwiftSet set, SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(set, "set must not be null").$memoryAddress()), swiftArena); + return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(set, "set must not be null").$memoryAddress()), swiftArena); } """, """ @@ -145,7 +145,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -162,7 +162,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -179,7 +179,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); + return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(), swiftArena); } """ ] @@ -240,7 +240,7 @@ struct JNISetTest { expectedChunks: [ """ public static org.swift.swiftkit.core.collections.SwiftSet f(org.swift.swiftkit.core.collections.SwiftSet set, java.lang.String element, SwiftArena swiftArena) { - return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(set, "set must not be null").$memoryAddress(), element), swiftArena); + return SwiftSet.wrapMemoryAddressUnsafe(SwiftModule.$f(Objects.requireNonNull(set, "set must not be null").$memoryAddress(), element), swiftArena); } """, """ diff --git a/Tests/JExtractSwiftTests/JNI/JNITupleTests.swift b/Tests/JExtractSwiftTests/JNI/JNITupleTests.swift index 328fa33d..1a92fa99 100644 --- a/Tests/JExtractSwiftTests/JNI/JNITupleTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNITupleTests.swift @@ -56,9 +56,9 @@ struct JNITupleTests { expectedChunks: [ """ let tupleResult$ = SwiftModule.returnPair() - var element_0_jni$ = tupleResult$.0.getJNIValue(in: environment) + var element_0_jni$ = tupleResult$.0.getJNILocalRefValue(in: environment) environment.interface.SetLongArrayRegion(environment, result_0$, 0, 1, &element_0_jni$) - let element_1_jni$ = tupleResult$.1.getJNIValue(in: environment) + let element_1_jni$ = tupleResult$.1.getJNILocalRefValue(in: environment) environment.interface.SetObjectArrayElement(environment, result_1$, 0, element_1_jni$) """ ] @@ -127,9 +127,9 @@ struct JNITupleTests { expectedChunks: [ """ let tupleResult$ = SwiftModule.labeledTuple() - var element_0_jni$ = tupleResult$.x.getJNIValue(in: environment) + var element_0_jni$ = tupleResult$.x.getJNILocalRefValue(in: environment) environment.interface.SetIntArrayRegion(environment, result_0$, 0, 1, &element_0_jni$) - var element_1_jni$ = tupleResult$.y.getJNIValue(in: environment) + var element_1_jni$ = tupleResult$.y.getJNILocalRefValue(in: environment) environment.interface.SetIntArrayRegion(environment, result_1$, 0, 1, &element_1_jni$) """ ] From 0f186985b58f9c481c5494368530f98b39744e53 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 30 Mar 2026 15:15:00 +0900 Subject: [PATCH 05/12] Add JNIGenericCombinationTests --- .../JNI/JNIGenericCombinationTests.swift | 286 ++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift new file mode 100644 index 00000000..cb43f632 --- /dev/null +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift @@ -0,0 +1,286 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import JExtractSwiftLib +import Testing + +@Suite +struct JNIGenericCombinationTests { + static let myIDDecl = + #""" + public struct MyID { + public var rawValue: T + public init(_ rawValue: T) { + self.rawValue = rawValue + } + public var description: String { + "\(rawValue)" + } + } + """# + + @Suite struct WithOptional { + let returnFuncFile = """ + \(myIDDecl) + + public func makeStringIDOptional(_ value: String) -> MyID? { + return MyID(value) + } + """ + + let argumentFuncFile = """ + \(myIDDecl) + + public func takeStringIDOptional(_ value: MyID?) { + } + """ + + @Test + func returnFuncJava() throws { + try assertOutput( + input: returnFuncFile, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + public static Optional> makeStringIDOptional(java.lang.String value, SwiftArena swiftArena) { + byte[] result$_discriminator$ = new byte[1]; + org.swift.swiftkit.core._OutSwiftGenericInstance resultWrapped$ = new org.swift.swiftkit.core._OutSwiftGenericInstance(); + SwiftModule.$makeStringIDOptional(value, result$_discriminator$, resultWrapped$); + return (result$_discriminator$[0] == 1) ? Optional.of(MyID.wrapMemoryAddressUnsafe(resultWrapped$.selfPointer, resultWrapped$.selfTypePointer, swiftArena)) : Optional.empty(); + } + """, + """ + private static native void $makeStringIDOptional(java.lang.String value, byte[] result_discriminator$, org.swift.swiftkit.core._OutSwiftGenericInstance resultWrappedOut); + """, + ] + ) + } + + @Test + func returnFuncSwift() throws { + try assertOutput( + input: returnFuncFile, + .jni, + .swift, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024makeStringIDOptional__Ljava_lang_String_2_3BLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2") + public func Java_com_example_swift_SwiftModule__00024makeStringIDOptional__Ljava_lang_String_2_3BLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2(environment: UnsafeMutablePointer!, thisClass: jclass, value: jstring?, result_discriminator$: jbyteArray?, resultWrappedOut: jobject?) { + if let innerResult$ = SwiftModule.makeStringIDOptional(String(fromJNI: value, in: environment)) { + let resultWrapped$ = UnsafeMutablePointer>.allocate(capacity: 1) + resultWrapped$.initialize(to: innerResult$) + let resultWrappedBits$ = Int64(Int(bitPattern: resultWrapped$)) + do { + environment.interface.SetLongField(environment, resultWrappedOut, _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, resultWrappedBits$.getJNIValue(in: environment)) + let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self) + let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer)) + environment.interface.SetLongField(environment, resultWrappedOut, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) + } + var flag$ = Int8(1) + environment.interface.SetByteArrayRegion(environment, result_discriminator$, 0, 1, &flag$) + } + else { + var flag$ = Int8(0) + environment.interface.SetByteArrayRegion(environment, result_discriminator$, 0, 1, &flag$) + } + return + } + """, + ] + ) + } + + @Test + func argumentFuncJava() throws { + try assertOutput( + input: argumentFuncFile, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + public static void takeStringIDOptional(Optional> value) { + SwiftModule.$takeStringIDOptional(value.map(MyID::$memoryAddress).orElse(0L)); + } + """, + """ + private static native void $takeStringIDOptional(long value); + """, + ] + ) + } + + @Test + func argumentFuncSwift() throws { + try assertOutput( + input: argumentFuncFile, + .jni, + .swift, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024takeStringIDOptional__J") + public func Java_com_example_swift_SwiftModule__00024takeStringIDOptional__J(environment: UnsafeMutablePointer!, thisClass: jclass, value: jlong) { + let valueBits$ = Int(Int64(fromJNI: value, in: environment)) + let value$ = UnsafeMutablePointer>(bitPattern: valueBits$) + SwiftModule.takeStringIDOptional(value$?.pointee) + } + """, + ] + ) + } + } + + @Suite struct WithTuple { + let returnFuncFile = """ + \(myIDDecl) + + public static func makeIDs(_ stringValue: String, _ intValue: Int64) -> (MyID, MyID) { + (MyID(stringValue), MyID(intValue)) + } + """ + + let argumentFuncFile = """ + \(myIDDecl) + + public static func takeValues(from tuple: (MyID, MyID)) -> (String, Int64) { + (tuple.0.rawValue, tuple.1.rawValue) + } + """ + + @Test + func returnFuncJava() throws { + try assertOutput( + input: returnFuncFile, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + public static org.swift.swiftkit.core.tuple.Tuple2, MyID> makeIDs(java.lang.String stringValue, long intValue, SwiftArena swiftArena) { + org.swift.swiftkit.core._OutSwiftGenericInstance result_0$ = new org.swift.swiftkit.core._OutSwiftGenericInstance(); + org.swift.swiftkit.core._OutSwiftGenericInstance result_1$ = new org.swift.swiftkit.core._OutSwiftGenericInstance(); + SwiftModule.$makeIDs(stringValue, intValue, result_0$, result_1$); + var result_0 = MyID.wrapMemoryAddressUnsafe(result_0$.selfPointer, result_0$.selfTypePointer, swiftArena); + var result_1 = MyID.wrapMemoryAddressUnsafe(result_1$.selfPointer, result_1$.selfTypePointer, swiftArena); + return new org.swift.swiftkit.core.tuple.Tuple2<>(result_0, result_1); + } + """, + """ + private static native void $makeIDs(java.lang.String stringValue, long intValue, org.swift.swiftkit.core._OutSwiftGenericInstance result_0$Out, org.swift.swiftkit.core._OutSwiftGenericInstance result_1$Out); + """, + ] + ) + } + + @Test + func returnFuncSwift() throws { + try assertOutput( + input: returnFuncFile, + .jni, + .swift, + detectChunkByInitialLines: 4, + expectedChunks: [ + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024makeIDs__Ljava_lang_String_2JLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2Lorg_swift_swiftkit_core__1OutSwiftGenericInstance_2") + public func Java_com_example_swift_SwiftModule__00024makeIDs__Ljava_lang_String_2JLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2Lorg_swift_swiftkit_core__1OutSwiftGenericInstance_2(environment: UnsafeMutablePointer!, thisClass: jclass, stringValue: jstring?, intValue: jlong, result_0$Out: jobject?, result_1$Out: jobject?) { + let tupleResult$ = SwiftModule.makeIDs(String(fromJNI: stringValue, in: environment), Int64(fromJNI: intValue, in: environment)) + let result_0$$ = UnsafeMutablePointer>.allocate(capacity: 1) + result_0$$.initialize(to: tupleResult$.0) + let result_0$Bits$ = Int64(Int(bitPattern: result_0$$)) + do { + environment.interface.SetLongField(environment, result_0$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, result_0$Bits$.getJNIValue(in: environment)) + let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self) + let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer)) + environment.interface.SetLongField(environment, result_0$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) + } + let result_1$$ = UnsafeMutablePointer>.allocate(capacity: 1) + result_1$$.initialize(to: tupleResult$.1) + let result_1$Bits$ = Int64(Int(bitPattern: result_1$$)) + do { + environment.interface.SetLongField(environment, result_1$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, result_1$Bits$.getJNIValue(in: environment)) + let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self) + let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer)) + environment.interface.SetLongField(environment, result_1$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) + } + return + } + """, + ] + ) + } + + @Test + func argumentFuncJava() throws { + try assertOutput( + input: argumentFuncFile, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + public static org.swift.swiftkit.core.tuple.Tuple2 takeValues(org.swift.swiftkit.core.tuple.Tuple2, MyID> tuple) { + java.lang.String[] result_0$ = new java.lang.String[1]; + long[] result_1$ = new long[1]; + SwiftModule.$takeValues(tuple.$0.$memoryAddress(), tuple.$1.$memoryAddress(), result_0$, result_1$); + return new org.swift.swiftkit.core.tuple.Tuple2<>(result_0$[0], result_1$[0]); + } + """, + """ + private static native void $takeValues(long tuple_0, long tuple_1, java.lang.String[] result_0$, long[] result_1$); + """, + ] + ) + } + + @Test + func argumentFuncSwift() throws { + try assertOutput( + input: argumentFuncFile, + .jni, + .swift, + detectChunkByInitialLines: 2, + expectedChunks: [ + #""" + @_cdecl("Java_com_example_swift_SwiftModule__00024takeValues__JJ_3Ljava_lang_String_2_3J") + public func Java_com_example_swift_SwiftModule__00024takeValues__JJ_3Ljava_lang_String_2_3J(environment: UnsafeMutablePointer!, thisClass: jclass, tuple_0: jlong, tuple_1: jlong, result_0$: jobjectArray?, result_1$: jlongArray?) { + assert(tuple_0 != 0, "tuple_0 memory address was null") + let tuple_0Bits$ = Int(Int64(fromJNI: tuple_0, in: environment)) + let tuple_0$ = UnsafeMutablePointer>(bitPattern: tuple_0Bits$) + guard let tuple_0$ else { + fatalError("tuple_0 memory address was null in call to \(#function)!") + } + assert(tuple_1 != 0, "tuple_1 memory address was null") + let tuple_1Bits$ = Int(Int64(fromJNI: tuple_1, in: environment)) + let tuple_1$ = UnsafeMutablePointer>(bitPattern: tuple_1Bits$) + guard let tuple_1$ else { + fatalError("tuple_1 memory address was null in call to \(#function)!") + } + let tupleResult$ = SwiftModule.takeValues(from: (tuple_0$.pointee, tuple_1$.pointee)) + let element_0_jni$ = tupleResult$.0.getJNILocalRefValue(in: environment) + environment.interface.SetObjectArrayElement(environment, result_0$, 0, element_0_jni$) + var element_1_jni$ = tupleResult$.1.getJNILocalRefValue(in: environment) + environment.interface.SetLongArrayRegion(environment, result_1$, 0, 1, &element_1_jni$) + return + } + """#, + ] + ) + } + } + +} From be6a52b99885feda244ec4a52abe9122476d96e9 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 30 Mar 2026 17:51:08 +0900 Subject: [PATCH 06/12] print downcall first when the result type is void --- .../com/example/swift/GenericTypeTest.java | 6 +-- ...t2JavaGenerator+JavaBindingsPrinting.swift | 8 ++- ...ISwift2JavaGenerator+JavaTranslation.swift | 49 +++++++++++-------- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java index d4cec5e7..3592bcdb 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java @@ -45,12 +45,12 @@ void genericTypeValueRoundtrip() { Optional> doubleIdOptional = MyIDs.makeDoubleIDOptional(42.195, arena); assertTrue(doubleIdOptional.isPresent()); - assertEquals(42.195, MyIDs.takeDoubleValueOptional(doubleIdOptional)); - assertEquals(42.195, MyIDs.takeDoubleValue(doubleIdOptional.get())); + assertEquals(42.195, MyIDs.takeDoubleValueOptional(doubleIdOptional).getAsDouble()); + assertEquals(42.195, MyIDs.takeDoubleValue(doubleIdOptional.get())); // ensure wrapped value is alive MyID> optionalIntId = MyIDs.makeOptionalIntID(OptionalLong.of(42L), arena); assertEquals("Optional(42)", optionalIntId.getDescription()); - assertEquals(42, MyIDs.takeOptionalIntValue(optionalIntId)); + assertEquals(42, MyIDs.takeOptionalIntValue(optionalIntId).getAsLong()); } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 6c99d3de..b21c7372 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -732,7 +732,13 @@ extension JNISwift2JavaGenerator { if translatedFunctionSignature.resultType.javaType.isVoid { printer.print("\(downcall);") } else { - let result = translatedFunctionSignature.resultType.conversion.render(&printer, downcall) + let result: String + if translatedDecl.nativeFunctionSignature.result.javaType.isVoid { + printer.print("\(downcall);") + result = translatedFunctionSignature.resultType.conversion.render(&printer, "") + } else { + result = translatedFunctionSignature.resultType.conversion.render(&printer, downcall) + } printer.print("return \(result);") } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 89219b9a..723713cf 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -701,7 +701,7 @@ extension JNISwift2JavaGenerator { conversion: .aggregate( variable: nil, [ - .print(.placeholder), // Make the downcall +// .print(.placeholder), // Make the downcall .method( .constant("future$"), function: "thenApply", @@ -988,7 +988,7 @@ extension JNISwift2JavaGenerator { conversion: .aggregate( variable: nil, [ -// .print(.placeholder), +// .print(.placeholder), // make the downcall .wrapMemoryAddressUnsafe( .commaSeparated([ .member(.constant(resultName), field: "selfPointer"), @@ -1170,10 +1170,11 @@ extension JNISwift2JavaGenerator { // genericParameters: genericParameters, // genericRequirements: genericRequirements // ) - let arrayType: JavaType = .array(elementResult.javaType) + elementOutParamNames.append(outParamName) if elementResult.outParameters.isEmpty { // Use the array as an output parameter instead of a return value. + let arrayType: JavaType = .array(elementResult.javaType) outParameters.append( OutParameter(name: outParamName, type: arrayType, allocation: .newArray(elementResult.javaType, size: 1)) ) @@ -1182,7 +1183,6 @@ extension JNISwift2JavaGenerator { outParameters.append(contentsOf: elementResult.outParameters) elementConversions.append(.placeToVar(elementResult.conversion, name: "\(resultName)_\(idx)")) } - elementOutParamNames.append(outParamName) elementJavaTypes.append(elementResult.javaType) } @@ -1196,7 +1196,7 @@ extension JNISwift2JavaGenerator { javaType: javaResultType, outParameters: outParameters, conversion: .tupleFromOutParams( - tupleClassName: "new \(fullTupleClassName)<>", + tupleClassName: fullTupleClassName, elements: tupleElements ) ) @@ -1294,6 +1294,7 @@ extension JNISwift2JavaGenerator { conversion: .toOptionalFromIndirectReturn( discriminatorName: .constant(discriminatorName), optionalClass: optionalClass, + nativeResultJavaType: javaType, toValue: .placeholder, resultName: resultName ) @@ -1319,10 +1320,13 @@ extension JNISwift2JavaGenerator { genericParameters: genericParameters, genericRequirements: genericRequirements, ) - let wrappedResultVariable = JavaNativeConversionStep.placeToVar( - wrappedValueResult.conversion, - name: resultName + "Wrapped" - ) + + // FIXME: Using NativeJavaTranslation results directly is more accurate + let nativeResultJavaType: JavaType = if wrappedValueResult.outParameters.isEmpty { + .long + } else { + .void + } let returnType = JavaType.class(package: nil, name: "Optional", typeParameters: [javaType]) return TranslatedResult( @@ -1334,7 +1338,8 @@ extension JNISwift2JavaGenerator { conversion: .toOptionalFromIndirectReturn( discriminatorName: .constant(discriminatorName), optionalClass: "Optional", - toValue: wrappedResultVariable, + nativeResultJavaType: nativeResultJavaType, + toValue: wrappedValueResult.conversion, resultName: resultName ) ) @@ -1759,16 +1764,22 @@ extension JNISwift2JavaGenerator { static func toOptionalFromIndirectReturn( discriminatorName: JavaNativeConversionStep, optionalClass: String, + nativeResultJavaType: JavaType, toValue valueConversion: JavaNativeConversionStep, resultName: String ) -> JavaNativeConversionStep { - .ternary( - .equals( - .subscriptOf(discriminatorName, arguments: [.constant("0")]), - .constant("1") - ), - thenExp: .method(.constant(optionalClass), function: "of", arguments: [valueConversion]), - elseExp: .method(.constant(optionalClass), function: "empty") + .aggregate( + variable: nativeResultJavaType.isVoid ? nil : (name: "\(resultName)$", type: nativeResultJavaType), + [ + .ternary( + .equals( + .subscriptOf(discriminatorName, arguments: [.constant("0")]), + .constant("1") + ), + thenExp: .method(.constant(optionalClass), function: "of", arguments: [valueConversion]), + elseExp: .method(.constant(optionalClass), function: "empty") + ), + ] ) } @@ -1958,14 +1969,12 @@ extension JNISwift2JavaGenerator { return #"Objects.requireNonNull(\#(inner), "\#(message)")"# case .tupleFromOutParams(let tupleClassName, let elements): - // Execute the native call first (the placeholder is the downcall expression) - printer.print("\(placeholder);") var args: [String] = [] for element in elements { let converted = element.elementConversion.render(&printer, "\(element.outParamName)[0]") args.append(converted) } - return "\(tupleClassName)(\(args.joined(separator: ", ")))" + return "new \(tupleClassName)<>(\(args.joined(separator: ", ")))" case .placeToVar(let inner, let name): let inner = inner.render(&printer, placeholder) From 63e7234db64dd83929ef7f6b931666b59434c8e4 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 30 Mar 2026 18:01:07 +0900 Subject: [PATCH 07/12] Cleanup code --- ...ISwift2JavaGenerator+JavaTranslation.swift | 79 ++++--------------- ...wift2JavaGenerator+NativeTranslation.swift | 26 ------ 2 files changed, 14 insertions(+), 91 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 723713cf..773473bc 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -698,20 +698,14 @@ extension JNISwift2JavaGenerator { javaType: translatedFutureType, annotations: result.annotations, outParameters: result.outParameters + [futureOutParameter], - conversion: .aggregate( - variable: nil, - [ -// .print(.placeholder), // Make the downcall - .method( - .constant("future$"), - function: "thenApply", - arguments: [ - .lambda( - args: ["futureResult$"], - body: .replacingPlaceholder(result.conversion, placeholder: "futureResult$") - ) - ] - ), + conversion: .method( + .constant("future$"), + function: "thenApply", + arguments: [ + .lambda( + args: ["futureResult$"], + body: .replacingPlaceholder(result.conversion, placeholder: "futureResult$") + ) ] ) ) @@ -985,18 +979,12 @@ extension JNISwift2JavaGenerator { javaType: javaType, annotations: resultAnnotations, outParameters: [.init(name: resultName, type: ._OutSwiftGenericInstance, allocation: .new)], - conversion: .aggregate( - variable: nil, - [ -// .print(.placeholder), // make the downcall - .wrapMemoryAddressUnsafe( - .commaSeparated([ - .member(.constant(resultName), field: "selfPointer"), - .member(.constant(resultName), field: "selfTypePointer"), - ]), - javaType - ), - ] + conversion: .wrapMemoryAddressUnsafe( + .commaSeparated([ + .member(.constant(resultName), field: "selfPointer"), + .member(.constant(resultName), field: "selfTypePointer"), + ]), + javaType ) ) } else { @@ -1165,12 +1153,6 @@ extension JNISwift2JavaGenerator { genericRequirements: genericRequirements ) -// let (javaType, elementConversion) = try translateTupleElementResult( -// type: element.type, -// genericParameters: genericParameters, -// genericRequirements: genericRequirements -// ) - elementOutParamNames.append(outParamName) if elementResult.outParameters.isEmpty { // Use the array as an output parameter instead of a return value. @@ -1202,39 +1184,6 @@ extension JNISwift2JavaGenerator { ) } -// /// Translate a single element type for tuple results on the Java side. -// private func translateTupleElementResult( -// type: SwiftType, -// genericParameters: [SwiftGenericParameterDeclaration], -// genericRequirements: [SwiftGenericRequirement] -// ) throws -> (JavaType, JavaNativeConversionStep) { -// switch type { -// case .nominal(let nominalType): -// if let knownType = nominalType.nominalTypeDecl.knownTypeKind { -// guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { -// throw JavaTranslationError.unsupportedSwiftType(type) -// } -// // Primitives: just read from array -// return (javaType, .placeholder) -// } -// -// guard !nominalType.isSwiftJavaWrapper else { -// throw JavaTranslationError.unsupportedSwiftType(type) -// } -// -// let javaType = try translateGenericTypeParameter( -// type, -// genericParameters: genericParameters, -// genericRequirements: genericRequirements -// ) -// // JExtract class: wrap memory address -// return (.long, .constructSwiftValue(.placeholder, javaType)) -// -// default: -// throw JavaTranslationError.unsupportedSwiftType(type) -// } -// } - func translateOptionalResult( wrappedType swiftType: SwiftType, resultName: String, diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index 9d44c7d0..409ad160 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -792,7 +792,6 @@ extension JNISwift2JavaGenerator { let outParamName = "\(resultName)_\(idx)$" // Get the JNI type for this element -// let elementResult = try translateElementResult(type: element.type) let elementResult = try translate( swiftResult: .init(convention: .indirect, type: element.type), resultName: outParamName @@ -825,31 +824,6 @@ extension JNISwift2JavaGenerator { ) } -// /// Translate a single element type for use in tuple result destructuring. -// private func translateElementResult(type: SwiftType) throws -> (javaType: JavaType, conversion: NativeSwiftConversionStep) { -// switch type { -// case .nominal(let nominalType): -// if let knownType = nominalType.nominalTypeDecl.knownTypeKind { -// guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config), -// javaType.implementsJavaValue -// else { -// throw JavaTranslationError.unsupportedSwiftType(type) -// } -// return (javaType: javaType, conversion: .getJNIValue(.placeholder)) -// } -// -// guard !nominalType.isSwiftJavaWrapper else { -// throw JavaTranslationError.unsupportedSwiftType(type) -// } -// -// // JExtract class: allocate and return pointer -// return (javaType: .long, conversion: .getJNIValue(.allocateSwiftValue(.placeholder, name: "element", swiftType: type))) -// -// default: -// throw JavaTranslationError.unsupportedSwiftType(type) -// } -// } - func translateArrayResult( elementType: SwiftType, resultName: String From 508ce5e39da1f48ec7bbcd1192d25414d6e3da59 Mon Sep 17 00:00:00 2001 From: Iceman Date: Mon, 30 Mar 2026 18:14:28 +0900 Subject: [PATCH 08/12] Update fixme comments --- .../JNI/JNISwift2JavaGenerator+JavaTranslation.swift | 5 +++-- .../JNI/JNISwift2JavaGenerator+NativeTranslation.swift | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 773473bc..48cd769f 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -1154,8 +1154,9 @@ extension JNISwift2JavaGenerator { ) elementOutParamNames.append(outParamName) + // FIXME: More accurate determination of whether the result is direct or indirect if elementResult.outParameters.isEmpty { - // Use the array as an output parameter instead of a return value. + // Convert direct result to indirect result let arrayType: JavaType = .array(elementResult.javaType) outParameters.append( OutParameter(name: outParamName, type: arrayType, allocation: .newArray(elementResult.javaType, size: 1)) @@ -1270,7 +1271,7 @@ extension JNISwift2JavaGenerator { genericRequirements: genericRequirements, ) - // FIXME: Using NativeJavaTranslation results directly is more accurate + // FIXME: More accurate JavaType using NativeJavaTranslation results directly let nativeResultJavaType: JavaType = if wrappedValueResult.outParameters.isEmpty { .long } else { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index 409ad160..6b010794 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -797,8 +797,9 @@ extension JNISwift2JavaGenerator { resultName: outParamName ) + // FIXME: More accurate determination of whether the result is direct or indirect if elementResult.outParameters.isEmpty { - // Use the array as an output parameter instead of a return value. + // Convert direct result to indirect result outParameters.append( JavaParameter(name: outParamName, type: .array(elementResult.javaType)) ) From 94a8ce459aef173b7da5bb13b5969e7e3079ffea Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 31 Mar 2026 10:23:20 +0900 Subject: [PATCH 09/12] swift format --- .../Sources/MySwiftLibrary/GenericType.swift | 4 +- .../Convenience/JavaType+Extensions.swift | 20 +- ...ISwift2JavaGenerator+JavaTranslation.swift | 24 +- .../JNI/JNIGenericCombinationTests.swift | 242 +++++++++--------- 4 files changed, 146 insertions(+), 144 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift index c1e3e947..d98faf90 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift @@ -58,7 +58,7 @@ public enum MyIDs { public static func makeDoubleIDOptional(_ value: Double) -> MyID? { MyID(value) } - + public static func takeDoubleValueOptional(from id: MyID?) -> Double? { id?.rawValue } @@ -70,7 +70,7 @@ public enum MyIDs { public static func makeOptionalIntID(_ value: Int?) -> MyID { MyID(value) } - + public static func takeOptionalIntValue(from id: MyID) -> Int? { id.rawValue } diff --git a/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift b/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift index 7fd2bdd1..4d3306e4 100644 --- a/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift +++ b/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift @@ -176,17 +176,17 @@ extension JavaType { case .javaLangString: return "String" case .class(let package, let name, let typeParameters): let packageClause: String = - if let package { - "\(package)." - } else { - "" - } + if let package { + "\(package)." + } else { + "" + } let genericClause: String = - if !typeParameters.isEmpty { - "<\(typeParameters.map(\.boxedName).joined(separator: ", "))>" - } else { - "" - } + if !typeParameters.isEmpty { + "<\(typeParameters.map(\.boxedName).joined(separator: ", "))>" + } else { + "" + } return "\(packageClause)\(name)\(genericClause)" case .array: return description } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 48cd769f..41a4fbae 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -1272,11 +1272,12 @@ extension JNISwift2JavaGenerator { ) // FIXME: More accurate JavaType using NativeJavaTranslation results directly - let nativeResultJavaType: JavaType = if wrappedValueResult.outParameters.isEmpty { - .long - } else { - .void - } + let nativeResultJavaType: JavaType = + if wrappedValueResult.outParameters.isEmpty { + .long + } else { + .void + } let returnType = JavaType.class(package: nil, name: "Optional", typeParameters: [javaType]) return TranslatedResult( @@ -1728,7 +1729,7 @@ extension JNISwift2JavaGenerator { ), thenExp: .method(.constant(optionalClass), function: "of", arguments: [valueConversion]), elseExp: .method(.constant(optionalClass), function: "empty") - ), + ) ] ) } @@ -1797,11 +1798,12 @@ extension JNISwift2JavaGenerator { guard case .class(_, let className, let typeParameters) = javaType else { fatalError("\(javaType) is not class.") } - let genericClause = if !typeParameters.isEmpty { - "<\(typeParameters.map(\.description).joined(separator: ", "))>" - } else { - "" - } + let genericClause = + if !typeParameters.isEmpty { + "<\(typeParameters.map(\.description).joined(separator: ", "))>" + } else { + "" + } return "\(className).\(genericClause)wrapMemoryAddressUnsafe(\(inner), swiftArena)" case .constructJavaClass(let inner, let javaType): diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift index cb43f632..897a2103 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift @@ -54,21 +54,21 @@ struct JNIGenericCombinationTests { .java, detectChunkByInitialLines: 2, expectedChunks: [ - """ - public static Optional> makeStringIDOptional(java.lang.String value, SwiftArena swiftArena) { - byte[] result$_discriminator$ = new byte[1]; - org.swift.swiftkit.core._OutSwiftGenericInstance resultWrapped$ = new org.swift.swiftkit.core._OutSwiftGenericInstance(); - SwiftModule.$makeStringIDOptional(value, result$_discriminator$, resultWrapped$); - return (result$_discriminator$[0] == 1) ? Optional.of(MyID.wrapMemoryAddressUnsafe(resultWrapped$.selfPointer, resultWrapped$.selfTypePointer, swiftArena)) : Optional.empty(); - } - """, - """ - private static native void $makeStringIDOptional(java.lang.String value, byte[] result_discriminator$, org.swift.swiftkit.core._OutSwiftGenericInstance resultWrappedOut); - """, + """ + public static Optional> makeStringIDOptional(java.lang.String value, SwiftArena swiftArena) { + byte[] result$_discriminator$ = new byte[1]; + org.swift.swiftkit.core._OutSwiftGenericInstance resultWrapped$ = new org.swift.swiftkit.core._OutSwiftGenericInstance(); + SwiftModule.$makeStringIDOptional(value, result$_discriminator$, resultWrapped$); + return (result$_discriminator$[0] == 1) ? Optional.of(MyID.wrapMemoryAddressUnsafe(resultWrapped$.selfPointer, resultWrapped$.selfTypePointer, swiftArena)) : Optional.empty(); + } + """, + """ + private static native void $makeStringIDOptional(java.lang.String value, byte[] result_discriminator$, org.swift.swiftkit.core._OutSwiftGenericInstance resultWrappedOut); + """, ] ) } - + @Test func returnFuncSwift() throws { try assertOutput( @@ -77,33 +77,33 @@ struct JNIGenericCombinationTests { .swift, detectChunkByInitialLines: 2, expectedChunks: [ - """ - @_cdecl("Java_com_example_swift_SwiftModule__00024makeStringIDOptional__Ljava_lang_String_2_3BLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2") - public func Java_com_example_swift_SwiftModule__00024makeStringIDOptional__Ljava_lang_String_2_3BLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2(environment: UnsafeMutablePointer!, thisClass: jclass, value: jstring?, result_discriminator$: jbyteArray?, resultWrappedOut: jobject?) { - if let innerResult$ = SwiftModule.makeStringIDOptional(String(fromJNI: value, in: environment)) { - let resultWrapped$ = UnsafeMutablePointer>.allocate(capacity: 1) - resultWrapped$.initialize(to: innerResult$) - let resultWrappedBits$ = Int64(Int(bitPattern: resultWrapped$)) - do { - environment.interface.SetLongField(environment, resultWrappedOut, _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, resultWrappedBits$.getJNIValue(in: environment)) - let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self) - let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer)) - environment.interface.SetLongField(environment, resultWrappedOut, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024makeStringIDOptional__Ljava_lang_String_2_3BLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2") + public func Java_com_example_swift_SwiftModule__00024makeStringIDOptional__Ljava_lang_String_2_3BLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2(environment: UnsafeMutablePointer!, thisClass: jclass, value: jstring?, result_discriminator$: jbyteArray?, resultWrappedOut: jobject?) { + if let innerResult$ = SwiftModule.makeStringIDOptional(String(fromJNI: value, in: environment)) { + let resultWrapped$ = UnsafeMutablePointer>.allocate(capacity: 1) + resultWrapped$.initialize(to: innerResult$) + let resultWrappedBits$ = Int64(Int(bitPattern: resultWrapped$)) + do { + environment.interface.SetLongField(environment, resultWrappedOut, _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, resultWrappedBits$.getJNIValue(in: environment)) + let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self) + let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer)) + environment.interface.SetLongField(environment, resultWrappedOut, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) + } + var flag$ = Int8(1) + environment.interface.SetByteArrayRegion(environment, result_discriminator$, 0, 1, &flag$) + } + else { + var flag$ = Int8(0) + environment.interface.SetByteArrayRegion(environment, result_discriminator$, 0, 1, &flag$) } - var flag$ = Int8(1) - environment.interface.SetByteArrayRegion(environment, result_discriminator$, 0, 1, &flag$) - } - else { - var flag$ = Int8(0) - environment.interface.SetByteArrayRegion(environment, result_discriminator$, 0, 1, &flag$) + return } - return - } - """, + """ ] ) } - + @Test func argumentFuncJava() throws { try assertOutput( @@ -112,18 +112,18 @@ struct JNIGenericCombinationTests { .java, detectChunkByInitialLines: 2, expectedChunks: [ - """ - public static void takeStringIDOptional(Optional> value) { - SwiftModule.$takeStringIDOptional(value.map(MyID::$memoryAddress).orElse(0L)); - } - """, - """ - private static native void $takeStringIDOptional(long value); - """, + """ + public static void takeStringIDOptional(Optional> value) { + SwiftModule.$takeStringIDOptional(value.map(MyID::$memoryAddress).orElse(0L)); + } + """, + """ + private static native void $takeStringIDOptional(long value); + """, ] ) } - + @Test func argumentFuncSwift() throws { try assertOutput( @@ -132,14 +132,14 @@ struct JNIGenericCombinationTests { .swift, detectChunkByInitialLines: 2, expectedChunks: [ - """ - @_cdecl("Java_com_example_swift_SwiftModule__00024takeStringIDOptional__J") - public func Java_com_example_swift_SwiftModule__00024takeStringIDOptional__J(environment: UnsafeMutablePointer!, thisClass: jclass, value: jlong) { - let valueBits$ = Int(Int64(fromJNI: value, in: environment)) - let value$ = UnsafeMutablePointer>(bitPattern: valueBits$) - SwiftModule.takeStringIDOptional(value$?.pointee) - } - """, + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024takeStringIDOptional__J") + public func Java_com_example_swift_SwiftModule__00024takeStringIDOptional__J(environment: UnsafeMutablePointer!, thisClass: jclass, value: jlong) { + let valueBits$ = Int(Int64(fromJNI: value, in: environment)) + let value$ = UnsafeMutablePointer>(bitPattern: valueBits$) + SwiftModule.takeStringIDOptional(value$?.pointee) + } + """ ] ) } @@ -170,19 +170,19 @@ struct JNIGenericCombinationTests { .java, detectChunkByInitialLines: 2, expectedChunks: [ - """ - public static org.swift.swiftkit.core.tuple.Tuple2, MyID> makeIDs(java.lang.String stringValue, long intValue, SwiftArena swiftArena) { - org.swift.swiftkit.core._OutSwiftGenericInstance result_0$ = new org.swift.swiftkit.core._OutSwiftGenericInstance(); - org.swift.swiftkit.core._OutSwiftGenericInstance result_1$ = new org.swift.swiftkit.core._OutSwiftGenericInstance(); - SwiftModule.$makeIDs(stringValue, intValue, result_0$, result_1$); - var result_0 = MyID.wrapMemoryAddressUnsafe(result_0$.selfPointer, result_0$.selfTypePointer, swiftArena); - var result_1 = MyID.wrapMemoryAddressUnsafe(result_1$.selfPointer, result_1$.selfTypePointer, swiftArena); - return new org.swift.swiftkit.core.tuple.Tuple2<>(result_0, result_1); - } - """, - """ - private static native void $makeIDs(java.lang.String stringValue, long intValue, org.swift.swiftkit.core._OutSwiftGenericInstance result_0$Out, org.swift.swiftkit.core._OutSwiftGenericInstance result_1$Out); - """, + """ + public static org.swift.swiftkit.core.tuple.Tuple2, MyID> makeIDs(java.lang.String stringValue, long intValue, SwiftArena swiftArena) { + org.swift.swiftkit.core._OutSwiftGenericInstance result_0$ = new org.swift.swiftkit.core._OutSwiftGenericInstance(); + org.swift.swiftkit.core._OutSwiftGenericInstance result_1$ = new org.swift.swiftkit.core._OutSwiftGenericInstance(); + SwiftModule.$makeIDs(stringValue, intValue, result_0$, result_1$); + var result_0 = MyID.wrapMemoryAddressUnsafe(result_0$.selfPointer, result_0$.selfTypePointer, swiftArena); + var result_1 = MyID.wrapMemoryAddressUnsafe(result_1$.selfPointer, result_1$.selfTypePointer, swiftArena); + return new org.swift.swiftkit.core.tuple.Tuple2<>(result_0, result_1); + } + """, + """ + private static native void $makeIDs(java.lang.String stringValue, long intValue, org.swift.swiftkit.core._OutSwiftGenericInstance result_0$Out, org.swift.swiftkit.core._OutSwiftGenericInstance result_1$Out); + """, ] ) } @@ -195,31 +195,31 @@ struct JNIGenericCombinationTests { .swift, detectChunkByInitialLines: 4, expectedChunks: [ - """ - @_cdecl("Java_com_example_swift_SwiftModule__00024makeIDs__Ljava_lang_String_2JLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2Lorg_swift_swiftkit_core__1OutSwiftGenericInstance_2") - public func Java_com_example_swift_SwiftModule__00024makeIDs__Ljava_lang_String_2JLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2Lorg_swift_swiftkit_core__1OutSwiftGenericInstance_2(environment: UnsafeMutablePointer!, thisClass: jclass, stringValue: jstring?, intValue: jlong, result_0$Out: jobject?, result_1$Out: jobject?) { - let tupleResult$ = SwiftModule.makeIDs(String(fromJNI: stringValue, in: environment), Int64(fromJNI: intValue, in: environment)) - let result_0$$ = UnsafeMutablePointer>.allocate(capacity: 1) - result_0$$.initialize(to: tupleResult$.0) - let result_0$Bits$ = Int64(Int(bitPattern: result_0$$)) - do { - environment.interface.SetLongField(environment, result_0$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, result_0$Bits$.getJNIValue(in: environment)) - let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self) - let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer)) - environment.interface.SetLongField(environment, result_0$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) - } - let result_1$$ = UnsafeMutablePointer>.allocate(capacity: 1) - result_1$$.initialize(to: tupleResult$.1) - let result_1$Bits$ = Int64(Int(bitPattern: result_1$$)) - do { - environment.interface.SetLongField(environment, result_1$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, result_1$Bits$.getJNIValue(in: environment)) - let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self) - let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer)) - environment.interface.SetLongField(environment, result_1$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) + """ + @_cdecl("Java_com_example_swift_SwiftModule__00024makeIDs__Ljava_lang_String_2JLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2Lorg_swift_swiftkit_core__1OutSwiftGenericInstance_2") + public func Java_com_example_swift_SwiftModule__00024makeIDs__Ljava_lang_String_2JLorg_swift_swiftkit_core__1OutSwiftGenericInstance_2Lorg_swift_swiftkit_core__1OutSwiftGenericInstance_2(environment: UnsafeMutablePointer!, thisClass: jclass, stringValue: jstring?, intValue: jlong, result_0$Out: jobject?, result_1$Out: jobject?) { + let tupleResult$ = SwiftModule.makeIDs(String(fromJNI: stringValue, in: environment), Int64(fromJNI: intValue, in: environment)) + let result_0$$ = UnsafeMutablePointer>.allocate(capacity: 1) + result_0$$.initialize(to: tupleResult$.0) + let result_0$Bits$ = Int64(Int(bitPattern: result_0$$)) + do { + environment.interface.SetLongField(environment, result_0$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, result_0$Bits$.getJNIValue(in: environment)) + let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self) + let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer)) + environment.interface.SetLongField(environment, result_0$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) + } + let result_1$$ = UnsafeMutablePointer>.allocate(capacity: 1) + result_1$$.initialize(to: tupleResult$.1) + let result_1$Bits$ = Int64(Int(bitPattern: result_1$$)) + do { + environment.interface.SetLongField(environment, result_1$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfPointer, result_1$Bits$.getJNIValue(in: environment)) + let metadataPointer = unsafeBitCast(MyID.self, to: UnsafeRawPointer.self) + let metadataPointerBits$ = Int64(Int(bitPattern: metadataPointer)) + environment.interface.SetLongField(environment, result_1$Out, _JNIMethodIDCache._OutSwiftGenericInstance.selfTypePointer, metadataPointerBits$.getJNIValue(in: environment)) + } + return } - return - } - """, + """ ] ) } @@ -232,17 +232,17 @@ struct JNIGenericCombinationTests { .java, detectChunkByInitialLines: 2, expectedChunks: [ - """ - public static org.swift.swiftkit.core.tuple.Tuple2 takeValues(org.swift.swiftkit.core.tuple.Tuple2, MyID> tuple) { - java.lang.String[] result_0$ = new java.lang.String[1]; - long[] result_1$ = new long[1]; - SwiftModule.$takeValues(tuple.$0.$memoryAddress(), tuple.$1.$memoryAddress(), result_0$, result_1$); - return new org.swift.swiftkit.core.tuple.Tuple2<>(result_0$[0], result_1$[0]); - } - """, - """ - private static native void $takeValues(long tuple_0, long tuple_1, java.lang.String[] result_0$, long[] result_1$); - """, + """ + public static org.swift.swiftkit.core.tuple.Tuple2 takeValues(org.swift.swiftkit.core.tuple.Tuple2, MyID> tuple) { + java.lang.String[] result_0$ = new java.lang.String[1]; + long[] result_1$ = new long[1]; + SwiftModule.$takeValues(tuple.$0.$memoryAddress(), tuple.$1.$memoryAddress(), result_0$, result_1$); + return new org.swift.swiftkit.core.tuple.Tuple2<>(result_0$[0], result_1$[0]); + } + """, + """ + private static native void $takeValues(long tuple_0, long tuple_1, java.lang.String[] result_0$, long[] result_1$); + """, ] ) } @@ -255,29 +255,29 @@ struct JNIGenericCombinationTests { .swift, detectChunkByInitialLines: 2, expectedChunks: [ - #""" - @_cdecl("Java_com_example_swift_SwiftModule__00024takeValues__JJ_3Ljava_lang_String_2_3J") - public func Java_com_example_swift_SwiftModule__00024takeValues__JJ_3Ljava_lang_String_2_3J(environment: UnsafeMutablePointer!, thisClass: jclass, tuple_0: jlong, tuple_1: jlong, result_0$: jobjectArray?, result_1$: jlongArray?) { - assert(tuple_0 != 0, "tuple_0 memory address was null") - let tuple_0Bits$ = Int(Int64(fromJNI: tuple_0, in: environment)) - let tuple_0$ = UnsafeMutablePointer>(bitPattern: tuple_0Bits$) - guard let tuple_0$ else { - fatalError("tuple_0 memory address was null in call to \(#function)!") - } - assert(tuple_1 != 0, "tuple_1 memory address was null") - let tuple_1Bits$ = Int(Int64(fromJNI: tuple_1, in: environment)) - let tuple_1$ = UnsafeMutablePointer>(bitPattern: tuple_1Bits$) - guard let tuple_1$ else { - fatalError("tuple_1 memory address was null in call to \(#function)!") + #""" + @_cdecl("Java_com_example_swift_SwiftModule__00024takeValues__JJ_3Ljava_lang_String_2_3J") + public func Java_com_example_swift_SwiftModule__00024takeValues__JJ_3Ljava_lang_String_2_3J(environment: UnsafeMutablePointer!, thisClass: jclass, tuple_0: jlong, tuple_1: jlong, result_0$: jobjectArray?, result_1$: jlongArray?) { + assert(tuple_0 != 0, "tuple_0 memory address was null") + let tuple_0Bits$ = Int(Int64(fromJNI: tuple_0, in: environment)) + let tuple_0$ = UnsafeMutablePointer>(bitPattern: tuple_0Bits$) + guard let tuple_0$ else { + fatalError("tuple_0 memory address was null in call to \(#function)!") + } + assert(tuple_1 != 0, "tuple_1 memory address was null") + let tuple_1Bits$ = Int(Int64(fromJNI: tuple_1, in: environment)) + let tuple_1$ = UnsafeMutablePointer>(bitPattern: tuple_1Bits$) + guard let tuple_1$ else { + fatalError("tuple_1 memory address was null in call to \(#function)!") + } + let tupleResult$ = SwiftModule.takeValues(from: (tuple_0$.pointee, tuple_1$.pointee)) + let element_0_jni$ = tupleResult$.0.getJNILocalRefValue(in: environment) + environment.interface.SetObjectArrayElement(environment, result_0$, 0, element_0_jni$) + var element_1_jni$ = tupleResult$.1.getJNILocalRefValue(in: environment) + environment.interface.SetLongArrayRegion(environment, result_1$, 0, 1, &element_1_jni$) + return } - let tupleResult$ = SwiftModule.takeValues(from: (tuple_0$.pointee, tuple_1$.pointee)) - let element_0_jni$ = tupleResult$.0.getJNILocalRefValue(in: environment) - environment.interface.SetObjectArrayElement(environment, result_0$, 0, element_0_jni$) - var element_1_jni$ = tupleResult$.1.getJNILocalRefValue(in: environment) - environment.interface.SetLongArrayRegion(environment, result_1$, 0, 1, &element_1_jni$) - return - } - """#, + """# ] ) } From a57298d013ef86b48605f218c7f419bcc7443722 Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 31 Mar 2026 16:31:40 +0900 Subject: [PATCH 10/12] Delete comment outed functions --- .../Sources/MySwiftLibrary/GenericType.swift | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift index d98faf90..eb0bed9a 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift @@ -47,14 +47,6 @@ public enum MyIDs { (tuple.0.rawValue, tuple.1.rawValue) } - // public static func makeBoolIDArray(_ value: Bool, length: Int) -> [MyID] { - // Array(repeating: MyID(value), count: length) - // } - - // public static func takeBoolValues(from ids: [MyID]) -> [Bool] { - // ids.map { $0.rawValue } - // } - public static func makeDoubleIDOptional(_ value: Double) -> MyID? { MyID(value) } From 277fe9423f6dbafe0ebb527bccfa53206ddcd5c8 Mon Sep 17 00:00:00 2001 From: Iceman Date: Tue, 31 Mar 2026 16:33:05 +0900 Subject: [PATCH 11/12] grammar fix --- .../JExtractSwiftLib/Convenience/JavaType+Extensions.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift b/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift index 4d3306e4..68059eed 100644 --- a/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift +++ b/Sources/JExtractSwiftLib/Convenience/JavaType+Extensions.swift @@ -175,7 +175,7 @@ extension JavaType { case .void: return "Void" case .javaLangString: return "String" case .class(let package, let name, let typeParameters): - let packageClause: String = + let packagePart: String = if let package { "\(package)." } else { @@ -187,7 +187,7 @@ extension JavaType { } else { "" } - return "\(packageClause)\(name)\(genericClause)" + return "\(packagePart)\(name)\(genericClause)" case .array: return description } } From e9a216cb706282b79351884140153e4002e4a74e Mon Sep 17 00:00:00 2001 From: Iceman Date: Wed, 1 Apr 2026 11:04:40 +0900 Subject: [PATCH 12/12] Avoid using optional int for example code --- .../Sources/MySwiftLibrary/GenericType.swift | 4 ++-- .../src/test/java/com/example/swift/GenericTypeTest.java | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift index eb0bed9a..0968489e 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/GenericType.swift @@ -59,11 +59,11 @@ public enum MyIDs { value.rawValue } - public static func makeOptionalIntID(_ value: Int?) -> MyID { + public static func makeOptionalStringID(_ value: String?) -> MyID { MyID(value) } - public static func takeOptionalIntValue(from id: MyID) -> Int? { + public static func takeOptionalStringValue(from id: MyID) -> String? { id.rawValue } } 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 3592bcdb..18e60b59 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/GenericTypeTest.java @@ -40,17 +40,14 @@ void genericTypeValueRoundtrip() { assertEquals("Java", MyIDs.takeValuesFromTuple(ids).$0); assertEquals(42, MyIDs.takeValuesFromTuple(ids).$1); - // MyID[] boolIds = MyIDs.makeBoolIDArray(true, 3, arena); - // assertEquals(List.of(true, true, true), MyIDs.takeBoolValuesFromArray(boolIds)); - Optional> doubleIdOptional = MyIDs.makeDoubleIDOptional(42.195, arena); assertTrue(doubleIdOptional.isPresent()); assertEquals(42.195, MyIDs.takeDoubleValueOptional(doubleIdOptional).getAsDouble()); assertEquals(42.195, MyIDs.takeDoubleValue(doubleIdOptional.get())); // ensure wrapped value is alive - MyID> optionalIntId = MyIDs.makeOptionalIntID(OptionalLong.of(42L), arena); - assertEquals("Optional(42)", optionalIntId.getDescription()); - assertEquals(42, MyIDs.takeOptionalIntValue(optionalIntId).getAsLong()); + MyID> optionalStringId = MyIDs.makeOptionalStringID(Optional.of("Java"), arena); + assertEquals("Optional(\"Java\")", optionalStringId.getDescription()); + assertEquals("Java", MyIDs.takeOptionalStringValue(optionalStringId).get()); } }