From 773cf2081a43375e40cb07d8550b0cfd106f98c6 Mon Sep 17 00:00:00 2001 From: Iceman Date: Fri, 27 Mar 2026 12:45:51 +0900 Subject: [PATCH 01/10] Treat optional, array, dictionary, set types as nominal type --- .../Common/TypeAnnotations.swift | 4 +- .../FFM/CDeclLowering/CRepresentation.swift | 9 +- ...Swift2JavaGenerator+FunctionLowering.swift | 217 +++++++++--------- ...MSwift2JavaGenerator+JavaTranslation.swift | 168 ++++++-------- ...Generator+InterfaceWrapperGeneration.swift | 60 ++--- ...ISwift2JavaGenerator+JavaTranslation.swift | 109 +-------- ...wift2JavaGenerator+NativeTranslation.swift | 64 +----- .../Swift2JavaTranslator.swift | 13 +- .../SwiftTypes/SwiftFunctionSignature.swift | 7 +- .../SwiftTypes/SwiftKnownTypeDecls.swift | 40 ++++ .../SwiftTypes/SwiftKnownTypes.swift | 39 ++++ .../SwiftNominalTypeDeclaration.swift | 10 + .../SwiftTypes/SwiftType.swift | 109 +++++---- 13 files changed, 399 insertions(+), 450 deletions(-) diff --git a/Sources/JExtractSwiftLib/Common/TypeAnnotations.swift b/Sources/JExtractSwiftLib/Common/TypeAnnotations.swift index b23281d3..45125293 100644 --- a/Sources/JExtractSwiftLib/Common/TypeAnnotations.swift +++ b/Sources/JExtractSwiftLib/Common/TypeAnnotations.swift @@ -19,7 +19,9 @@ import SwiftJavaJNICore /// in Java sources when the corresponding Java type is rendered. func getTypeAnnotations(swiftType: SwiftType, config: Configuration) -> [JavaAnnotation] { switch swiftType { - case .array(let wrapped) where wrapped.isUnsignedInteger: + case .nominal(let nominal) where + nominal.nominalTypeDecl.knownTypeKind == .array + && nominal.genericArguments![0].isUnsignedInteger: return [JavaAnnotation.unsigned] case _ where swiftType.isUnsignedInteger: return [JavaAnnotation.unsigned] diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift index d2eeb58d..426ee42b 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/CRepresentation.swift @@ -31,6 +31,10 @@ extension CType { } switch knownType { + case .optional where nominalType.genericArguments?.count == 1 && nominalType.genericArguments![0].isPointer: + try self.init(cdeclType: nominalType.genericArguments![0]) + return + case .unsafePointer where nominalType.genericArguments?.count == 1: self = .pointer( .qualified(const: true, volatile: false, type: try CType(cdeclType: nominalType.genericArguments![0])) @@ -67,10 +71,7 @@ extension CType { case .tuple([]): self = .void - case .optional(let wrapped) where wrapped.isPointer: - try self.init(cdeclType: wrapped) - - case .genericParameter, .metatype, .optional, .tuple, .opaque, .existential, .composite, .array, .dictionary, .set: + case .genericParameter, .metatype, .tuple, .opaque, .existential, .composite: throw CDeclToCLoweringError.invalidCDeclType(cdeclType) } } diff --git a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift index d8406016..dcd99e1c 100644 --- a/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift +++ b/Sources/JExtractSwiftLib/FFM/CDeclLowering/FFMSwift2JavaGenerator+FunctionLowering.swift @@ -245,7 +245,7 @@ struct CdeclLowering { SwiftParameter( convention: .byValue, parameterName: "\(parameterName)_pointer", - type: .optional(isMutable ? knownTypes.unsafeMutableRawPointer : knownTypes.unsafeRawPointer) + type: knownTypes.optionalSugar(isMutable ? knownTypes.unsafeMutableRawPointer : knownTypes.unsafeRawPointer) ), SwiftParameter( convention: .byValue, @@ -300,6 +300,51 @@ struct CdeclLowering { ) } + case .array: + guard let genericArgs = nominal.genericArguments, genericArgs.count == 1, + genericArgs[0] == knownTypes.uint8 + else { + throw LoweringError.unhandledType(type) + } + + // Lower an array as 'address' raw pointer and 'count' integer + let cdeclParameters = [ + SwiftParameter( + convention: .byValue, + parameterName: "\(parameterName)_pointer", + type: knownTypes.unsafeRawPointer + ), + SwiftParameter( + convention: .byValue, + parameterName: "\(parameterName)_count", + type: knownTypes.int + ), + ] + + let bufferPointerInit = ConversionStep.initialize( + knownTypes.unsafeRawBufferPointer, + arguments: [ + LabeledArgument( + label: "start", + argument: .explodedComponent(.placeholder, component: "pointer") + ), + LabeledArgument( + label: "count", + argument: .explodedComponent(.placeholder, component: "count") + ), + ] + ) + + let arrayInit = ConversionStep.initialize( + type, + arguments: [LabeledArgument(argument: bufferPointerInit)] + ) + + return LoweredParameter( + cdeclParameters: cdeclParameters, + conversion: arrayInit + ) + case .foundationData, .essentialsData: break @@ -382,65 +427,8 @@ struct CdeclLowering { } throw LoweringError.unhandledType(type) - case .optional(let wrapped): - return try lowerOptionalParameter( - wrapped, - convention: convention, - parameterName: parameterName, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - case .composite: throw LoweringError.unhandledType(type) - - case .array(let wrapped) where wrapped == knownTypes.uint8: - // Lower an array as 'address' raw pointer and 'count' integer - let cdeclParameters = [ - SwiftParameter( - convention: .byValue, - parameterName: "\(parameterName)_pointer", - type: knownTypes.unsafeRawPointer - ), - SwiftParameter( - convention: .byValue, - parameterName: "\(parameterName)_count", - type: knownTypes.int - ), - ] - - let bufferPointerInit = ConversionStep.initialize( - knownTypes.unsafeRawBufferPointer, - arguments: [ - LabeledArgument( - label: "start", - argument: .explodedComponent(.placeholder, component: "pointer") - ), - LabeledArgument( - label: "count", - argument: .explodedComponent(.placeholder, component: "count") - ), - ] - ) - - let arrayInit = ConversionStep.initialize( - type, - arguments: [LabeledArgument(argument: bufferPointerInit)] - ) - - return LoweredParameter( - cdeclParameters: cdeclParameters, - conversion: arrayInit - ) - - case .array: - throw LoweringError.unhandledType(type) - - case .dictionary: - throw LoweringError.unhandledType(type) - - case .set: - throw LoweringError.unhandledType(type) } } @@ -462,7 +450,7 @@ struct CdeclLowering { SwiftParameter( convention: .byValue, parameterName: parameterName, - type: .optional(knownTypes.unsafePointer(wrappedType)) + type: knownTypes.optionalSugar(knownTypes.unsafePointer(wrappedType)) ) ], conversion: .pointee(.optionalChain(.placeholder)) @@ -476,20 +464,20 @@ struct CdeclLowering { case .foundationData, .essentialsData: break case .unsafeRawPointer, .unsafeMutableRawPointer: - throw LoweringError.unhandledType(.optional(wrappedType)) + throw LoweringError.unhandledType(knownTypes.optionalSugar(wrappedType)) case .unsafeRawBufferPointer, .unsafeMutableRawBufferPointer: - throw LoweringError.unhandledType(.optional(wrappedType)) + throw LoweringError.unhandledType(knownTypes.optionalSugar(wrappedType)) case .unsafePointer, .unsafeMutablePointer: - throw LoweringError.unhandledType(.optional(wrappedType)) + throw LoweringError.unhandledType(knownTypes.optionalSugar(wrappedType)) case .unsafeBufferPointer, .unsafeMutableBufferPointer: - throw LoweringError.unhandledType(.optional(wrappedType)) + throw LoweringError.unhandledType(knownTypes.optionalSugar(wrappedType)) case .void, .string: - throw LoweringError.unhandledType(.optional(wrappedType)) + throw LoweringError.unhandledType(knownTypes.optionalSugar(wrappedType)) case .foundationDataProtocol, .essentialsDataProtocol: - throw LoweringError.unhandledType(.optional(wrappedType)) + throw LoweringError.unhandledType(knownTypes.optionalSugar(wrappedType)) default: // Unreachable? Should be handled by `CType(cdeclType:)` lowering above. - throw LoweringError.unhandledType(.optional(wrappedType)) + throw LoweringError.unhandledType(knownTypes.optionalSugar(wrappedType)) } } @@ -499,7 +487,7 @@ struct CdeclLowering { SwiftParameter( convention: .byValue, parameterName: parameterName, - type: .optional(knownTypes.unsafeRawPointer) + type: knownTypes.optionalSugar(knownTypes.unsafeRawPointer) ) ], conversion: .pointee(.typedPointer(.optionalChain(.placeholder), swiftType: wrappedType)) @@ -519,7 +507,7 @@ struct CdeclLowering { genericRequirements: genericRequirements ) } - throw LoweringError.unhandledType(.optional(wrappedType)) + throw LoweringError.unhandledType(knownTypes.optionalSugar(wrappedType)) case .tuple(let tuple): if tuple.count == 1 { @@ -531,10 +519,10 @@ struct CdeclLowering { genericRequirements: genericRequirements ) } - throw LoweringError.unhandledType(.optional(wrappedType)) + throw LoweringError.unhandledType(knownTypes.optionalSugar(wrappedType)) - case .function, .metatype, .optional, .composite, .array, .dictionary, .set: - throw LoweringError.unhandledType(.optional(wrappedType)) + case .function, .metatype, .composite: + throw LoweringError.unhandledType(knownTypes.optionalSugar(wrappedType)) } } @@ -610,7 +598,7 @@ struct CdeclLowering { SwiftParameter( convention: .byValue, parameterName: "\(parameterName)_pointer", - type: .optional(isMutable ? knownTypes.unsafeMutableRawPointer : knownTypes.unsafeRawPointer) + type: knownTypes.optionalSugar(isMutable ? knownTypes.unsafeMutableRawPointer : knownTypes.unsafeRawPointer) ), SwiftParameter( convention: .byValue, @@ -635,12 +623,13 @@ struct CdeclLowering { // Custom types are not supported yet. throw LoweringError.unhandledType(type) - case .genericParameter, .function, .metatype, .optional, .tuple, .existential, .opaque, .composite, .array, .dictionary, .set: + case .genericParameter, .function, .metatype, .tuple, .existential, .opaque, .composite: // TODO: Implement throw LoweringError.unhandledType(type) } } + /// Create "out" parameter names when we're returning an array-like result. fileprivate func makeBufferIndirectReturnParameters(_ outParameterName: String, isMutable: Bool) -> [SwiftParameter] { [ @@ -648,7 +637,7 @@ struct CdeclLowering { convention: .byValue, parameterName: "\(outParameterName)_pointer", type: knownTypes.unsafeMutablePointer( - .optional(isMutable ? knownTypes.unsafeMutableRawPointer : knownTypes.unsafeRawPointer) + knownTypes.optionalSugar(isMutable ? knownTypes.unsafeMutableRawPointer : knownTypes.unsafeRawPointer) ) ), SwiftParameter( @@ -740,6 +729,45 @@ struct CdeclLowering { // Not supported at this point. throw LoweringError.unhandledType(type) + case .array where nominal.genericArguments?.count == 1 && nominal.genericArguments![0] == knownTypes.uint8 : + let resultName = "_result" + + return LoweredResult( + cdeclResultType: .void, // we call into the _result_initialize instead + cdeclOutParameters: [ + SwiftParameter( + convention: .byValue, + parameterName: "\(outParameterName)_initialize", + type: knownTypes.functionInitializeByteBuffer + ) + ], + conversion: .aggregate( + [ + .method( + base: resultName, + methodName: "withUnsafeBufferPointer", + arguments: [ + .init( + argument: + .closureLowering( + parameters: [.placeholder], + result: .method( + base: "\(outParameterName)_initialize", + methodName: nil, // just `(...)` apply the closure + arguments: [ + .init(label: nil, argument: .member(.constant("_0"), member: "baseAddress!")), + .init(label: nil, argument: .member(.constant("_0"), member: "count")), + ] + ) + ) + ) + ] + ) + ], + name: resultName + ) + ) + default: // Unreachable? Should be handled by `CType(cdeclType:)` lowering above. throw LoweringError.unhandledType(type) @@ -799,46 +827,7 @@ struct CdeclLowering { conversion: .tupleExplode(conversions, name: outParameterName) ) - case .array(let wrapped) where wrapped == knownTypes.uint8: - let resultName = "_result" - - return LoweredResult( - cdeclResultType: .void, // we call into the _result_initialize instead - cdeclOutParameters: [ - SwiftParameter( - convention: .byValue, - parameterName: "\(outParameterName)_initialize", - type: knownTypes.functionInitializeByteBuffer - ) - ], - conversion: .aggregate( - [ - .method( - base: resultName, - methodName: "withUnsafeBufferPointer", - arguments: [ - .init( - argument: - .closureLowering( - parameters: [.placeholder], - result: .method( - base: "\(outParameterName)_initialize", - methodName: nil, // just `(...)` apply the closure - arguments: [ - .init(label: nil, argument: .member(.constant("_0"), member: "baseAddress!")), - .init(label: nil, argument: .member(.constant("_0"), member: "count")), - ] - ) - ) - ) - ] - ) - ], - name: resultName - ) - ) - - case .genericParameter, .function, .optional, .existential, .opaque, .composite, .array, .dictionary, .set: + case .genericParameter, .function, .existential, .opaque, .composite: throw LoweringError.unhandledType(type) } } diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index 221ef889..78e91669 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -439,6 +439,23 @@ extension FFMSwift2JavaGenerator { conversion: .call(.placeholder, function: "SwiftRuntime.toCString", withArena: true) ) + case .array where swiftNominalType.genericArguments?.count == 1 && swiftNominalType.genericArguments![0] == knownTypes.uint8 : + return TranslatedParameter( + javaParameters: [ + JavaParameter(name: parameterName, type: .array(.byte), annotations: parameterAnnotations) + ], + conversion: + .commaSeparated([ + .call( + .commaSeparated([.constant("ValueLayout.JAVA_BYTE"), .placeholder]), + base: .temporaryArena, + function: "allocateFrom", + withArena: false // this would pass the arena as last argument, but instead we make a call on the arena + ), + .property(.placeholder, propertyName: "length"), + ]) + ) + case .foundationData, .essentialsData: break @@ -515,45 +532,8 @@ extension FFMSwift2JavaGenerator { // Otherwise, not supported yet. throw JavaTranslationError.unhandledType(swiftType) - case .optional(let wrapped): - return try translateOptionalParameter( - wrappedType: wrapped, - convention: convention, - parameterName: parameterName, - loweredParam: loweredParam, - methodName: methodName, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - case .composite: throw JavaTranslationError.unhandledType(swiftType) - - case .array(let wrapped) where wrapped == knownTypes.uint8: - return TranslatedParameter( - javaParameters: [ - JavaParameter(name: parameterName, type: .array(.byte), annotations: parameterAnnotations) - ], - conversion: - .commaSeparated([ - .call( - .commaSeparated([.constant("ValueLayout.JAVA_BYTE"), .placeholder]), - base: .temporaryArena, - function: "allocateFrom", - withArena: false // this would pass the arena as last argument, but instead we make a call on the arena - ), - .property(.placeholder, propertyName: "length"), - ]) - ) - - case .array: - throw JavaTranslationError.unhandledType(swiftType) - - case .dictionary: - throw JavaTranslationError.unhandledType(swiftType) - - case .set: - throw JavaTranslationError.unhandledType(swiftType) } } @@ -631,7 +611,7 @@ extension FFMSwift2JavaGenerator { case .short: ("Optional", "toOptionalSegmentShort") case .float: ("Optional", "toOptionalSegmentFloat") default: - throw JavaTranslationError.unhandledType(.optional(swiftType)) + throw JavaTranslationError.unhandledType(known: .optional(swiftType)) } return TranslatedParameter( javaParameters: [ @@ -650,7 +630,7 @@ extension FFMSwift2JavaGenerator { case .essentialsData, .essentialsDataProtocol: break default: - throw JavaTranslationError.unhandledType(.optional(swiftType)) + throw JavaTranslationError.unhandledType(known: .optional(swiftType)) } } @@ -677,7 +657,7 @@ extension FFMSwift2JavaGenerator { genericRequirements: genericRequirements ) } - throw JavaTranslationError.unhandledType(.optional(swiftType)) + throw JavaTranslationError.unhandledType(known: .optional(swiftType)) case .tuple(let tuple): if tuple.count == 1 { return try translateOptionalParameter( @@ -690,9 +670,9 @@ extension FFMSwift2JavaGenerator { genericRequirements: genericRequirements ) } - throw JavaTranslationError.unhandledType(.optional(swiftType)) + throw JavaTranslationError.unhandledType(known: .optional(swiftType)) default: - throw JavaTranslationError.unhandledType(.optional(swiftType)) + throw JavaTranslationError.unhandledType(known: .optional(swiftType)) } } @@ -770,6 +750,57 @@ extension FFMSwift2JavaGenerator { case .string: // FIXME: Implement throw JavaTranslationError.unhandledType(swiftType) + + case .array where swiftNominalType.genericArguments?.count == 1 && swiftNominalType.genericArguments![0] == knownTypes.uint8 : + return TranslatedResult( + javaResultType: + .array(.byte), + annotations: [.unsigned], + outParameters: [], // no out parameters, but we do an "out" callback + outCallback: OutCallback( + name: "$_result_initialize", + members: [ + "byte[] result = null" + ], + parameters: [ + JavaParameter(name: "pointer", type: .javaForeignMemorySegment), + JavaParameter(name: "count", type: .long), + ], + cFunc: CFunction( + resultType: .void, + name: "apply", + parameters: [ + CParameter(type: .pointer(.void)), + CParameter(type: .integral(.size_t)), + ], + isVariadic: false + ), + body: + "this.result = _0.reinterpret(_1).toArray(ValueLayout.JAVA_BYTE); // copy native Swift array to Java heap array" + ), + conversion: .initializeResultWithUpcall( + [ + .introduceVariable( + name: "_result_initialize", + initializeWith: .javaNew( + .commaSeparated( + [ + // We need to refer to the nested class that is created for this function. + // The class that contains all the related functional interfaces is called the same + // as the downcall function, so we use the thunk name to find this class/ + .placeholderForSwiftThunkName, .constant("$_result_initialize.Function$Impl()"), + ], + separator: "." + ) + ) + ), + // .constant("var = new \(.placeholderForDowncallThunkName).."), + .placeholderForDowncall, // perform the downcall here + ], + extractResult: .property(.constant("_result_initialize"), propertyName: "result") + ) + ) + default: throw JavaTranslationError.unhandledType(swiftType) } @@ -804,57 +835,7 @@ extension FFMSwift2JavaGenerator { resultAnnotations: resultAnnotations ) - case .array(let wrapped) where wrapped == knownTypes.uint8: - return TranslatedResult( - javaResultType: - .array(.byte), - annotations: [.unsigned], - outParameters: [], // no out parameters, but we do an "out" callback - outCallback: OutCallback( - name: "$_result_initialize", - members: [ - "byte[] result = null" - ], - parameters: [ - JavaParameter(name: "pointer", type: .javaForeignMemorySegment), - JavaParameter(name: "count", type: .long), - ], - cFunc: CFunction( - resultType: .void, - name: "apply", - parameters: [ - CParameter(type: .pointer(.void)), - CParameter(type: .integral(.size_t)), - ], - isVariadic: false - ), - body: - "this.result = _0.reinterpret(_1).toArray(ValueLayout.JAVA_BYTE); // copy native Swift array to Java heap array" - ), - conversion: .initializeResultWithUpcall( - [ - .introduceVariable( - name: "_result_initialize", - initializeWith: .javaNew( - .commaSeparated( - [ - // We need to refer to the nested class that is created for this function. - // The class that contains all the related functional interfaces is called the same - // as the downcall function, so we use the thunk name to find this class/ - .placeholderForSwiftThunkName, .constant("$_result_initialize.Function$Impl()"), - ], - separator: "." - ) - ) - ), - // .constant("var = new \(.placeholderForDowncallThunkName).."), - .placeholderForDowncall, // perform the downcall here - ], - extractResult: .property(.constant("_result_initialize"), propertyName: "result") - ) - ) - - case .genericParameter, .optional, .function, .existential, .opaque, .composite, .array, .dictionary, .set: + case .genericParameter, .function, .existential, .opaque, .composite: throw JavaTranslationError.unhandledType(swiftType) } @@ -1122,4 +1103,5 @@ extension CType { enum JavaTranslationError: Error { case inoutNotSupported(SwiftType, file: String = #file, line: Int = #line) case unhandledType(SwiftType, file: String = #file, line: Int = #line) + case unhandledType(known: SwiftKnownType, file: String = #file, line: Int = #line) } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift index 9cde170d..b3b3419a 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift @@ -230,21 +230,6 @@ extension JNISwift2JavaGenerator { case .tuple([]): // void return .placeholder - case .optional(let wrappedType): - return try translateOptionalParameter( - name: parameterName, - wrappedType: wrappedType - ) - - case .array(let elementType): - return try translateArrayParameter(name: parameterName, elementType: elementType) - - case .dictionary: - throw JavaTranslationError.unsupportedSwiftType(type) - - case .set: - throw JavaTranslationError.unsupportedSwiftType(type) - case .genericParameter, .function, .metatype, .tuple, .existential, .opaque, .composite: throw JavaTranslationError.unsupportedSwiftType(type) } @@ -253,6 +238,15 @@ extension JNISwift2JavaGenerator { private func translateArrayParameter(name: String, elementType: SwiftType) throws -> UpcallConversionStep { switch elementType { case .nominal(let nominalType): + if let knownType = nominalType.nominalTypeDecl.knownTypeKind { + switch knownType { + case .optional, .array, .dictionary, .set: + throw JavaTranslationError.unsupportedSwiftType(known: .array(elementType)) + default: + break + } + } + // We assume this is a JExtracted type return .map( .placeholder, @@ -263,8 +257,8 @@ extension JNISwift2JavaGenerator { ) ) - case .array, .dictionary, .set, .composite, .existential, .function, .genericParameter, .metatype, .opaque, .optional, .tuple: - throw JavaTranslationError.unsupportedSwiftType(.array(elementType)) + case .composite, .existential, .function, .genericParameter, .metatype, .opaque, .tuple: + throw JavaTranslationError.unsupportedSwiftType(known: .array(elementType)) } } @@ -325,18 +319,6 @@ extension JNISwift2JavaGenerator { case .tuple([]): // void return .placeholder - case .optional(let wrappedType): - return try self.translateOptionalResult(wrappedType: wrappedType, methodName: methodName) - - case .array(let elementType): - return try self.translateArrayResult(elementType: elementType) - - case .dictionary: - throw JavaTranslationError.unsupportedSwiftType(type) - - case .set: - throw JavaTranslationError.unsupportedSwiftType(type) - case .genericParameter, .function, .metatype, .tuple, .existential, .opaque, .composite: throw JavaTranslationError.unsupportedSwiftType(type) } @@ -345,6 +327,15 @@ extension JNISwift2JavaGenerator { private func translateArrayResult(elementType: SwiftType) throws -> UpcallConversionStep { switch elementType { case .nominal(let nominalType): + if let knownType = nominalType.nominalTypeDecl.knownTypeKind { + switch knownType { + case .optional, .array, .dictionary, .set: + throw JavaTranslationError.unsupportedSwiftType(known: .array(elementType)) + default: + break + } + } + // We assume this is a JExtracted type return .map( .placeholder, @@ -355,8 +346,8 @@ extension JNISwift2JavaGenerator { ) ) - case .array, .dictionary, .set, .composite, .existential, .function, .genericParameter, .metatype, .opaque, .optional, .tuple: - throw JavaTranslationError.unsupportedSwiftType(.array(elementType)) + case .composite, .existential, .function, .genericParameter, .metatype, .opaque, .tuple: + throw JavaTranslationError.unsupportedSwiftType(known: .array(elementType)) } } @@ -484,14 +475,13 @@ extension SwiftType { case .bool, .int, .uint, .int8, .uint8, .int16, .uint16, .int32, .uint32, .int64, .uint64, .float, .double, .string, .void: return true + case .array where swiftNominalType.genericArguments?.count == 1: + return swiftNominalType.genericArguments![0].isDirectlyTranslatedToWrapJava default: return false } - case .array(let elementType): - return elementType.isDirectlyTranslatedToWrapJava - - case .genericParameter, .function, .metatype, .optional, .tuple, .existential, .opaque, .composite, .dictionary, .set: + case .genericParameter, .function, .metatype, .tuple, .existential, .opaque, .composite: return false } } diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 2563c1a5..a73f43e1 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -578,14 +578,6 @@ extension JNISwift2JavaGenerator { conversion: .placeholder ) - case .optional(let wrapped): - return try translateOptionalParameter( - wrappedType: wrapped, - parameterName: parameterName, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - case .opaque(let proto), .existential(let proto): guard let parameterPosition else { fatalError("Cannot extract opaque or existential type that is not a parameter: \(proto)") @@ -615,31 +607,6 @@ extension JNISwift2JavaGenerator { throw JavaTranslationError.unsupportedSwiftType(swiftType) - case .array(let elementType): - return try translateArrayParameter( - elementType: elementType, - parameterName: parameterName, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - - case .dictionary(let keyType, let valueType): - return try translateDictionaryParameter( - keyType: keyType, - valueType: valueType, - parameterName: parameterName, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - - case .set(let elementType): - return try translateSetParameter( - elementType: elementType, - parameterName: parameterName, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - case .metatype: return TranslatedParameter( parameter: JavaParameter(name: parameterName, type: .long), @@ -1056,36 +1023,6 @@ extension JNISwift2JavaGenerator { case .tuple([]): return TranslatedResult(javaType: .void, outParameters: [], conversion: .placeholder) - case .optional(let wrapped): - return try translateOptionalResult( - wrappedType: wrapped, - resultName: resultName, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - - case .array(let elementType): - return try translateArrayResult( - elementType: elementType, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - - case .dictionary(let keyType, let valueType): - return try translateDictionaryResult( - keyType: keyType, - valueType: valueType, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - - case .set(let elementType): - return try translateSetResult( - elementType: elementType, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - case .tuple(let elements) where !elements.isEmpty: return try translateTupleResult( elements: elements, @@ -1212,43 +1149,6 @@ extension JNISwift2JavaGenerator { } return .class(package: nil, name: generic.name) - case .optional(let wrapped): - let wrappedType = try translateGenericTypeParameter( - wrapped, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - return .class(package: "java.util", name: "Optional", typeParameters: [wrappedType]) - - case .array(let elementType): - let elementJavaType = try translateGenericTypeParameter( - elementType, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - return .array(elementJavaType) - - case .dictionary(let keyType, let valueType): - let keyJavaType = try translateGenericTypeParameter( - keyType, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - let valueJavaType = try translateGenericTypeParameter( - valueType, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - return .swiftDictionaryMap(keyJavaType, valueJavaType) - - case .set(let elementType): - let elementJavaType = try translateGenericTypeParameter( - elementType, - genericParameters: genericParameters, - genericRequirements: genericRequirements - ) - return .swiftSet(elementJavaType) - case .metatype, .tuple, .function, .existential, .opaque, .composite: throw JavaTranslationError.unsupportedSwiftType(swiftType) } @@ -2130,6 +2030,15 @@ extension JNISwift2JavaGenerator { .unsupportedSwiftType(type, fileID: _fileID, line: _line) } + case unsupportedSwiftType(known: SwiftKnownType, fileID: String, line: Int) + static func unsupportedSwiftType( + known type: SwiftKnownType, + _fileID: String = #fileID, + _line: Int = #line + ) -> JavaTranslationError { + .unsupportedSwiftType(known: type, fileID: _fileID, line: _line) + } + /// The user has not supplied a mapping from `SwiftType` to /// a java class. case wrappedJavaClassTranslationNotProvided(SwiftType) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index e83ab08a..24eb3524 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -295,12 +295,6 @@ extension JNISwift2JavaGenerator { conversionCheck: nil ) - case .optional(let wrapped): - return try translateOptionalParameter( - wrappedType: wrapped, - parameterName: parameterName - ) - case .opaque(let proto), .existential(let proto): return try translateProtocolParameter( protocolType: proto, @@ -321,25 +315,6 @@ extension JNISwift2JavaGenerator { throw JavaTranslationError.unsupportedSwiftType(type) - case .array(let elementType): - return try translateArrayParameter( - elementType: elementType, - parameterName: parameterName - ) - - case .dictionary(let keyType, let valueType): - return try translateDictionaryParameter( - keyType: keyType, - valueType: valueType, - parameterName: parameterName - ) - - case .set(let elementType): - return try translateSetParameter( - elementType: elementType, - parameterName: parameterName - ) - case .metatype: return NativeParameter( parameters: [ @@ -672,7 +647,7 @@ extension JNISwift2JavaGenerator { outParameters: [] ) - case .function, .metatype, .optional, .tuple, .existential, .opaque, .genericParameter, .composite, .array, .dictionary, .set: + case .function, .metatype, .tuple, .existential, .opaque, .genericParameter, .composite: throw JavaTranslationError.unsupportedSwiftType(type) } } @@ -704,7 +679,7 @@ extension JNISwift2JavaGenerator { // Custom types are not supported yet. throw JavaTranslationError.unsupportedSwiftType(type) - case .function, .metatype, .optional, .tuple, .existential, .opaque, .genericParameter, .composite, .array, .dictionary, .set: + case .function, .metatype, .tuple, .existential, .opaque, .genericParameter, .composite: throw JavaTranslationError.unsupportedSwiftType(type) } } @@ -814,25 +789,6 @@ extension JNISwift2JavaGenerator { outParameters: [] ) - case .optional(let wrapped): - return try translateOptionalResult(wrappedType: wrapped, resultName: resultName) - - case .array(let elementType): - return try translateArrayResult(elementType: elementType, resultName: resultName) - - case .dictionary(let keyType, let valueType): - return try translateDictionaryResult( - keyType: keyType, - valueType: valueType, - resultName: resultName - ) - - case .set(let elementType): - return try translateSetResult( - elementType: elementType, - resultName: resultName - ) - case .tuple(let elements) where !elements.isEmpty: return try translateTupleResult(elements: elements, resultName: resultName) @@ -911,7 +867,7 @@ extension JNISwift2JavaGenerator { guard let javaType = JNIJavaTypeTranslator.translate(knownType: knownType, config: self.config), javaType.implementsJavaValue else { - throw JavaTranslationError.unsupportedSwiftType(.array(elementType)) + throw JavaTranslationError.unsupportedSwiftType(known: .array(elementType)) } return NativeResult( @@ -922,7 +878,7 @@ extension JNISwift2JavaGenerator { } guard !nominalType.isSwiftJavaWrapper else { - throw JavaTranslationError.unsupportedSwiftType(.array(elementType)) + throw JavaTranslationError.unsupportedSwiftType(known: .array(elementType)) } // Assume JExtract imported class @@ -948,7 +904,7 @@ extension JNISwift2JavaGenerator { ) default: - throw JavaTranslationError.unsupportedSwiftType(.array(elementType)) + throw JavaTranslationError.unsupportedSwiftType(known: .array(elementType)) } } @@ -969,21 +925,21 @@ extension JNISwift2JavaGenerator { parameters: [ JavaParameter(name: parameterName, type: .array(javaType)) ], - conversion: .initFromJNI(.placeholder, swiftType: .array(elementType)), + conversion: .initFromJNI(.placeholder, swiftType: knownTypes.arraySugar(elementType)), indirectConversion: nil, conversionCheck: nil ) } guard !nominalType.isSwiftJavaWrapper else { - throw JavaTranslationError.unsupportedSwiftType(.array(elementType)) + throw JavaTranslationError.unsupportedSwiftType(knownTypes.arraySugar(elementType)) } // Assume JExtract wrapped class return NativeParameter( parameters: [JavaParameter(name: parameterName, type: .array(.long))], conversion: .method( - .initFromJNI(.placeholder, swiftType: .array(self.knownTypes.int64)), + .initFromJNI(.placeholder, swiftType: knownTypes.arraySugar(self.knownTypes.int64)), function: "map", arguments: [ ( @@ -1020,7 +976,7 @@ extension JNISwift2JavaGenerator { parameters: [ JavaParameter(name: parameterName, type: .long) ], - conversion: .initFromJNI(.placeholder, swiftType: .dictionary(key: keyType, value: valueType)), + conversion: .initFromJNI(.placeholder, swiftType: knownTypes.dictionarySugar(keyType, valueType)), indirectConversion: nil, conversionCheck: nil ) @@ -1050,7 +1006,7 @@ extension JNISwift2JavaGenerator { parameters: [ JavaParameter(name: parameterName, type: .long) ], - conversion: .initFromJNI(.placeholder, swiftType: .set(element: elementType)), + conversion: .initFromJNI(.placeholder, swiftType: knownTypes.set(elementType)), indirectConversion: nil, conversionCheck: nil ) diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index 18cf5991..ff4de215 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -151,9 +151,12 @@ extension Swift2JavaTranslator { func check(_ type: SwiftType) -> Bool { switch type { case .nominal(let nominal): + if let genericArguments = nominal.genericArguments { + if genericArguments.contains(where: check) { + return true + } + } return predicate(nominal.nominalTypeDecl) - case .optional(let ty): - return check(ty) case .tuple(let tuple): return tuple.contains(where: { check($0.type) }) case .function(let fn): @@ -166,12 +169,6 @@ extension Swift2JavaTranslator { return types.contains(where: check) case .genericParameter: return false - case .array(let ty): - return check(ty) - case .dictionary(let key, let value): - return check(key) || check(value) - case .set(let element): - return check(element) } } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift index 5c285a7d..7f02fe3d 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift @@ -96,7 +96,12 @@ extension SwiftFunctionSignature { lookupContext: lookupContext ) - let type = node.optionalMark != nil ? .optional(enclosingType) : enclosingType + let type: SwiftType = if node.optionalMark != nil { + SwiftKnownTypes(symbolTable: lookupContext.symbolTable) + .optionalSugar(enclosingType) + } else { + enclosingType + } self.init( selfParameter: .initializer(enclosingType), diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift index 2b41e12b..c4c79e67 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypeDecls.swift @@ -14,6 +14,46 @@ import SwiftSyntax +enum SwiftKnownType { + case bool + case int + case uint + case int8 + case uint8 + case int16 + case uint16 + case int32 + case uint32 + case int64 + case uint64 + case float + case double + case unsafeRawPointer + case unsafeRawBufferPointer + case unsafeMutableRawPointer + case unsafeMutableRawBufferPointer + case unsafePointer(_ pointee: SwiftType) + case unsafeMutablePointer(_ pointee: SwiftType) + case unsafeBufferPointer(_ element: SwiftType) + case unsafeMutableBufferPointer(_ element: SwiftType) + case optional(_ wrapped: SwiftType) + case void + case string + case array(_ element: SwiftType) + case dictionary(_ key: SwiftType, _ value: SwiftType) + case set(_ element: SwiftType) + + // Foundation + case foundationDataProtocol + case essentialsDataProtocol + case foundationData + case essentialsData + case foundationDate + case essentialsDate + case foundationUUID + case essentialsUUID +} + enum SwiftKnownTypeDeclKind: String, Hashable { // Swift case bool = "Swift.Bool" diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift index edf75f0f..a1a21fd6 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftKnownTypes.swift @@ -104,6 +104,45 @@ struct SwiftKnownTypes { ) } + func optionalSugar(_ wrappedType: SwiftType) -> SwiftType { + .nominal( + SwiftNominalType( + sugarName: .optional, + nominalTypeDecl: symbolTable[.optional], + genericArguments: [wrappedType] + ) + ) + } + + func arraySugar(_ elementType: SwiftType) -> SwiftType { + .nominal( + SwiftNominalType( + sugarName: .array, + nominalTypeDecl: symbolTable[.array], + genericArguments: [elementType] + ) + ) + } + + func dictionarySugar(_ keyType: SwiftType, _ valueType: SwiftType) -> SwiftType { + .nominal( + SwiftNominalType( + sugarName: .dictionary, + nominalTypeDecl: symbolTable[.dictionary], + genericArguments: [keyType, valueType] + ) + ) + } + + func set(_ elementType: SwiftType) -> SwiftType { + .nominal( + SwiftNominalType( + nominalTypeDecl: symbolTable[.set], + genericArguments: [elementType] + ) + ) + } + /// Returns the known representative concrete type if there is one for the /// given protocol kind. E.g. `String` for `StringProtocol` func representativeType(of knownProtocol: SwiftKnownTypeDeclKind) -> SwiftType? { diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift index 78ae5289..d4414971 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift @@ -169,6 +169,16 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration { } } +extension SwiftNominalTypeDeclaration: CustomStringConvertible { + package var description: String { + if isGeneric { + "\(qualifiedName)<\(genericParameters.map(\.name).joined(separator: ", "))>" + } else { + qualifiedName + } + } +} + package class SwiftGenericParameterDeclaration: SwiftTypeDeclaration { let syntax: GenericParameterSyntax diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift index f3328c8d..835c3450 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift @@ -38,9 +38,6 @@ enum SwiftType: Equatable { /// `.Type` indirect case metatype(SwiftType) - /// `?` - indirect case optional(SwiftType) - /// `(