diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftModuleSymbolTable.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftModuleSymbolTable.swift index f42d750fb..c77038596 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftModuleSymbolTable.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftModuleSymbolTable.swift @@ -36,6 +36,11 @@ struct SwiftModuleSymbolTable: SwiftSymbolTableProtocol { /// type name to the nominal type declaration. var nestedTypes: [SwiftNominalTypeDeclaration: [String: SwiftNominalTypeDeclaration]] = [:] + /// The nested typealias declarations defined within this module. The map itself is indexed + /// by the nominal type declaration, and each entry is a map from the nested typealias + /// name to the typealias declaration. + var nestedTypeAliases: [SwiftNominalTypeDeclaration: [String: SwiftTypeAliasDeclaration]] = [:] + /// Look for a top-level type with the given name. func lookupTopLevelNominalType(_ name: String) -> SwiftNominalTypeDeclaration? { topLevelTypes[name] @@ -51,6 +56,11 @@ struct SwiftModuleSymbolTable: SwiftSymbolTableProtocol { nestedTypes[parent]?[name] } + // Look for a nested typealias with the given name. + func lookupNestedTypealias(_ name: String, parent: SwiftNominalTypeDeclaration) -> SwiftTypeAliasDeclaration? { + nestedTypeAliases[parent]?[name] + } + func isAlternative(for moduleName: String) -> Bool { alternativeModules.flatMap { $0.moduleNames.contains(moduleName) } ?? false } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift index f2dd1fb3e..900f2c003 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift @@ -77,6 +77,8 @@ extension SwiftParsedModuleSymbolTableBuilder { if let nominalTypeNode = decl.asNominal { self.handle(sourceFilePath: sourceFilePath, nominalTypeDecl: nominalTypeNode, parent: nil) + } else if let typeAliasNode = decl.as(TypeAliasDeclSyntax.self) { + self.handle(sourceFilePath: sourceFilePath, typeAliasDecl: typeAliasNode, parent: nil) } else if let extensionNode = decl.as(ExtensionDeclSyntax.self) { self.handle(extensionDecl: extensionNode, sourceFilePath: sourceFilePath) } else if let ifConfigNode = decl.as(IfConfigDeclSyntax.self) { @@ -113,7 +115,9 @@ extension SwiftParsedModuleSymbolTableBuilder { ) { // If we have already recorded a nominal type with the name in this module, // it's an invalid redeclaration. - if let _ = symbolTable.lookupType(node.name.text, parent: parent) { + if symbolTable.lookupType(node.name.text, parent: parent) != nil + || symbolTable.lookupTypealias(node.name.text, parent: parent) != nil + { log?.debug("Failed to add a decl into symbol table: redeclaration; " + node.nameForDebug) return } @@ -137,6 +141,31 @@ extension SwiftParsedModuleSymbolTableBuilder { self.handle(sourceFilePath: sourceFilePath, memberBlock: node.memberBlock, parent: nominalTypeDecl) } + mutating func handle( + sourceFilePath: String, + typeAliasDecl node: TypeAliasDeclSyntax, + parent: SwiftNominalTypeDeclaration? + ) { + if symbolTable.lookupType(node.name.text, parent: parent) != nil + || symbolTable.lookupTypealias(node.name.text, parent: parent) != nil + { + log?.debug("Failed to add a decl into symbol table: redeclaration; " + node.nameForDebug) + return + } + + let typeAliasDecl = SwiftTypeAliasDeclaration( + sourceFilePath: sourceFilePath, + moduleName: moduleName, + node: node + ) + + if let parent { + symbolTable.nestedTypeAliases[parent, default: [:]][typeAliasDecl.name] = typeAliasDecl + } else { + symbolTable.topLevelTypeAliases[typeAliasDecl.name] = typeAliasDecl + } + } + mutating func handle( sourceFilePath: String, memberBlock node: MemberBlockSyntax, @@ -146,9 +175,10 @@ extension SwiftParsedModuleSymbolTableBuilder { // Find any nested types within this nominal type and add them. if let nominalMember = member.decl.asNominal { self.handle(sourceFilePath: sourceFilePath, nominalTypeDecl: nominalMember, parent: parent) + } else if let typeAliasMember = member.decl.as(TypeAliasDeclSyntax.self) { + self.handle(sourceFilePath: sourceFilePath, typeAliasDecl: typeAliasMember, parent: parent) } } - } mutating func handle( diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift index 2a3796344..d07a2753a 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftSymbolTable.swift @@ -26,11 +26,14 @@ package protocol SwiftSymbolTableProtocol { /// return nominal types within this module. func lookupTopLevelNominalType(_ name: String) -> SwiftNominalTypeDeclaration? + /// Look for a top-level typealias with the given name. + func lookupTopLevelTypealias(_ name: String) -> SwiftTypeAliasDeclaration? + // Look for a nested type with the given name. func lookupNestedType(_ name: String, parent: SwiftNominalTypeDeclaration) -> SwiftNominalTypeDeclaration? - /// Look for a top-level typealias with the given name. - func lookupTopLevelTypealias(_ name: String) -> SwiftTypeAliasDeclaration? + // Look for a nested typealias with the given name. + func lookupNestedTypealias(_ name: String, parent: SwiftNominalTypeDeclaration) -> SwiftTypeAliasDeclaration? } extension SwiftSymbolTableProtocol { @@ -42,6 +45,14 @@ extension SwiftSymbolTableProtocol { return lookupTopLevelNominalType(name) } + + package func lookupTypealias(_ name: String, parent: SwiftNominalTypeDeclaration?) -> SwiftTypeAliasDeclaration? { + if let parent { + return lookupNestedTypealias(name, parent: parent) + } + + return lookupTopLevelTypealias(name) + } } package class SwiftSymbolTable { @@ -167,6 +178,21 @@ extension SwiftSymbolTable: SwiftSymbolTableProtocol { return importedModules[moduleName]?.lookupTopLevelNominalType(name) } + /// Look for a top-level typealias with the given name. + package func lookupTopLevelTypealias(_ name: String) -> SwiftTypeAliasDeclaration? { + if let parsedResult = parsedModule.lookupTopLevelTypealias(name) { + return parsedResult + } + + for importedModule in prioritySortedImportedModules { + if let result = importedModule.lookupTopLevelTypealias(name) { + return result + } + } + + return nil + } + // Look for a nested type with the given name. package func lookupNestedType(_ name: String, parent: SwiftNominalTypeDeclaration) -> SwiftNominalTypeDeclaration? { if let parsedResult = parsedModule.lookupNestedType(name, parent: parent) { @@ -182,14 +208,14 @@ extension SwiftSymbolTable: SwiftSymbolTableProtocol { return nil } - /// Look for a top-level typealias with the given name. - package func lookupTopLevelTypealias(_ name: String) -> SwiftTypeAliasDeclaration? { - if let parsedResult = parsedModule.lookupTopLevelTypealias(name) { + // Look for a nested typealias with the given name. + package func lookupNestedTypealias(_ name: String, parent: SwiftNominalTypeDeclaration) -> SwiftTypeAliasDeclaration? { + if let parsedResult = parsedModule.lookupNestedTypealias(name, parent: parent) { return parsedResult } - for importedModule in prioritySortedImportedModules { - if let result = importedModule.lookupTopLevelTypealias(name) { + for importedModule in importedModules.values { + if let result = importedModule.lookupNestedTypealias(name, parent: parent) { return result } } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift index 0c9901114..95940a475 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift @@ -443,7 +443,9 @@ extension SwiftType { guard let parentDecl = parent.asNominalTypeDeclaration else { throw TypeTranslationError.unknown(originalType) } - typeDecl = lookupContext.symbolTable.lookupNestedType(name.text, parent: parentDecl) + typeDecl = + lookupContext.symbolTable.lookupNestedType(name.text, parent: parentDecl) + ?? lookupContext.symbolTable.lookupNestedTypealias(name.text, parent: parentDecl) } else if let module { typeDecl = lookupContext.moduleQualifiedLookup(name: name.text, in: module) } else { diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift index 559a850c8..408ef6fd8 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift @@ -64,6 +64,9 @@ class SwiftTypeLookupContext { if let found = symbolTable.lookupNestedType(name.name, parent: nominalDecl) { return found } + if let found = symbolTable.lookupNestedTypealias(name.name, parent: nominalDecl) { + return found + } } case .lookForGenericParameters(let extensionNode): diff --git a/Tests/JExtractSwiftTests/TypealiasResolutionTests.swift b/Tests/JExtractSwiftTests/TypealiasResolutionTests.swift index 9d18fcae8..aca0fbd10 100644 --- a/Tests/JExtractSwiftTests/TypealiasResolutionTests.swift +++ b/Tests/JExtractSwiftTests/TypealiasResolutionTests.swift @@ -226,14 +226,21 @@ struct TypealiasResolutionTests { } public typealias FishBox = Box + public func makeFishBox() -> FishBox { + .init() + } """# - var config = Configuration() - config.swiftModule = "SwiftModule" - let translator = Swift2JavaTranslator(config: config) - try translator.analyze(path: "/fake/Fake.swift", text: input) - - #expect(translator.importedTypes["FishBox"] != nil, "FishBox specialization should still register") + try assertOutput( + input: input, + .jni, + .java, + detectChunkByInitialLines: 1, + expectedChunks: [ + "public final class FishBox implements JNISwiftInstance {", + "public static Box makeFishBox(", + ], + ) } // ==== ----------------------------------------------------------------------- @@ -405,4 +412,86 @@ struct TypealiasResolutionTests { #expect(nominal.nominalTypeDecl.name == "Optional") #expect(nominal.genericArguments?.first?.description == "Int64") } + + // ==== ----------------------------------------------------------------------- + // MARK: Nested typealiases + + @Test("Typealias nested inside a type resolves correctly") + func nestedTypealiasResolves() throws { + let input = + #""" + public struct Foo { + public typealias ID = Int + public var id: ID + } + + public func useFooID(value: Foo.ID) -> Foo.ID { + value + } + """# + + try assertOutput( + input: input, + .jni, + .swift, + detectChunkByInitialLines: 2, + expectedChunks: [ + #""" + @_cdecl("Java_com_example_swift_Foo__00024getId__J") + public func Java_com_example_swift_Foo__00024getId__J(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong) -> jlong { + """#, + #""" + @_cdecl("Java_com_example_swift_Foo__00024setId__JJ") + public func Java_com_example_swift_Foo__00024setId__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, newValue: jlong, selfPointer: jlong) { + """#, + ], + ) + + try assertOutput( + input: input, + .jni, + .java, + detectChunkByInitialLines: 2, + expectedChunks: [ + """ + public long getId() + """, + """ + public void setId(long newValue) + """, + """ + public static long useFooID(long value) + """, + ], + ) + } + + @Test("Nested typealias used in an extension of that typealias's RHS resolves") + func useNestedTypealiasFromExtension() throws { + let input = + #""" + public typealias MyEnumAlt = Never + + extension MyStruct.MyEnumAlt { + public func methodInExtension() {} + } + + public struct MyStruct { + public enum MyEnum {} + public typealias MyEnumAlt = MyEnum + } + """# + + try assertOutput( + input: input, + .ffm, + .java, + expectedChunks: [ + #""" + private static final MemorySegment ADDR = + SwiftModule.findOrThrow("swiftjava_SwiftModule_MyStruct_MyEnum_methodInExtension"); + """# + ], + ) + } }