diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Optionals.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Optionals.swift index 3c08cb56..b5842301 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Optionals.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Optionals.swift @@ -91,3 +91,11 @@ public func multipleOptionals( ) -> Int64? { 1 } + +public func optionalTuple() -> (Int64, String)? { + (42, "hello") +} + +public func optionalTuple2() -> (Int64?, Alignment?)? { + (42, .horizontal) +} diff --git a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Tuples.swift b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Tuples.swift index b3038f97..d92ad184 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Tuples.swift +++ b/Samples/SwiftJavaExtractJNISampleApp/Sources/MySwiftLibrary/Tuples.swift @@ -45,3 +45,7 @@ public func makeBigTuple() -> ( public func namedByteArrayTuple() -> (name: [UInt8], another: [UInt8]) { (name: [1, 2, 3], another: [4, 5]) } + +public func genericTypeTuple() -> (MyID, Alignment) { + (MyID(1.23), .horizontal) +} diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/OptionalsTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/OptionalsTest.java index 771cffc3..6db74849 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/OptionalsTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/OptionalsTest.java @@ -143,4 +143,30 @@ void optionalThrows() { assertEquals("swiftError", exception.getMessage()); } } -} \ No newline at end of file + + @Test + void optionalTuple() { + var result = MySwiftLibrary.optionalTuple(); + assertDoesNotThrow(() -> { + var resultUnwrapped = result.orElseThrow(); + assertEquals(42, resultUnwrapped.$0); + assertEquals("hello", resultUnwrapped.$1); + }); + } + + @Test + void optionalTuple2() { + try (var arena = SwiftArena.ofConfined()) { + var result = MySwiftLibrary.optionalTuple2(arena); + assertDoesNotThrow(() -> { + var resultUnwrapped = result.orElseThrow(); + assertDoesNotThrow(() -> { + assertEquals(42, resultUnwrapped.$0.orElseThrow()); + }); + assertDoesNotThrow(() -> { + assertEquals(Alignment.Discriminator.HORIZONTAL, resultUnwrapped.$1.orElseThrow().getDiscriminator()); + }); + }); + } + } +} diff --git a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/TupleTest.java b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/TupleTest.java index 1a110eb2..cc89e124 100644 --- a/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/TupleTest.java +++ b/Samples/SwiftJavaExtractJNISampleApp/src/test/java/com/example/swift/TupleTest.java @@ -15,6 +15,7 @@ package com.example.swift; import org.junit.jupiter.api.Test; +import org.swift.swiftkit.core.SwiftArena; import org.swift.swiftkit.core.tuple.Tuple2; import org.swift.swiftkit.core.tuple.Tuple3; import org.swift.swiftkit.core.tuple.Tuple16; @@ -95,4 +96,13 @@ void namedByteArrayTuple() { assertArrayEquals(new byte[] { 1, 2, 3 }, (byte[]) result.$0); assertArrayEquals(new byte[] { 4, 5 }, (byte[]) result.$1); } + + @Test + void genericTypeTuple() { + try (var arena = SwiftArena.ofConfined()) { + var result = MySwiftLibrary.genericTypeTuple(arena); + assertEquals("1.23", result.$0.getDescription()); + assertEquals(Alignment.Discriminator.HORIZONTAL, result.$1.getDiscriminator()); + } + } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index f7b6e2d2..d6b55e7c 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -904,46 +904,35 @@ extension JNISwift2JavaGenerator { switch swiftType { case .nominal(let nominalType): - if let knownType = nominalType.nominalTypeDecl.knownTypeKind { + if let knownType = nominalType.asKnownType { switch knownType { - case .optional: - guard let genericArgs = nominalType.genericArguments, genericArgs.count == 1 else { - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } + case .optional(let wrapped): return try translateOptionalResult( - wrappedType: genericArgs[0], + wrappedType: wrapped, + methodName: methodName, resultName: resultName, genericParameters: genericParameters, genericRequirements: genericRequirements, ) - case .array: - guard let elementType = nominalType.genericArguments?.first else { - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } + case .array(let element): return try translateArrayResult( - elementType: elementType, + elementType: element, genericParameters: genericParameters, genericRequirements: genericRequirements, ) - case .dictionary: - guard let genericArgs = nominalType.genericArguments, genericArgs.count == 2 else { - throw JavaTranslationError.dictionaryRequiresKeyAndValueTypes(swiftType) - } + case .dictionary(let key, let value): return try translateDictionaryResult( - keyType: genericArgs[0], - valueType: genericArgs[1], + keyType: key, + valueType: value, genericParameters: genericParameters, genericRequirements: genericRequirements, ) - case .set: - guard let genericArgs = nominalType.genericArguments, genericArgs.count == 1 else { - throw JavaTranslationError.setRequiresElementType(swiftType) - } + case .set(let element): return try translateSetResult( - elementType: genericArgs[0], + elementType: element, genericParameters: genericParameters, genericRequirements: genericRequirements, ) @@ -968,7 +957,7 @@ extension JNISwift2JavaGenerator { ) default: - guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { + guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType.kind, config: self.config) else { throw JavaTranslationError.unsupportedSwiftType(swiftType) } @@ -1046,52 +1035,47 @@ extension JNISwift2JavaGenerator { case .nominal(let nominalType): let nominalTypeName = nominalType.nominalTypeDecl.qualifiedName - if let knownType = nominalType.nominalTypeDecl.knownTypeKind { + if let knownType = nominalType.asKnownType { switch knownType { - case .optional: - guard let genericArgs = nominalType.genericArguments, genericArgs.count == 1 else { - throw JavaTranslationError.unsupportedSwiftType(swiftType) + case .optional(let wrapped): + if let wrappedKnownKind = wrapped.asNominalTypeDeclaration?.knownTypeKind, + let javaType = JNIJavaTypeTranslator.translate(knownType: wrappedKnownKind, config: self.config), + let optionalType = javaType.optionalType + { + return .class(package: nil, name: optionalType) } + let wrappedType = try translateGenericTypeParameter( - genericArgs[0], + wrapped, genericParameters: genericParameters, genericRequirements: genericRequirements, ) - return .class(package: "java.util", name: "Optional", typeParameters: [wrappedType]) + return .optional(wrappedType) - case .array: - guard let elementType = nominalType.genericArguments?.first else { - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } + case .array(let element): let elementJavaType = try translateGenericTypeParameter( - elementType, + element, genericParameters: genericParameters, genericRequirements: genericRequirements, ) return .array(elementJavaType) - case .dictionary: - guard let genericArgs = nominalType.genericArguments, genericArgs.count == 2 else { - throw JavaTranslationError.dictionaryRequiresKeyAndValueTypes(swiftType) - } + case .dictionary(let key, let value): let keyJavaType = try translateGenericTypeParameter( - genericArgs[0], + key, genericParameters: genericParameters, genericRequirements: genericRequirements, ) let valueJavaType = try translateGenericTypeParameter( - genericArgs[1], + value, genericParameters: genericParameters, genericRequirements: genericRequirements, ) return .swiftDictionaryMap(keyJavaType, valueJavaType) - case .set: - guard let genericArgs = nominalType.genericArguments, genericArgs.count == 1 else { - throw JavaTranslationError.setRequiresElementType(swiftType) - } + case .set(let element): let elementJavaType = try translateGenericTypeParameter( - genericArgs[0], + element, genericParameters: genericParameters, genericRequirements: genericRequirements, ) @@ -1110,7 +1094,7 @@ extension JNISwift2JavaGenerator { return .javaUtilUUID default: - guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { + guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType.kind, config: self.config) else { throw JavaTranslationError.unsupportedSwiftType(swiftType) } return javaType.boxedType @@ -1153,7 +1137,17 @@ extension JNISwift2JavaGenerator { } return .class(package: nil, name: generic.name) - case .metatype, .tuple, .function, .existential, .opaque, .composite: + case .tuple(let elements): + let elementJavaTypes = try elements.map { element in + try translateGenericTypeParameter( + element.type, + genericParameters: genericParameters, + genericRequirements: genericRequirements + ) + } + return .tuple(elementTypes: elementJavaTypes) + + case .metatype, .function, .existential, .opaque, .composite: throw JavaTranslationError.unsupportedSwiftType(swiftType) } } @@ -1185,9 +1179,19 @@ extension JNISwift2JavaGenerator { // out names are always ...$N, no need to use real named tuple names here, this is just for the thunk elementOutParamNames.append(outParamName) + outParameters.append(contentsOf: elementResult.outParameters) // FIXME: More accurate determination of whether the result is direct or indirect - if elementResult.outParameters.isEmpty { + // essentially, it should refer to the NativeTranslation result + let isOptional = + switch elementResult.javaType { + case .class(_, let name, let typeParameters) + where (name.starts(with: "Optional") && typeParameters.count == 0) + || (name == "Optional" && typeParameters.count == 1): + true + default: false + } // Optional uses a combination of direct and indirect values. + if isOptional || elementResult.outParameters.isEmpty { // Convert direct result to indirect result. // For most class types (Swift wrapper classes), the JNI native representation // is 'long' (a memory address). However, String is a native JNI reference @@ -1207,7 +1211,6 @@ extension JNISwift2JavaGenerator { ) elementConversions.append(elementResult.conversion) } else { - outParameters.append(contentsOf: elementResult.outParameters) elementConversions.append(.placeToVar(elementResult.conversion, name: "\(resultName)_\(idx)")) } elementJavaTypes.append(elementResult.javaType) @@ -1259,6 +1262,7 @@ extension JNISwift2JavaGenerator { func translateOptionalResult( wrappedType swiftType: SwiftType, + methodName: String, resultName: String, genericParameters: [SwiftGenericParameterDeclaration], genericRequirements: [SwiftGenericRequirement], @@ -1269,108 +1273,88 @@ extension JNISwift2JavaGenerator { switch swiftType { case .nominal(let nominalType): - if let knownType = nominalType.nominalTypeDecl.knownTypeKind { - switch knownType { - case .foundationDate, .essentialsDate: - // Handled as wrapped struct - break - - case .foundationData, .essentialsData: - // Handled as wrapped struct - break - - default: - guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config) else { - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } - - guard let returnType = javaType.optionalType, let optionalClass = javaType.optionalWrapperType else { - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } - - // Check if we can fit the value and a discriminator byte in a primitive. - // so the return JNI value will be (value, discriminator) - if let nextIntergralTypeWithSpaceForByte = javaType.nextIntergralTypeWithSpaceForByte { - return TranslatedResult( - javaType: .class(package: nil, name: returnType), - annotations: parameterAnnotations, - outParameters: [], - conversion: .combinedValueToOptional( - .placeholder, - nextIntergralTypeWithSpaceForByte.javaType, - resultName: resultName, - valueType: javaType, - valueSizeInBytes: nextIntergralTypeWithSpaceForByte.valueBytes, - optionalType: optionalClass, - ), - ) - } else { - // Otherwise, we return the result as normal, but - // use an indirect return for the discriminator. - return TranslatedResult( - javaType: .class(package: nil, name: returnType), - annotations: parameterAnnotations, - outParameters: [ - OutParameter(name: discriminatorName, type: .array(.byte), allocation: .newArray(.byte, size: 1)) - ], - conversion: .toOptionalFromIndirectReturn( - discriminatorName: .constant(discriminatorName), - optionalClass: optionalClass, - nativeResultJavaType: javaType, - toValue: .placeholder, - resultName: resultName, - ), - ) - } + if let knownType = nominalType.nominalTypeDecl.knownTypeKind, + let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config), + let returnType = javaType.optionalType, + let optionalClass = javaType.optionalWrapperType + { + // Check if we can fit the value and a discriminator byte in a primitive. + // so the return JNI value will be (value, discriminator) + if let nextIntergralTypeWithSpaceForByte = javaType.nextIntergralTypeWithSpaceForByte { + return TranslatedResult( + javaType: .class(package: nil, name: returnType), + annotations: parameterAnnotations, + outParameters: [], + conversion: .combinedValueToOptional( + .placeholder, + nextIntergralTypeWithSpaceForByte.javaType, + resultName: resultName, + valueType: javaType, + valueSizeInBytes: nextIntergralTypeWithSpaceForByte.valueBytes, + optionalType: optionalClass, + ), + ) + } else { + // Otherwise, we return the result as normal, but + // use an indirect return for the discriminator. + return TranslatedResult( + javaType: .class(package: nil, name: returnType), + annotations: parameterAnnotations, + outParameters: [ + OutParameter(name: discriminatorName, type: .array(.byte), allocation: .newArray(.byte, size: 1)) + ], + conversion: .toOptionalFromIndirectReturn( + discriminatorName: .constant(discriminatorName), + optionalClass: optionalClass, + nativeResultJavaType: javaType, + toValue: .placeholder, + resultName: resultName, + ), + ) } } - guard !nominalType.isSwiftJavaWrapper else { - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } + default: + break + } - // We assume this is a JExtract class. - let javaType = try translateGenericTypeParameter( - swiftType, - genericParameters: genericParameters, - genericRequirements: genericRequirements, - ) + let wrappedJavaType = try translateGenericTypeParameter( + swiftType, + genericParameters: genericParameters, + genericRequirements: genericRequirements, + ) - let wrappedValueResult = try translate( - swiftResult: SwiftResult(convention: .direct, type: swiftType), - methodName: "", - resultName: resultName + "Wrapped$", - genericParameters: genericParameters, - genericRequirements: genericRequirements, - ) + let wrappedValueResult = try translate( + swiftResult: SwiftResult(convention: .direct, type: swiftType), + methodName: methodName, + resultName: resultName + "Wrapped$", + genericParameters: genericParameters, + genericRequirements: genericRequirements, + ) - // FIXME: More accurate JavaType using NativeJavaTranslation results directly - let nativeResultJavaType: JavaType = - if wrappedValueResult.outParameters.isEmpty { - .long - } else { - .void - } + // FIXME: More accurate JavaType using NativeJavaTranslation results directly + let nativeResultJavaType: JavaType = + if wrappedValueResult.outParameters.isEmpty { + .long + } else { + .void + } - 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: .constant(discriminatorName), - optionalClass: "Optional", - nativeResultJavaType: nativeResultJavaType, - toValue: wrappedValueResult.conversion, - resultName: resultName - ) + let returnType = JavaType.optional(wrappedJavaType) + return TranslatedResult( + javaType: returnType, + annotations: parameterAnnotations, + outParameters: [ + OutParameter(name: discriminatorName, type: .array(.byte), allocation: .newArray(.byte, size: 1)) + ] + wrappedValueResult.outParameters, + conversion: .toOptionalFromIndirectReturn( + discriminatorName: .constant(discriminatorName), + optionalClass: "Optional", + nativeResultJavaType: nativeResultJavaType, + toValue: wrappedValueResult.conversion, + resultName: resultName ) - - default: - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } + ) } func translateArrayParameter( diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index dc565af5..f0d1fa5f 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -522,99 +522,79 @@ extension JNISwift2JavaGenerator { func translateOptionalResult( wrappedType swiftType: SwiftType, methodName: String, - resultName: String = "result" + resultName: String ) throws -> NativeResult { let discriminatorName = "\(resultName)_discriminator$" switch swiftType { case .nominal(let nominalType): - if let knownType = nominalType.nominalTypeDecl.knownTypeKind { - switch knownType { - case .foundationDate, .essentialsDate: - // Handled as wrapped struct - break - - case .foundationData, .essentialsData: - // Handled as wrapped struct - break - - default: - guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config), - javaType.implementsJavaValue - else { - self.logger.debug("Known type \(knownType) is not supported for optional results, skipping.") - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } - - // Check if we can fit the value and a discriminator byte in a primitive. - // so the return JNI value will be (value, discriminator) - if let nextIntergralTypeWithSpaceForByte = javaType.nextIntergralTypeWithSpaceForByte { - return NativeResult( - javaType: nextIntergralTypeWithSpaceForByte.javaType, - conversion: .getJNIValue( - .optionalRaisingWidenIntegerType( - .placeholder, - resultName: resultName, - valueType: javaType, - combinedSwiftType: nextIntergralTypeWithSpaceForByte.swiftType, - valueSizeInBytes: nextIntergralTypeWithSpaceForByte.valueBytes - ) - ), - outParameters: [] - ) - } else { - // Use indirect byte array to store discriminator - - return NativeResult( - javaType: javaType, - conversion: .optionalRaisingIndirectReturn( - .getJNIValue(.placeholder), - returnType: javaType, - discriminatorParameterName: discriminatorName, - placeholderValue: .member( - .constant("\(swiftType)"), - member: "jniPlaceholderValue" - ) - ), - outParameters: [ - JavaParameter(name: discriminatorName, type: .array(.byte)) - ] - ) - } + if let knownType = nominalType.nominalTypeDecl.knownTypeKind, + let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config), + javaType.implementsJavaValue + { + // Check if we can fit the value and a discriminator byte in a primitive. + // so the return JNI value will be (value, discriminator) + if let nextIntergralTypeWithSpaceForByte = javaType.nextIntergralTypeWithSpaceForByte { + return NativeResult( + javaType: nextIntergralTypeWithSpaceForByte.javaType, + conversion: .getJNIValue( + .optionalRaisingWidenIntegerType( + .placeholder, + resultName: resultName, + valueType: javaType, + combinedSwiftType: nextIntergralTypeWithSpaceForByte.swiftType, + valueSizeInBytes: nextIntergralTypeWithSpaceForByte.valueBytes + ) + ), + outParameters: [] + ) + } else { + // Use indirect byte array to store discriminator + return NativeResult( + javaType: javaType, + conversion: .optionalRaisingIndirectReturn( + .getJNIValue(.placeholder), + resultName: "\(resultName)$", + returnType: javaType, + discriminatorParameterName: discriminatorName, + placeholderValue: .member( + .constant("\(swiftType)"), + member: "jniPlaceholderValue" + ) + ), + outParameters: [ + JavaParameter(name: discriminatorName, type: .array(.byte)) + ] + ) } } - - guard !nominalType.isSwiftJavaWrapper else { - // TODO: Should be the same as above - throw JavaTranslationError.unsupportedSwiftType(swiftType) - } - - let wrappedValueResult = try translate( - swiftResult: .init( - convention: .direct, - type: swiftType - ), - methodName: methodName, - resultName: resultName + "Wrapped" - ) - - // Assume JExtract imported class - return NativeResult( - javaType: wrappedValueResult.javaType, - conversion: .optionalRaisingIndirectReturn( - wrappedValueResult.conversion, - returnType: wrappedValueResult.javaType, - discriminatorParameterName: discriminatorName, - placeholderValue: .constant("0") - ), - outParameters: [ - JavaParameter(name: discriminatorName, type: .array(.byte)) - ] + wrappedValueResult.outParameters - ) - default: - throw JavaTranslationError.unsupportedSwiftType(swiftType) + break } + + let wrappedValueResult = try translate( + swiftResult: .init( + convention: .direct, + type: swiftType + ), + methodName: methodName, + resultName: resultName + "Wrapped" + ) + + // Assume that non-JavaValue types are always wrapped in a pointer. + return NativeResult( + javaType: wrappedValueResult.javaType, + conversion: .optionalRaisingIndirectReturn( + wrappedValueResult.conversion, + resultName: "\(resultName)$", + returnType: wrappedValueResult.javaType, + discriminatorParameterName: discriminatorName, + placeholderValue: .constant("0") + ), + outParameters: [ + JavaParameter(name: discriminatorName, type: .array(.byte)) + ] + wrappedValueResult.outParameters + ) } func translateClosureResult( @@ -813,14 +793,12 @@ extension JNISwift2JavaGenerator { resultName: outParamName ) - // FIXME: More accurate determination of whether the result is direct or indirect - if elementResult.outParameters.isEmpty { + outParameters.append(contentsOf: elementResult.outParameters) + if !elementResult.javaType.isVoid { // Convert direct result to indirect result outParameters.append( JavaParameter(name: outParamName, type: .array(elementResult.javaType)) ) - } else { - outParameters.append(contentsOf: elementResult.outParameters) } destructureElements.append( @@ -1165,6 +1143,7 @@ extension JNISwift2JavaGenerator { indirect case optionalRaisingIndirectReturn( NativeSwiftConversionStep, + resultName: String, returnType: JavaType, discriminatorParameterName: String, placeholderValue: NativeSwiftConversionStep @@ -1533,17 +1512,18 @@ extension JNISwift2JavaGenerator { case .optionalRaisingIndirectReturn( let inner, + let resultName, let returnType, let discriminatorParameterName, let placeholderValue ): if !returnType.isVoid { - printer.print("let result$: \(returnType.jniTypeName)") + printer.print("let \(resultName): \(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("\(resultName) = \(inner)") } printer.print( """ @@ -1555,7 +1535,7 @@ extension JNISwift2JavaGenerator { printer.printBraceBlock("else") { printer in let placeholderValue = placeholderValue.render(&printer, placeholder) if !returnType.isVoid { - printer.print("result$ = \(placeholderValue)") + printer.print("\(resultName) = \(placeholderValue)") } printer.print( """ @@ -1565,7 +1545,7 @@ extension JNISwift2JavaGenerator { ) } if !returnType.isVoid { - return "result$" + return resultName } else { return "" } diff --git a/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift b/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift index 57651e1e..544c59cb 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIGenericCombinationTests.swift @@ -55,7 +55,7 @@ struct JNIGenericCombinationTests { detectChunkByInitialLines: 2, expectedChunks: [ """ - public static Optional> makeStringIDOptional(java.lang.String value, SwiftArena swiftArena) { + public static java.util.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$); diff --git a/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift b/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift index 728165bf..79c26094 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIOptionalTests.swift @@ -155,7 +155,7 @@ struct JNIOptionalTests { * public func optionalClass(_ arg: MyClass?) -> MyClass? * } */ - public static Optional optionalClass(Optional arg, SwiftArena swiftArena) { + public static java.util.Optional optionalClass(Optional arg, SwiftArena swiftArena) { byte[] result$_discriminator$ = new byte[1]; long result$ = SwiftModule.$optionalClass(arg.map(MyClass::$memoryAddress).orElse(0L), result$_discriminator$); return (result$_discriminator$[0] == 1) ? Optional.of(MyClass.wrapMemoryAddressUnsafe(result$, swiftArena)) : Optional.empty(); diff --git a/Tests/JExtractSwiftTests/JNI/JNITupleTests.swift b/Tests/JExtractSwiftTests/JNI/JNITupleTests.swift index 67efc431..5163dfde 100644 --- a/Tests/JExtractSwiftTests/JNI/JNITupleTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNITupleTests.swift @@ -147,4 +147,60 @@ struct JNITupleTests { ] ) } + + @Test + func optionalTuple() throws { + let input = """ + public struct Foo {} + public func optionalTuple() -> (Int64?, Foo)? { + (42, Foo()) + } + """ + + try assertOutput( + input: input, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + byte[] result$_discriminator$ = new byte[1]; + byte[] resultWrapped$_0$$_discriminator$ = new byte[1]; + long[] resultWrapped$_0$ = new long[1]; + long[] resultWrapped$_1$ = new long[1]; + SwiftModule.$optionalTuple(result$_discriminator$, resultWrapped$_0$$_discriminator$, resultWrapped$_0$, resultWrapped$_1$); + """, + """ + private static native void $optionalTuple(byte[] result_discriminator$, byte[] resultWrapped_0$_discriminator$, long[] resultWrapped_0$, long[] resultWrapped_1$); + """, + ] + ) + } + + @Test + func genericTuple() throws { + let input = """ + public struct Box {} + public func genericTuple() -> (Box, Box) { + fatalError() + } + """ + + try assertOutput( + input: input, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + 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.$genericTuple(result_0$, result_1$); + """, + """ + private static native void $genericTuple(org.swift.swiftkit.core._OutSwiftGenericInstance result_0$Out, org.swift.swiftkit.core._OutSwiftGenericInstance result_1$Out); + """, + ] + ) + } }