diff --git a/Sources/JExtractSwift/ImportedDecls.swift b/Sources/JExtractSwift/ImportedDecls.swift index 885266a8..e98d29b3 100644 --- a/Sources/JExtractSwift/ImportedDecls.swift +++ b/Sources/JExtractSwift/ImportedDecls.swift @@ -52,29 +52,17 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible { public var swiftDecl: any DeclSyntaxProtocol - var translatedSignature: TranslatedFunctionSignature - package var apiKind: SwiftAPIKind + var functionSignature: SwiftFunctionSignature + public var signatureString: String { self.swiftDecl.signatureString } - var loweredSignature: LoweredFunctionSignature { - translatedSignature.loweredSignature - } - - var swiftSignature: SwiftFunctionSignature { - loweredSignature.original - } - - package func cFunctionDecl(cName: String) -> CFunction { - // 'try!' because we know 'loweredSignature' can be described with C. - try! loweredSignature.cFunctionDecl(cName: cName) - } var parentType: SwiftType? { - guard let selfParameter = swiftSignature.selfParameter else { + guard let selfParameter = functionSignature.selfParameter else { return nil } switch selfParameter { @@ -91,7 +79,7 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible { /// this will contain that declaration's imported name. /// /// This is necessary when rendering accessor Java code we need the type that "self" is expecting to have. - public var hasParent: Bool { translatedSignature.selfParameter != nil } + public var hasParent: Bool { functionSignature.selfParameter != nil } /// A display name to use to refer to the Swift declaration with its /// enclosing type, if there is one. @@ -116,13 +104,13 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible { swiftDecl: any DeclSyntaxProtocol, name: String, apiKind: SwiftAPIKind, - translatedSignature: TranslatedFunctionSignature + functionSignature: SwiftFunctionSignature ) { self.module = module self.name = name self.swiftDecl = swiftDecl self.apiKind = apiKind - self.translatedSignature = translatedSignature + self.functionSignature = functionSignature } public var description: String { diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+JavaBindingsPrinting.swift b/Sources/JExtractSwift/Swift2JavaTranslator+JavaBindingsPrinting.swift index 0403ddf3..90f98577 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+JavaBindingsPrinting.swift @@ -19,6 +19,11 @@ extension Swift2JavaTranslator { _ printer: inout CodePrinter, _ decl: ImportedFunc ) { + guard let _ = translatedSignature(for: decl) else { + // Failed to translate. Skip. + return + } + printer.printSeparator(decl.displayName) printJavaBindingDescriptorClass(&printer, decl) @@ -33,7 +38,9 @@ extension Swift2JavaTranslator { _ decl: ImportedFunc ) { let thunkName = thunkNameRegistry.functionThunkName(decl: decl) - let cFunc = decl.cFunctionDecl(cName: thunkName) + let translatedSignature = self.translatedSignature(for: decl)! + // 'try!' because we know 'loweredSignature' can be described with C. + let cFunc = try! translatedSignature.loweredSignature.cFunctionDecl(cName: thunkName) printer.printBraceBlock( """ @@ -129,19 +136,20 @@ extension Swift2JavaTranslator { } var modifiers = "public" - switch decl.swiftSignature.selfParameter { + switch decl.functionSignature.selfParameter { case .staticMethod, .initializer, nil: modifiers.append(" static") default: break } - let returnTy = decl.translatedSignature.result.javaResultType + let translatedSignature = self.translatedSignature(for: decl)! + let returnTy = translatedSignature.result.javaResultType - var paramDecls = decl.translatedSignature.parameters + var paramDecls = translatedSignature.parameters .flatMap(\.javaParameters) .map { "\($0.type) \($0.name)" } - if decl.translatedSignature.requiresSwiftArena { + if translatedSignature.requiresSwiftArena { paramDecls.append("SwiftArena swiftArena$") } @@ -157,7 +165,7 @@ extension Swift2JavaTranslator { \(modifiers) \(returnTy) \(methodName)(\(paramDecls.joined(separator: ", "))) """ ) { printer in - if case .instance(_) = decl.swiftSignature.selfParameter { + if case .instance(_) = decl.functionSignature.selfParameter { // Make sure the object has not been destroyed. printer.print("$ensureAlive();") } @@ -174,7 +182,9 @@ extension Swift2JavaTranslator { _ decl: ImportedFunc ) { //=== Part 1: prepare temporary arena if needed. - if decl.translatedSignature.requiresTemporaryArena { + let translatedSignature = self.translatedSignature(for: decl)! + + if translatedSignature.requiresTemporaryArena { printer.print("try(var arena$ = Arena.ofConfined()) {") printer.indent(); } @@ -183,21 +193,21 @@ extension Swift2JavaTranslator { var downCallArguments: [String] = [] // Regular parameters. - for (i, parameter) in decl.translatedSignature.parameters.enumerated() { - let original = decl.swiftSignature.parameters[i] + for (i, parameter) in translatedSignature.parameters.enumerated() { + let original = decl.functionSignature.parameters[i] let parameterName = original.parameterName ?? "_\(i)" let lowered = parameter.conversion.render(&printer, parameterName) downCallArguments.append(lowered) } // 'self' parameter. - if let selfParameter = decl.translatedSignature.selfParameter { + if let selfParameter = translatedSignature.selfParameter { let lowered = selfParameter.conversion.render(&printer, "this") downCallArguments.append(lowered) } // Indirect return receivers. - for outParameter in decl.translatedSignature.result.outParameters { + for outParameter in translatedSignature.result.outParameters { let memoryLayout = renderMemoryLayoutValue(for: outParameter.type) let arena = if let className = outParameter.type.className, @@ -222,27 +232,27 @@ extension Swift2JavaTranslator { let downCall = "\(thunkName).call(\(downCallArguments.joined(separator: ", ")))" //=== Part 4: Convert the return value. - if decl.translatedSignature.result.javaResultType == .void { + if translatedSignature.result.javaResultType == .void { printer.print("\(downCall);") } else { let placeholder: String - if decl.translatedSignature.result.outParameters.isEmpty { + if translatedSignature.result.outParameters.isEmpty { placeholder = downCall } else { // FIXME: Support cdecl thunk returning a value while populating the out parameters. printer.print("\(downCall);") placeholder = "_result" } - let result = decl.translatedSignature.result.conversion.render(&printer, placeholder) + let result = translatedSignature.result.conversion.render(&printer, placeholder) - if decl.translatedSignature.result.javaResultType != .void { + if translatedSignature.result.javaResultType != .void { printer.print("return \(result);") } else { printer.print("\(result);") } } - if decl.translatedSignature.requiresTemporaryArena { + if translatedSignature.requiresTemporaryArena { printer.outdent() printer.print("}") } diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+JavaTranslation.swift b/Sources/JExtractSwift/Swift2JavaTranslator+JavaTranslation.swift index 31935290..bf1cd9d2 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+JavaTranslation.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+JavaTranslation.swift @@ -15,15 +15,26 @@ import JavaTypes extension Swift2JavaTranslator { - func translate( - swiftSignature: SwiftFunctionSignature - ) throws -> TranslatedFunctionSignature { - let lowering = CdeclLowering(swiftStdlibTypes: self.swiftStdlibTypes) - let loweredSignature = try lowering.lowerFunctionSignature(swiftSignature) + func translatedSignature( + for decl: ImportedFunc + ) -> TranslatedFunctionSignature? { + if let cached = translatedSignatures[decl] { + return cached + } - let translation = JavaTranslation(swiftStdlibTypes: self.swiftStdlibTypes) - let translated = try translation.translate(loweredFunctionSignature: loweredSignature) + let translated: TranslatedFunctionSignature? + do { + let lowering = CdeclLowering(swiftStdlibTypes: self.swiftStdlibTypes) + let loweredSignature = try lowering.lowerFunctionSignature(decl.functionSignature) + + let translation = JavaTranslation(swiftStdlibTypes: self.swiftStdlibTypes) + translated = try translation.translate(loweredFunctionSignature: loweredSignature) + } catch { + self.log.info("Failed to translate: '\(decl.swiftDecl.qualifiedNameForDebug)'; \(error)") + translated = nil + } + translatedSignatures[decl] = translated return translated } } diff --git a/Sources/JExtractSwift/SwiftThunkTranslator.swift b/Sources/JExtractSwift/Swift2JavaTranslator+SwiftThunkPrinting.swift similarity index 96% rename from Sources/JExtractSwift/SwiftThunkTranslator.swift rename to Sources/JExtractSwift/Swift2JavaTranslator+SwiftThunkPrinting.swift index 7db4c50b..51547ba8 100644 --- a/Sources/JExtractSwift/SwiftThunkTranslator.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+SwiftThunkPrinting.swift @@ -174,8 +174,13 @@ struct SwiftThunkTranslator { func render(forFunc decl: ImportedFunc) -> [DeclSyntax] { st.log.trace("Rendering thunks for: \(decl.displayName)") + let thunkName = st.thunkNameRegistry.functionThunkName(decl: decl) - let thunkFunc = decl.loweredSignature.cdeclThunk( + guard let translatedSignatures = st.translatedSignature(for: decl) else { + return [] + } + + let thunkFunc = translatedSignatures.loweredSignature.cdeclThunk( cName: thunkName, swiftAPIName: decl.name, as: decl.apiKind, diff --git a/Sources/JExtractSwift/Swift2JavaTranslator.swift b/Sources/JExtractSwift/Swift2JavaTranslator.swift index c8ed5bbe..496c9b44 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator.swift @@ -56,6 +56,9 @@ public final class Swift2JavaTranslator { package var thunkNameRegistry: ThunkNameRegistry = ThunkNameRegistry() + /// Cached Java translation result. 'nil' indicates failed translation. + var translatedSignatures: [ImportedFunc: TranslatedFunctionSignature?] = [:] + /// The name of the Swift module being translated. var swiftModuleName: String { symbolTable.moduleName diff --git a/Sources/JExtractSwift/Swift2JavaVisitor.swift b/Sources/JExtractSwift/Swift2JavaVisitor.swift index 0c433e78..4bb85a46 100644 --- a/Sources/JExtractSwift/Swift2JavaVisitor.swift +++ b/Sources/JExtractSwift/Swift2JavaVisitor.swift @@ -121,16 +121,15 @@ final class Swift2JavaVisitor: SyntaxVisitor { self.log.debug("Import function: '\(node.qualifiedNameForDebug)'") - let translatedSignature: TranslatedFunctionSignature + let signature: SwiftFunctionSignature do { - let swiftSignature = try SwiftFunctionSignature( + signature = try SwiftFunctionSignature( node, enclosingType: self.currentSwiftType, symbolTable: self.translator.symbolTable ) - translatedSignature = try translator.translate(swiftSignature: swiftSignature) } catch { - self.log.debug("Failed to translate: '\(node.qualifiedNameForDebug)'; \(error)") + self.log.debug("Failed to import: '\(node.qualifiedNameForDebug)'; \(error)") return .skipChildren } @@ -139,7 +138,7 @@ final class Swift2JavaVisitor: SyntaxVisitor { swiftDecl: node, name: node.name.text, apiKind: .function, - translatedSignature: translatedSignature + functionSignature: signature ) log.debug("Record imported method \(node.qualifiedNameForDebug)") @@ -166,26 +165,19 @@ final class Swift2JavaVisitor: SyntaxVisitor { self.log.debug("Import variable: \(node.kind) '\(node.qualifiedNameForDebug)'") func importAccessor(kind: SwiftAPIKind) throws { - let translatedSignature: TranslatedFunctionSignature - do { - let swiftSignature = try SwiftFunctionSignature( - node, - isSet: kind == .setter, - enclosingType: self.currentSwiftType, - symbolTable: self.translator.symbolTable - ) - translatedSignature = try translator.translate(swiftSignature: swiftSignature) - } catch { - self.log.debug("Failed to translate: \(node.qualifiedNameForDebug); \(error)") - throw error - } + let signature = try SwiftFunctionSignature( + node, + isSet: kind == .setter, + enclosingType: self.currentSwiftType, + symbolTable: self.translator.symbolTable + ) let imported = ImportedFunc( module: translator.swiftModuleName, swiftDecl: node, name: varName, apiKind: kind, - translatedSignature: translatedSignature + functionSignature: signature ) log.debug("Record imported variable accessor \(kind == .getter ? "getter" : "setter"):\(node.qualifiedNameForDebug)") @@ -205,7 +197,7 @@ final class Swift2JavaVisitor: SyntaxVisitor { try importAccessor(kind: .setter) } } catch { - self.log.debug("Failed to translate: \(node.qualifiedNameForDebug); \(error)") + self.log.debug("Failed to import: \(node.qualifiedNameForDebug); \(error)") return .skipChildren } @@ -222,16 +214,15 @@ final class Swift2JavaVisitor: SyntaxVisitor { self.log.debug("Import initializer: \(node.kind) '\(node.qualifiedNameForDebug)'") - let translatedSignature: TranslatedFunctionSignature + let signature: SwiftFunctionSignature do { - let swiftSignature = try SwiftFunctionSignature( + signature = try SwiftFunctionSignature( node, enclosingType: self.currentSwiftType, symbolTable: self.translator.symbolTable ) - translatedSignature = try translator.translate(swiftSignature: swiftSignature) } catch { - self.log.debug("Failed to translate: \(node.qualifiedNameForDebug); \(error)") + self.log.debug("Failed to import: \(node.qualifiedNameForDebug); \(error)") return .skipChildren } let imported = ImportedFunc( @@ -239,7 +230,7 @@ final class Swift2JavaVisitor: SyntaxVisitor { swiftDecl: node, name: "init", apiKind: .initializer, - translatedSignature: translatedSignature + functionSignature: signature ) currentType.initializers.append(imported) diff --git a/Sources/JExtractSwift/ThunkNameRegistry.swift b/Sources/JExtractSwift/ThunkNameRegistry.swift index 222958e7..3369ec62 100644 --- a/Sources/JExtractSwift/ThunkNameRegistry.swift +++ b/Sources/JExtractSwift/ThunkNameRegistry.swift @@ -37,7 +37,7 @@ package struct ThunkNameRegistry { case .setter: suffix = "$set" default: - suffix = decl.swiftSignature.parameters + suffix = decl.functionSignature.parameters .map { "_" + ($0.argumentLabel ?? "_") } .joined() }