diff --git a/Package.swift b/Package.swift index 18fda7eef..4c28d1d68 100644 --- a/Package.swift +++ b/Package.swift @@ -301,7 +301,8 @@ let package = Package( ), .target( - name: "CodePrinting" + name: "CodePrinting", + dependencies: ["SwiftJavaConfigurationShared"] ), .target( diff --git a/Sources/CodePrinting/CodePrinter+Java.swift b/Sources/CodePrinting/CodePrinter+Java.swift new file mode 100644 index 000000000..fcb5af09c --- /dev/null +++ b/Sources/CodePrinting/CodePrinter+Java.swift @@ -0,0 +1,53 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// ==== ----------------------------------------------------------------------- +// MARK: CodePrinter - Java-language helpers + +extension CodePrinter where Language == JavaLanguage { + + /// Print a Java `if () { ... }` block. + public mutating func printIfBlock( + _ condition: Any, + function: String = #function, + file: String = #fileID, + line: UInt = #line, + body: (inout CodePrinter) throws -> Void + ) rethrows { + try printBraceBlock("if (\(condition))", function: function, file: file, line: line, body: body) + } + + /// Print a Javadoc comment. + /// + /// Shape depends on `options.sourceVersion`: + /// - Java 23+: Markdown-style line comments (`///`) based on https://openjdk.org/jeps/467 + /// - Earlier versions: classic `/** ... */` block, with each body line + /// prefixed by ` * ` (just ` *` for blank paragraph separators). + /// + /// Pass paragraph breaks as blank lines (`\n\n`) inside `text`. + public mutating func printJavadocComment(_ text: String) { + let lines = text.split(separator: "\n", omittingEmptySubsequences: false) + if options.sourceVersion >= 23 { + for line in lines { + print(line.isEmpty ? "///" : "/// \(line)") + } + } else { + print("/**") + for line in lines { + print(line.isEmpty ? " *" : " * \(line)") + } + print(" */") + } + } +} diff --git a/Sources/CodePrinting/CodePrinter+Swift.swift b/Sources/CodePrinting/CodePrinter+Swift.swift new file mode 100644 index 000000000..a13146178 --- /dev/null +++ b/Sources/CodePrinting/CodePrinter+Swift.swift @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +// ==== ----------------------------------------------------------------------- +// MARK: CodePrinter - Swift-language helpers + +extension CodePrinter where Language == SwiftLanguage { + + /// Print a Swift `if { ... }` block. + /// The condition is emitted verbatim, without surrounding parentheses + /// (Swift convention). + public mutating func printIfBlock( + _ condition: Any, + function: String = #function, + file: String = #fileID, + line: UInt = #line, + body: (inout CodePrinter) throws -> Void + ) rethrows { + try printBraceBlock("if \(condition)", function: function, file: file, line: line, body: body) + } + + /// Print a Swift `guard else { ... }` block. + public mutating func printGuardBlock( + _ condition: Any, + function: String = #function, + file: String = #fileID, + line: UInt = #line, + body: (inout CodePrinter) throws -> Void + ) rethrows { + try printBraceBlock("guard \(condition) else", function: function, file: file, line: line, body: body) + } +} diff --git a/Sources/CodePrinting/CodePrinter.swift b/Sources/CodePrinting/CodePrinter.swift index 1fa3db8dd..29c1de124 100644 --- a/Sources/CodePrinting/CodePrinter.swift +++ b/Sources/CodePrinting/CodePrinter.swift @@ -21,7 +21,7 @@ import Foundation // ==== ----------------------------------------------------------------------- // MARK: CodePrinter -public struct CodePrinter: Sendable { +public struct CodePrinter: Sendable { public var contents: String = "" public var verbose: Bool = false @@ -32,11 +32,10 @@ public struct CodePrinter: Sendable { /// terminator equivalent to `.newLine`. public var emitSourceLocations: Bool = true - /// Token used to start an inline comment. Defaults to `//` for Swift- and - /// Java-style output. Hash-comment languages can flip this to `.hash` so - /// source-location trailers, `printSeparator` banners, and echo-mode - /// headers render with the right comment lead. - public var inlineCommentStyle: InlineCommentStyle = .slashSlash + /// Token used to start an inline comment. Defaults to the language's + /// `defaultInlineCommentStyle`; can be overridden per instance for + /// special cases (e.g. testing the alternative style). + public var inlineCommentStyle: InlineCommentStyle public var indentationDepth: Int = 0 { didSet { @@ -65,12 +64,19 @@ public struct CodePrinter: Sendable { case accumulateAll case flushToFileOnWrite } + + /// Per-language options (e.g. Java source version). Languages without + /// options use `Void`. Read by language-constrained extensions. + public var options: Language.Options + public init( mode: PrintMode = .flushToFileOnWrite, - inlineCommentStyle: InlineCommentStyle = .slashSlash + inlineCommentStyle: InlineCommentStyle? = nil, + options: Language.Options = Language.defaultOptions ) { self.mode = mode - self.inlineCommentStyle = inlineCommentStyle + self.inlineCommentStyle = inlineCommentStyle ?? Language.defaultInlineCommentStyle + self.options = options } public mutating func append(_ text: String) { @@ -124,27 +130,6 @@ public struct CodePrinter: Sendable { print("}", terminator, function: function, file: file, line: line) } - public mutating func printIfBlock( - _ condition: Any, - function: String = #function, - file: String = #fileID, - line: UInt = #line, - body: (inout CodePrinter) throws -> Void - ) rethrows { - try printBraceBlock("if (\(condition))", function: function, file: file, line: line, body: body) - } - - /// Print a Swift `guard else { … }` block. - public mutating func printGuardBlock( - _ condition: Any, - function: String = #function, - file: String = #fileID, - line: UInt = #line, - body: (inout CodePrinter) throws -> Void - ) rethrows { - try printBraceBlock("guard \(condition) else", function: function, file: file, line: line, body: body) - } - public mutating func printParts( _ parts: String..., terminator: PrinterTerminator = .newLine, diff --git a/Sources/CodePrinting/CodePrinterLanguage.swift b/Sources/CodePrinting/CodePrinterLanguage.swift new file mode 100644 index 000000000..77c33631a --- /dev/null +++ b/Sources/CodePrinting/CodePrinterLanguage.swift @@ -0,0 +1,75 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 Apple Inc. and the Swift.org project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of Swift.org project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import SwiftJavaConfigurationShared + +// ==== ----------------------------------------------------------------------- +// MARK: CodePrinterLanguage + +/// Phantom-type tag identifying the language a `CodePrinter` emits. +/// Generators pick a concrete language so language-specific helpers +/// (e.g. `printGuardBlock` for Swift, `printIfBlock` shaped for Java) +/// resolve at compile time via constrained extensions. +public protocol CodePrinterLanguage: Sendable { + /// Per-language printer options. Defaults to `Void` for languages + /// that don't need any (e.g. Swift today). Java carries the targeted + /// source version so helpers can pick the right syntax shape. + associatedtype Options: Sendable = Void + + /// Default lead used for inline comments (source-location trailers, + /// `printSeparator` banners). May be overridden at the printer instance + /// via `CodePrinter.inlineCommentStyle`. + static var defaultInlineCommentStyle: InlineCommentStyle { get } + + /// Default value for `CodePrinter.options`. + static var defaultOptions: Options { get } +} + +extension CodePrinterLanguage where Options == Void { + public static var defaultOptions: Void { () } +} + +// ==== ----------------------------------------------------------------------- +// MARK: Concrete languages + +/// Swift output. +public enum SwiftLanguage: CodePrinterLanguage { + public static let defaultInlineCommentStyle: InlineCommentStyle = .slashSlash +} + +/// Java output. +public enum JavaLanguage: CodePrinterLanguage { + public struct Options: Sendable { + /// Targeted Java source version. + public var sourceVersion: JavaVersion + + public init(sourceVersion: JavaVersion = 8) { + self.sourceVersion = sourceVersion + } + } + + public static let defaultInlineCommentStyle: InlineCommentStyle = .slashSlash + public static let defaultOptions = Options() +} + +// ==== ----------------------------------------------------------------------- +// MARK: Typealiases + +/// Convenience for `CodePrinter`. Use this everywhere a +/// printer's output is Swift code. +public typealias SwiftPrinter = CodePrinter + +/// Convenience for `CodePrinter`. Use this everywhere a +/// printer's output is Java code. +public typealias JavaPrinter = CodePrinter diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift index a482041c5..7b10ddc42 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+FoundationData.swift @@ -24,7 +24,7 @@ import struct Foundation.URL extension FFMSwift2JavaGenerator { /// Print Java helper methods for Foundation.Data type - package func printFoundationDataHelpers(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + package func printFoundationDataHelpers(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { let typeName = decl.swiftNominal.name let thunkNameCopyBytes = "swiftjava_\(swiftModuleName)_\(typeName)_copyBytes__" diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift index af5bb32e7..b8330a86d 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaBindingsPrinting.swift @@ -19,7 +19,7 @@ import SwiftJavaJNICore extension FFMSwift2JavaGenerator { package func printFunctionDowncallMethods( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedFunc, ) { guard let _ = translatedDecl(for: decl) else { @@ -39,7 +39,7 @@ extension FFMSwift2JavaGenerator { /// Print FFM Java binding descriptors for the imported Swift API. package func printJavaBindingDescriptorClass( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedFunc, ) { let thunkName = thunkNameRegistry.functionThunkName(decl: decl) @@ -58,10 +58,10 @@ extension FFMSwift2JavaGenerator { /// Reusable function to print the FFM Java binding descriptors for a C function. package func printJavaBindingDescriptorClass( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ cFunc: CFunction, symbolLookup: SymbolLookupTarget = .module, - additionalContent: ((inout CodePrinter) -> Void)? = nil, + additionalContent: ((inout JavaPrinter) -> Void)? = nil, ) { let lookup = symbolLookup.javaClassName(moduleName: self.swiftModuleName) printer.printBraceBlock( @@ -89,7 +89,7 @@ extension FFMSwift2JavaGenerator { /// Print the 'FunctionDescriptor' of the lowered cdecl thunk. func printFunctionDescriptorDefinition( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ resultType: CType, _ parameters: [CParameter], ) { @@ -116,7 +116,7 @@ extension FFMSwift2JavaGenerator { } func printJavaBindingDowncallMethod( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ cFunc: CFunction, ) { let returnTy = cFunc.resultType.javaType @@ -160,7 +160,7 @@ extension FFMSwift2JavaGenerator { /// * function pointer parameter as a functional interface. /// * Unnamed-struct parameter as a record. (unimplemented) func printParameterDescriptorClasses( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ cFunc: CFunction, ) { for param in cFunc.parameters { @@ -175,7 +175,7 @@ extension FFMSwift2JavaGenerator { } func printUpcallParameterDescriptorClasses( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ outCallback: OutCallback, ) { let name = outCallback.name @@ -200,7 +200,7 @@ extension FFMSwift2JavaGenerator { /// /// If a `functionBody` is provided, a `Function$Impl` class will be emitted as well. func printFunctionPointerParameterDescriptorClass( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ name: String, _ cType: CType, impl: OutCallback?, @@ -270,7 +270,7 @@ extension FFMSwift2JavaGenerator { /// /// * User-facing functional interfaces. func printJavaBindingWrapperHelperClass( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedFunc, ) { let translated = self.translatedDecl(for: decl)! @@ -292,7 +292,7 @@ extension FFMSwift2JavaGenerator { /// Print "wrapper" functional interface representing a Swift closure type. func printJavaBindingWrapperFunctionTypeHelper( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ functionType: TranslatedFunctionType, _ bindingDescriptorName: String, ) { @@ -359,7 +359,7 @@ extension FFMSwift2JavaGenerator { /// Print the calling body that forwards all the parameters to the `methodName`, /// with adding `SwiftArena.ofAuto()` at the end. package func printJavaBindingWrapperMethod( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedFunc, ) { let translated = self.translatedDecl(for: decl)! @@ -420,7 +420,7 @@ extension FFMSwift2JavaGenerator { /// /// This assumes that all the parameters are passed-in with appropriate names. package func printDowncall( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedFunc, ) { //=== Part 1: prepare temporary arena if needed. @@ -519,7 +519,7 @@ extension FFMSwift2JavaGenerator { let downCall = "\(thunkName).call(\(downCallArguments.joined(separator: ", ")))" /// Helper to emit the error check after a downcall - func printErrorCheck(_ printer: inout CodePrinter) { + func printErrorCheck(_ printer: inout JavaPrinter) { guard translatedSignature.isThrowing else { return } printer.printIfBlock("!result$throws.get(ValueLayout.ADDRESS, 0).equals(MemorySegment.NULL)") { printer in printer.print("throw new \(JavaType.swiftJavaErrorException.className!)(result$throws.get(ValueLayout.ADDRESS, 0), AllocatingSwiftArena.ofAuto());") @@ -586,7 +586,7 @@ extension FFMSwift2JavaGenerator { /// Print a return statement with an optional 32-bit integer overflow check. private func printReturnWithOverflowCheck( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, value: String, overflowCheck: OverflowCheckType, ) { @@ -705,7 +705,7 @@ extension FFMSwift2JavaGenerator.JavaConversionStep { } /// Returns the conversion string applied to the placeholder. - func render(_ printer: inout CodePrinter, _ placeholder: String, placeholderForDowncall: String? = nil) -> String { + func render(_ printer: inout JavaPrinter, _ placeholder: String, placeholderForDowncall: String? = nil) -> String { // NOTE: 'printer' is used if the conversion wants to cause side-effects. // E.g. storing a temporary values into a variable. switch self { @@ -741,7 +741,7 @@ extension FFMSwift2JavaGenerator.JavaConversionStep { case .initializeResultWithUpcall(let steps, _): // TODO: could we use the printing to introduce the upcall handle instead? return steps.map { step in - var printer = CodePrinter() + var printer = JavaPrinter() var out = "" out += step.render(&printer, placeholder, placeholderForDowncall: placeholderForDowncall) out += printer.contents diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift index 9e39cefdc..0247c7897 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+SwiftThunkPrinting.swift @@ -19,7 +19,7 @@ import SwiftSyntaxBuilder extension FFMSwift2JavaGenerator { package func writeSwiftThunkSources() throws { - var printer = CodePrinter() + var printer = SwiftPrinter() try writeSwiftThunkSources(printer: &printer) } @@ -37,7 +37,7 @@ extension FFMSwift2JavaGenerator { for expectedFileName in self.expectedOutputSwiftFileNames { log.trace("Write SwiftPM-'expected' empty file: \(expectedFileName.bold)") - var printer = CodePrinter() + var printer = SwiftPrinter() printer.print("// Empty file generated on purpose") _ = try printer.writeContents( outputDirectory: self.swiftOutputDirectory, @@ -47,7 +47,7 @@ extension FFMSwift2JavaGenerator { } } - package func writeSwiftThunkSources(printer: inout CodePrinter) throws { + package func writeSwiftThunkSources(printer: inout SwiftPrinter) throws { // Skip global thunks when generating for a single type if config.singleType == nil { let moduleFilenameBase = "\(self.swiftModuleName)Module+SwiftJava" @@ -133,7 +133,7 @@ extension FFMSwift2JavaGenerator { } } - public func printGlobalSwiftThunkSources(_ printer: inout CodePrinter) throws { + public func printGlobalSwiftThunkSources(_ printer: inout SwiftPrinter) throws { let stt = SwiftThunkTranslator(self) printer.print( @@ -157,7 +157,7 @@ extension FFMSwift2JavaGenerator { } } - public func printSwiftThunkSources(_ printer: inout CodePrinter, decl: ExtractedFunc) { + public func printSwiftThunkSources(_ printer: inout SwiftPrinter, decl: ExtractedFunc) { let stt = SwiftThunkTranslator(self) for thunk in stt.render(forFunc: decl) { @@ -166,7 +166,7 @@ extension FFMSwift2JavaGenerator { } } - package func printSwiftThunkSources(_ printer: inout CodePrinter, ty: ExtractedNominalType) throws { + package func printSwiftThunkSources(_ printer: inout SwiftPrinter, ty: ExtractedNominalType) throws { let stt = SwiftThunkTranslator(self) self.currentJavaIdentifiers = JavaIdentifierFactory( diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift index 4a9f899ce..feda997c4 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator.swift @@ -165,12 +165,12 @@ extension FFMSwift2JavaGenerator { extension FFMSwift2JavaGenerator { package func writeExportedJavaSources() throws { - var printer = CodePrinter() + var printer = JavaPrinter() try writeExportedJavaSources(printer: &printer) } /// Every imported public type becomes a public class in its own file in Java. - package func writeExportedJavaSources(printer: inout CodePrinter) throws { + package func writeExportedJavaSources(printer: inout JavaPrinter) throws { let typesToExport: [(key: String, value: ExtractedNominalType)] if let singleType = config.singleType { typesToExport = analysis.extractedTypes @@ -221,7 +221,7 @@ extension FFMSwift2JavaGenerator { /// Render the Java file contents for an imported Swift module. /// /// This includes any Swift global functions in that module, and some general type information and helpers. - func printModule(_ printer: inout CodePrinter) { + func printModule(_ printer: inout JavaPrinter) { printHeader(&printer) printPackage(&printer) printImports(&printer) @@ -244,7 +244,7 @@ extension FFMSwift2JavaGenerator { } } - func printImportedNominal(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + func printImportedNominal(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { printHeader(&printer) printPackage(&printer) printImports(&printer) // TODO: we could have some imports be driven from types used in the generated decl @@ -359,7 +359,7 @@ extension FFMSwift2JavaGenerator { } } - func printHeader(_ printer: inout CodePrinter) { + func printHeader(_ printer: inout JavaPrinter) { printer.print( """ // Generated by jextract-swift @@ -369,7 +369,7 @@ extension FFMSwift2JavaGenerator { ) } - func printPackage(_ printer: inout CodePrinter) { + func printPackage(_ printer: inout JavaPrinter) { printer.print( """ package \(javaPackage); @@ -378,7 +378,7 @@ extension FFMSwift2JavaGenerator { ) } - func printImports(_ printer: inout CodePrinter) { + func printImports(_ printer: inout JavaPrinter) { for i in FFMSwift2JavaGenerator.defaultJavaImports { printer.print("import \(i);") } @@ -386,9 +386,9 @@ extension FFMSwift2JavaGenerator { } func printNominal( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedNominalType, - body: (inout CodePrinter) -> Void, + body: (inout JavaPrinter) -> Void, ) { let isErrorType = decl.swiftNominal.isSwiftJavaErrorType @@ -425,7 +425,7 @@ extension FFMSwift2JavaGenerator { /// Returns a closure that prints the constructor and related extras for special nominal types /// (e.g. error types), or `nil` for normal types that use the default layout + constructor - func getSpecialNominalConstructorPrinting(_ decl: ExtractedNominalType) -> ((inout CodePrinter) -> Void)? { + func getSpecialNominalConstructorPrinting(_ decl: ExtractedNominalType) -> ((inout JavaPrinter) -> Void)? { if decl.swiftNominal.isSwiftJavaErrorType { return { printer in // Error constructor: wrap the opaque pointer so it becomes a pointer-to-reference @@ -449,7 +449,7 @@ extension FFMSwift2JavaGenerator { /// Returns a closure that prints post-members extras for special nominal types /// (e.g. `fetchDescription` for error types), or `nil` for normal types that use `toString()` - func getSpecialNominalPostMembersPrinting(_ decl: ExtractedNominalType) -> ((inout CodePrinter) -> Void)? { + func getSpecialNominalPostMembersPrinting(_ decl: ExtractedNominalType) -> ((inout JavaPrinter) -> Void)? { if decl.swiftNominal.isSwiftJavaErrorType { return { printer in // Error types inherit toString() from Exception; print fetchDescription helper instead @@ -459,7 +459,7 @@ extension FFMSwift2JavaGenerator { return nil } - func printModuleClass(_ printer: inout CodePrinter, body: (inout CodePrinter) -> Void) { + func printModuleClass(_ printer: inout JavaPrinter, body: (inout JavaPrinter) -> Void) { printer.printBraceBlock("public final class \(swiftModuleName)") { printer in printPrivateConstructor(&printer, swiftModuleName) @@ -545,7 +545,7 @@ extension FFMSwift2JavaGenerator { } } - func printClassConstants(printer: inout CodePrinter) { + func printClassConstants(printer: inout JavaPrinter) { printer.print( """ static final java.lang.String LIB_NAME = "\(config.nativeLibraryName ?? swiftModuleName)"; @@ -554,7 +554,7 @@ extension FFMSwift2JavaGenerator { ) } - func printPrivateConstructor(_ printer: inout CodePrinter, _ typeName: String) { + func printPrivateConstructor(_ printer: inout JavaPrinter, _ typeName: String) { printer.print( """ private \(typeName)() { @@ -569,7 +569,7 @@ extension FFMSwift2JavaGenerator { ) } - private func printClassMemoryLayout(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printClassMemoryLayout(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { printer.print( """ public static final GroupLayout $LAYOUT = (GroupLayout) SwiftValueWitnessTable.layoutOfSwiftType(TYPE_METADATA.$memorySegment()); @@ -581,7 +581,7 @@ extension FFMSwift2JavaGenerator { } func printToStringMethod( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedNominalType, ) { printer.print( @@ -599,7 +599,7 @@ extension FFMSwift2JavaGenerator { } /// Print special helper methods for known types like Foundation.Data - func printSpecificTypeHelpers(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + func printSpecificTypeHelpers(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { guard let knownType = decl.swiftNominal.knownTypeKind else { return } @@ -615,7 +615,7 @@ extension FFMSwift2JavaGenerator { /// Print the `fetchDescription` static helper for SwiftJavaError. /// This calls the `errorDescription()` downcall to get the error message /// for the super constructor - func printSwiftJavaErrorFetchDescriptionMethod(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + func printSwiftJavaErrorFetchDescriptionMethod(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { // Find the errorDescription method's thunk name let errorDescMethod = decl.methods.first { $0.name == "errorDescription" } guard let errorDescMethod, let _ = translatedDecl(for: errorDescMethod) else { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift index 41a9029a0..1d9e39bc8 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+InterfaceWrapperGeneration.swift @@ -389,7 +389,7 @@ enum UpcallConversionStep { indirect case map(UpcallConversionStep, body: UpcallConversionStep) /// Returns the conversion string applied to the placeholder. - func render(_ printer: inout CodePrinter, _ placeholder: String) -> String { + func render(_ printer: inout SwiftPrinter, _ placeholder: String) -> String { switch self { case .placeholder: return placeholder @@ -445,7 +445,7 @@ enum UpcallConversionStep { case .map(let inner, let body): let inner = inner.render(&printer, placeholder) - var printer = CodePrinter() + var printer = SwiftPrinter() printer.printBraceBlock("\(inner).map") { printer in let body = body.render(&printer, "$0") printer.print("return \(body)") diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 6a3ad1e5f..4da66f1fc 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -38,11 +38,11 @@ extension JNISwift2JavaGenerator { extension JNISwift2JavaGenerator { func writeExportedJavaSources() throws { - var printer = CodePrinter() + var printer = JavaPrinter() try writeExportedJavaSources(&printer) } - package func writeExportedJavaSources(_ printer: inout CodePrinter) throws { + package func writeExportedJavaSources(_ printer: inout JavaPrinter) throws { let typesToExport: [(key: String, value: ExtractedNominalType)] if let singleType = config.singleType { typesToExport = analysis.extractedTypes @@ -100,7 +100,7 @@ extension JNISwift2JavaGenerator { } } - private func printModule(_ printer: inout CodePrinter) { + private func printModule(_ printer: inout JavaPrinter) { printHeader(&printer) printPackage(&printer) printImports(&printer) @@ -154,7 +154,7 @@ extension JNISwift2JavaGenerator { } } - private func printImportedNominal(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printImportedNominal(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { printHeader(&printer) printPackage(&printer) printImports(&printer) @@ -171,7 +171,7 @@ extension JNISwift2JavaGenerator { } } - private func printProtocol(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printProtocol(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { var extends = self.inheritedProtocols(of: decl).map(\.effectiveJavaSimpleName) // If we cannot generate Swift wrappers @@ -207,7 +207,7 @@ extension JNISwift2JavaGenerator { } } - private func printConcreteType(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printConcreteType(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { printNominal(&printer, decl) { printer in printer.print( """ @@ -399,7 +399,7 @@ extension JNISwift2JavaGenerator { } /// Prints helpers for specific types like `Foundation.Date` - private func printSpecificTypeHelpers(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printSpecificTypeHelpers(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { guard let knownType = decl.swiftNominal.knownTypeKind else { return } switch knownType { @@ -414,7 +414,7 @@ extension JNISwift2JavaGenerator { } } - private func printHeader(_ printer: inout CodePrinter) { + private func printHeader(_ printer: inout JavaPrinter) { printer.print( """ // Generated by jextract-swift @@ -424,7 +424,7 @@ extension JNISwift2JavaGenerator { ) } - private func printPackage(_ printer: inout CodePrinter) { + private func printPackage(_ printer: inout JavaPrinter) { printer.print( """ package \(javaPackage); @@ -433,7 +433,7 @@ extension JNISwift2JavaGenerator { ) } - private func printImports(_ printer: inout CodePrinter) { + private func printImports(_ printer: inout JavaPrinter) { for i in JNISwift2JavaGenerator.defaultJavaImports { printer.print("import \(i);") } @@ -441,9 +441,9 @@ extension JNISwift2JavaGenerator { } private func printNominal( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedNominalType, - body: (inout CodePrinter) -> Void, + body: (inout JavaPrinter) -> Void, ) { if decl.swiftNominal.isSendable { printer.print("@ThreadSafe // Sendable") @@ -468,13 +468,13 @@ extension JNISwift2JavaGenerator { } } - private func printModuleClass(_ printer: inout CodePrinter, body: (inout CodePrinter) -> Void) { + private func printModuleClass(_ printer: inout JavaPrinter, body: (inout JavaPrinter) -> Void) { printer.printBraceBlock("public final class \(swiftModuleName)") { printer in body(&printer) } } - private func printEnumHelpers(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printEnumHelpers(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { printEnumDiscriminator(&printer, decl) printer.println() printEnumCaseInterface(&printer, decl) @@ -484,7 +484,7 @@ extension JNISwift2JavaGenerator { printEnumCases(&printer, decl) } - private func printEnumDiscriminator(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printEnumDiscriminator(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { if decl.cases.isEmpty { return } @@ -501,7 +501,7 @@ extension JNISwift2JavaGenerator { } } - private func printEnumCaseInterface(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printEnumCaseInterface(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { if decl.cases.isEmpty { return } @@ -558,7 +558,7 @@ extension JNISwift2JavaGenerator { } } - private func printEnumStaticInitializers(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printEnumStaticInitializers(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { let isEffectivelyGeneric = decl.swiftNominal.isGeneric && !decl.isSpecialization if !decl.cases.isEmpty && isEffectivelyGeneric { self.logger.debug("Skipping generic static initializers in '\(decl.effectiveJavaSimpleName)'") @@ -570,7 +570,7 @@ extension JNISwift2JavaGenerator { } } - private func printEnumCases(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printEnumCases(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { let caseTypeParameters: [JavaType] = decl.genericParameterNames.map { .class(package: nil, name: $0) } @@ -625,7 +625,7 @@ extension JNISwift2JavaGenerator { } private func printFunctionDowncallMethods( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedFunc, skipMethodBody: Bool = false, ) { @@ -649,7 +649,7 @@ extension JNISwift2JavaGenerator { /// /// * User-facing functional interfaces. private func printJavaBindingWrapperHelperClass( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedFunc, ) { let translated = self.translatedDecl(for: decl)! @@ -670,7 +670,7 @@ extension JNISwift2JavaGenerator { /// Print "wrapper" functional interface representing a Swift closure type. func printJavaBindingWrapperFunctionTypeHelper( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ functionType: TranslatedFunctionType, ) { let apiParams = functionType.parameters.map({ $0.parameter.renderParameter() }) @@ -686,7 +686,7 @@ extension JNISwift2JavaGenerator { } private func printNecessarySupportTypes( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedFunc ) { let translatedDecl = translatedDecl(for: decl)! @@ -697,7 +697,7 @@ extension JNISwift2JavaGenerator { } private func printJavaBindingWrapperMethod( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ decl: ExtractedFunc, skipMethodBody: Bool, ) { @@ -708,7 +708,7 @@ extension JNISwift2JavaGenerator { } private func printJavaBindingWrapperMethod( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ translatedDecl: TranslatedFunctionDecl, importedFunc: ExtractedFunc? = nil, skipMethodBody: Bool, @@ -806,7 +806,7 @@ extension JNISwift2JavaGenerator { } } - private func printNativeFunction(_ printer: inout CodePrinter, _ translatedDecl: TranslatedFunctionDecl) { + private func printNativeFunction(_ printer: inout JavaPrinter, _ translatedDecl: TranslatedFunctionDecl) { let nativeSignature = translatedDecl.nativeFunctionSignature let resultType = nativeSignature.result.javaType var parameters = nativeSignature.parameters.flatMap(\.parameters) @@ -826,7 +826,7 @@ extension JNISwift2JavaGenerator { } private func printDowncall( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ translatedDecl: TranslatedFunctionDecl, ) { let translatedFunctionSignature = translatedDecl.translatedFunctionSignature @@ -879,7 +879,7 @@ extension JNISwift2JavaGenerator { } } - private func printTypeMetadataAddressFunction(_ printer: inout CodePrinter, _ type: ExtractedNominalType) { + private func printTypeMetadataAddressFunction(_ printer: inout JavaPrinter, _ type: ExtractedNominalType) { let isEffectivelyGeneric = type.swiftNominal.isGeneric && !type.isSpecialization if isEffectivelyGeneric { printer.print("@Override") @@ -897,7 +897,7 @@ extension JNISwift2JavaGenerator { } } - private func printFoundationDateHelpers(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printFoundationDateHelpers(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { printer.print( """ /** @@ -950,7 +950,7 @@ extension JNISwift2JavaGenerator { ) } - private func printFoundationDataHelpers(_ printer: inout CodePrinter, _ decl: ExtractedNominalType) { + private func printFoundationDataHelpers(_ printer: inout JavaPrinter, _ decl: ExtractedNominalType) { printer.print( """ /** diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index f4a5f5f1a..0ce3291f8 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -1816,7 +1816,7 @@ extension JNISwift2JavaGenerator { } /// Returns the conversion string applied to the placeholder. - func render(_ printer: inout CodePrinter, _ placeholder: String) -> String { + func render(_ printer: inout JavaPrinter, _ placeholder: String) -> String { // NOTE: 'printer' is used if the conversion wants to cause side-effects. // E.g. storing a temporary values into a variable. switch self { @@ -1950,7 +1950,7 @@ extension JNISwift2JavaGenerator { return inner.render(&printer, placeholder) case .lambda(let args, let body): - var printer = CodePrinter() + var printer = JavaPrinter() printer.printBraceBlock("(\(args.joined(separator: ", "))) ->") { printer in let body = body.render(&printer, placeholder) if !body.isEmpty { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+LabeledTuples.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+LabeledTuples.swift index 0250dd390..2dd178f7a 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+LabeledTuples.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+LabeledTuples.swift @@ -32,7 +32,7 @@ extension JNISwift2JavaGenerator { /// } /// ``` func printAdHocLabeledTupleStaticClass( - _ printer: inout CodePrinter, + _ printer: inout JavaPrinter, _ labeledTupleType: JavaType ) { guard labeledTupleType.isSwiftJavaLabeledTuple else { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index 60bc9b707..e9212ed69 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -1328,7 +1328,7 @@ extension JNISwift2JavaGenerator { } /// Returns the conversion string applied to the placeholder. - func render(_ printer: inout CodePrinter, _ placeholder: String) -> String { + func render(_ printer: inout SwiftPrinter, _ placeholder: String) -> String { // NOTE: 'printer' is used if the conversion wants to cause side-effects. // E.g. storing a temporary values into a variable. switch self { @@ -1368,7 +1368,7 @@ extension JNISwift2JavaGenerator { let existentialType = SwiftKitPrinting.renderExistentialType(protocolTypes) printer.print("let \(variableName): \(existentialType)") - func printStandardJExtractBlock(_ printer: inout CodePrinter) { + func printStandardJExtractBlock(_ printer: inout SwiftPrinter) { let pointerVariableName = "\(inner)pointer$" let typeMetadataVariableName = "\(inner)typeMetadata$" printer.print( @@ -1492,7 +1492,7 @@ extension JNISwift2JavaGenerator { return "\(inner).pointee" case .closureLowering(let parameters, let nativeResult): - var printer = CodePrinter() + var printer = SwiftPrinter() let methodSignature = MethodSignature( resultType: nativeResult.javaType, @@ -1541,7 +1541,7 @@ extension JNISwift2JavaGenerator { return printer.finalize() case .escapingClosureLowering(let syntheticFunction, let closureName): - var printer = CodePrinter() + var printer = SwiftPrinter() let fn = syntheticFunction.functionType let parameterNames = fn.parameters.enumerated().map { idx, param in @@ -1553,7 +1553,7 @@ extension JNISwift2JavaGenerator { // Build upcall arguments using UpcallConversionStep conversions var upcallArguments: [String] = [] for (idx, conversion) in syntheticFunction.parameterConversions.enumerated() { - var argPrinter = CodePrinter() + var argPrinter = SwiftPrinter() let paramName = parameterNames[idx] let converted = conversion.render(&argPrinter, paramName) upcallArguments.append(converted) @@ -1562,7 +1562,7 @@ extension JNISwift2JavaGenerator { // Build result conversion // Note: The Java interface is synchronous even for async closures. // The async nature is on the Swift side, inferred from the expected type. - var resultPrinter = CodePrinter() + var resultPrinter = SwiftPrinter() let upcallExpr = "javaInterface$.apply(\(upcallArguments.joined(separator: ", ")))" let resultConverted = syntheticFunction.resultConversion.render(&resultPrinter, upcallExpr) let resultPrefix = resultPrinter.finalize() @@ -1637,7 +1637,7 @@ extension JNISwift2JavaGenerator { if !returnType.isVoid { printer.print("let \(resultName): \(returnType.jniTypeName)") } - printer.printBraceBlock("if let innerResult$ = \(placeholder)") { printer in + printer.printIfBlock("let innerResult$ = \(placeholder)") { printer in let inner = inner.render(&printer, "innerResult$") if !returnType.isVoid { printer.print("\(resultName) = \(inner)") @@ -1710,7 +1710,7 @@ extension JNISwift2JavaGenerator { return "\(inner).\(member)" case .optionalMap(let inner): - var printer = CodePrinter() + var printer = SwiftPrinter() printer.printBraceBlock("\(placeholder).map") { printer in let inner = inner.render(&printer, "$0") printer.print("return \(inner)") @@ -1769,7 +1769,7 @@ extension JNISwift2JavaGenerator { } } - func printDo(printer: inout CodePrinter) { + func printDo(printer: inout SwiftPrinter) { // Make sure try/await are printed when necessary and avoid duplicate, or wrong-order, keywords (which would cause warnings) let placeholderWithoutTry = if placeholder.hasPrefix("try ") { @@ -1810,7 +1810,7 @@ extension JNISwift2JavaGenerator { } } - func printTaskBody(printer: inout CodePrinter) { + func printTaskBody(printer: inout SwiftPrinter) { if let selfParameter = nativeFunctionSignature.selfParameter { for parameter in selfParameter.parameters { printer.print("let \(parameter.name)$ = \(parameter.name)Sendable$") @@ -1849,7 +1849,7 @@ extension JNISwift2JavaGenerator { printer.print("var task: Task? = nil") printer.printHashIfBlock("swift(>=6.2)") { printer in - printer.printBraceBlock("if #available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *)") { printer in + printer.printIfBlock("#available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, *)") { printer in printer.printBraceBlock("task = Task.immediate") { printer in // Even immediate tasks are a sending closure in Swift 6.2+, so reattach instead of capturing the caller's environment directly. printer.print("var environment = try! JavaVirtualMachine.shared().environment()") @@ -1858,7 +1858,7 @@ extension JNISwift2JavaGenerator { } } - printer.printBraceBlock("if task == nil") { printer in + printer.printIfBlock("task == nil") { printer in printer.printBraceBlock("task = Task") { printer in // We can be on any thread, so we need to attach the thread. printer.print("var environment = try! JavaVirtualMachine.shared().environment()") @@ -1869,7 +1869,7 @@ extension JNISwift2JavaGenerator { return "" case .closure(let args, let body): - var printer = CodePrinter() + var printer = SwiftPrinter() printer.printBraceBlock("", parameters: args) { printer in let body = body.render(&printer, placeholder) if !body.isEmpty { @@ -1955,7 +1955,7 @@ extension JNISwift2JavaGenerator { case check32BitIntOverflow(typeWithMinAndMax: SwiftType) // Returns the check string - func render(_ printer: inout CodePrinter, _ placeholder: String) -> String { + func render(_ printer: inout SwiftPrinter, _ placeholder: String) -> String { switch self { case .check32BitIntOverflow(let minMaxSource): return "\(placeholder) >= \(minMaxSource).min && \(placeholder) <= \(minMaxSource).max" diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 71c30cb8a..bf0b7f603 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -26,7 +26,7 @@ import Foundation extension JNISwift2JavaGenerator { func writeSwiftThunkSources() throws { - var printer = CodePrinter() + var printer = SwiftPrinter() try writeSwiftThunkSources(&printer) } @@ -44,7 +44,7 @@ extension JNISwift2JavaGenerator { for expectedFileName in self.expectedOutputSwiftFileNames { logger.trace("Write SwiftPM-'expected' empty file: \(expectedFileName.bold)") - var printer = CodePrinter() + var printer = SwiftPrinter() printer.print("// Empty file generated on purpose") _ = try printer.writeContents( outputDirectory: self.swiftOutputDirectory, @@ -54,7 +54,7 @@ extension JNISwift2JavaGenerator { } } - package func writeSwiftThunkSources(_ printer: inout CodePrinter) throws { + package func writeSwiftThunkSources(_ printer: inout SwiftPrinter) throws { let moduleFilenameBase = "\(self.swiftModuleName)Module+SwiftJava" let moduleFilename = "\(moduleFilenameBase).swift" @@ -179,7 +179,7 @@ extension JNISwift2JavaGenerator { /// Prints the extension needed to make allow upcalls from Swift to Java for protocols private func printSwiftInterfaceWrapper( - _ printer: inout CodePrinter, + _ printer: inout SwiftPrinter, _ translatedWrapper: JavaInterfaceSwiftWrapper, ) throws { let inheritedWrappers = self.inheritedProtocols(of: translatedWrapper.importedType).compactMap { self.interfaceProtocolWrappers[$0] } @@ -213,7 +213,7 @@ extension JNISwift2JavaGenerator { } private func printInterfaceWrapperFunctionImpl( - _ printer: inout CodePrinter, + _ printer: inout SwiftPrinter, _ function: JavaInterfaceSwiftWrapper.Function, inside wrapper: JavaInterfaceSwiftWrapper, ) throws { @@ -269,7 +269,7 @@ extension JNISwift2JavaGenerator { } private func printerInterfaceWrapperVariable( - _ printer: inout CodePrinter, + _ printer: inout SwiftPrinter, _ variable: JavaInterfaceSwiftWrapper.Variable, inside wrapper: JavaInterfaceSwiftWrapper, ) { @@ -288,7 +288,7 @@ extension JNISwift2JavaGenerator { } } - private func printGlobalSwiftThunkSources(_ printer: inout CodePrinter) throws { + private func printGlobalSwiftThunkSources(_ printer: inout SwiftPrinter) throws { printHeader(&printer) self.currentJavaIdentifiers = JavaIdentifierFactory( @@ -306,7 +306,7 @@ extension JNISwift2JavaGenerator { } } - private func printNominalTypeThunks(_ printer: inout CodePrinter, _ type: ExtractedNominalType) throws { + private func printNominalTypeThunks(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) throws { printHeader(&printer) printer.println() @@ -322,7 +322,7 @@ extension JNISwift2JavaGenerator { } } - private func printConcreteTypeThunks(_ printer: inout CodePrinter, _ type: ExtractedNominalType) { + private func printConcreteTypeThunks(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) { // Specialized types are treated as concrete even if the underlying Swift type is generic let isEffectivelyGeneric = type.swiftNominal.isGeneric && !type.isSpecialization @@ -371,7 +371,7 @@ extension JNISwift2JavaGenerator { printer.println() } - private func printProtocolThunks(_ printer: inout CodePrinter, _ type: ExtractedNominalType) throws { + private func printProtocolThunks(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) throws { guard let protocolWrapper = self.interfaceProtocolWrappers[type] else { return } @@ -379,7 +379,7 @@ extension JNISwift2JavaGenerator { try printSwiftInterfaceWrapper(&printer, protocolWrapper) } - private func printEnumRawDiscriminator(_ printer: inout CodePrinter, _ type: ExtractedNominalType) { + private func printEnumRawDiscriminator(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) { if type.cases.isEmpty { return } @@ -395,7 +395,7 @@ extension JNISwift2JavaGenerator { } } - private func printEnumCase(_ printer: inout CodePrinter, _ enumType: ExtractedNominalType, _ enumCase: ExtractedEnumCase) { + private func printEnumCase(_ printer: inout SwiftPrinter, _ enumType: ExtractedNominalType, _ enumCase: ExtractedEnumCase) { guard let translatedCase = self.translatedEnumCase(for: enumCase) else { return } @@ -417,7 +417,7 @@ extension JNISwift2JavaGenerator { } private func printEnumGetAsCaseThunk( - _ printer: inout CodePrinter, + _ printer: inout SwiftPrinter, _ enumType: ExtractedNominalType, _ enumCase: TranslatedEnumCase, ) { @@ -430,7 +430,7 @@ extension JNISwift2JavaGenerator { let params = enumCase.original.parameters.enumerated().map { i, param in param.name ?? "_\(i)" }.joined(separator: ", ") - printer.printBraceBlock("if case let .\(enumCase.original.name)(\(params)) = self") { printer in + printer.printIfBlock("case let .\(enumCase.original.name)(\(params)) = self") { printer in printer.print("return (\(params))") } printer.print("return nil") @@ -442,7 +442,7 @@ extension JNISwift2JavaGenerator { } private func printSwiftFunctionThunk( - _ printer: inout CodePrinter, + _ printer: inout SwiftPrinter, _ decl: ExtractedFunc, ) { guard let translatedDecl = translatedDecl(for: decl) else { @@ -465,7 +465,7 @@ extension JNISwift2JavaGenerator { } private func printSwiftFunctionHelperClasses( - _ printer: inout CodePrinter, + _ printer: inout SwiftPrinter, _ decl: ExtractedFunc, ) { let protocolParameters = decl.functionSignature.parameters.compactMap { parameter in @@ -556,7 +556,7 @@ extension JNISwift2JavaGenerator { } private func printFunctionDowncall( - _ printer: inout CodePrinter, + _ printer: inout SwiftPrinter, _ decl: ExtractedFunc, ) { guard let translatedDecl = self.translatedDecl(for: decl) else { @@ -675,7 +675,7 @@ extension JNISwift2JavaGenerator { } // Lower the result. - func innerBody(in printer: inout CodePrinter) -> String { + func innerBody(in printer: inout SwiftPrinter) -> String { let loweredResult = nativeSignature.result.conversion.render(&printer, result) if !decl.functionSignature.result.type.isVoid { @@ -710,9 +710,9 @@ extension JNISwift2JavaGenerator { } private func printCDecl( - _ printer: inout CodePrinter, + _ printer: inout SwiftPrinter, _ translatedDecl: TranslatedFunctionDecl, - _ body: (inout CodePrinter) -> Void, + _ body: (inout SwiftPrinter) -> Void, ) { let nativeSignature = translatedDecl.nativeFunctionSignature var parameters = nativeSignature.parameters.flatMap(\.parameters) @@ -737,12 +737,12 @@ extension JNISwift2JavaGenerator { } private func printCDecl( - _ printer: inout CodePrinter, + _ printer: inout SwiftPrinter, javaMethodName: String, parentName: SwiftQualifiedTypeName, parameters: [JavaParameter], resultType: JavaType, - _ body: (inout CodePrinter) -> Void, + _ body: (inout SwiftPrinter) -> Void, ) { let jniSignature = parameters.reduce(into: "") { signature, parameter in signature += parameter.type.jniTypeSignature @@ -783,7 +783,7 @@ extension JNISwift2JavaGenerator { } } - private func printJNICache(_ printer: inout CodePrinter, _ type: ExtractedNominalType) { + private func printJNICache(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) { let cacheName = JNICaching.cacheName(for: type) let jniClassName = "\(javaPackagePath)/\(type.effectiveJavaTypeName.jniEscapedName)" let isEffectivelyGeneric = type.swiftNominal.isGeneric && type.effectiveJavaTypeName == type.swiftNominal.qualifiedTypeName @@ -820,7 +820,7 @@ extension JNISwift2JavaGenerator { } } - private func printNominalJavaBridge(_ printer: inout CodePrinter, _ type: ExtractedNominalType) { + private func printNominalJavaBridge(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) { let bridgeName = JNICaching.bridgeName(for: type) let cacheName = JNICaching.cacheName(for: type) let isEffectivelyGeneric = type.swiftNominal.isGeneric && !type.isSpecialization @@ -859,7 +859,7 @@ extension JNISwift2JavaGenerator { } } - private func printHeader(_ printer: inout CodePrinter) { + private func printHeader(_ printer: inout SwiftPrinter) { // `public import` so the thunk file remains valid under // `InternalImportsByDefault` (SE-0409) printer.print( @@ -881,7 +881,7 @@ extension JNISwift2JavaGenerator { self.lookupContext.symbolTable.printImportedModules(&printer) } - private func printTypeMetadataAddressThunk(_ printer: inout CodePrinter, _ type: ExtractedNominalType) { + private func printTypeMetadataAddressThunk(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) { // Specialized types are treated as concrete let isEffectivelyGeneric = type.swiftNominal.isGeneric && !type.isSpecialization if isEffectivelyGeneric { @@ -905,7 +905,7 @@ extension JNISwift2JavaGenerator { } /// Prints thunks for specific known types like Foundation.Date, Foundation.Data - private func printSpecificTypeThunks(_ printer: inout CodePrinter, _ type: ExtractedNominalType) { + private func printSpecificTypeThunks(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) { guard let knownType = type.swiftNominal.knownTypeKind else { return } switch knownType { @@ -919,7 +919,7 @@ extension JNISwift2JavaGenerator { } /// Prints Swift thunks for Foundation.Data helper methods - private func printFoundationDataThunks(_ printer: inout CodePrinter, _ type: ExtractedNominalType) { + private func printFoundationDataThunks(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) { let selfPointerParam = JavaParameter(name: "selfPointer", type: .long) let parentName = type.qualifiedName @@ -965,7 +965,7 @@ extension JNISwift2JavaGenerator { } } - private func printFunctionOpenerCall(_ printer: inout CodePrinter, _ decl: ExtractedFunc) { + private func printFunctionOpenerCall(_ printer: inout SwiftPrinter, _ decl: ExtractedFunc) { guard let translatedDecl = self.translatedDecl(for: decl) else { fatalError("Cannot print function opener for a function that can't be translated: \(decl)") } @@ -1005,10 +1005,10 @@ extension JNISwift2JavaGenerator { "_\(swiftModuleName)_\(type.flatName)_opener" } - private func printOpenerProtocol(_ printer: inout CodePrinter, _ type: ExtractedNominalType) { + private func printOpenerProtocol(_ printer: inout SwiftPrinter, _ type: ExtractedNominalType) { let protocolName = openerProtocolName(for: type.swiftNominal) - func printFunctionDecl(_ printer: inout CodePrinter, decl: ExtractedFunc, skipMethodBody: Bool) { + func printFunctionDecl(_ printer: inout SwiftPrinter, decl: ExtractedFunc, skipMethodBody: Bool) { guard let translatedDecl = self.translatedDecl(for: decl) else { return } let nativeSignature = translatedDecl.nativeFunctionSignature @@ -1066,7 +1066,7 @@ extension JNISwift2JavaGenerator { /// /// - Returns: name of the created "self" variable private func printSelfJLongToUnsafeMutablePointer( - _ printer: inout CodePrinter, + _ printer: inout SwiftPrinter, swiftParentName: String, _ selfPointerParam: JavaParameter, ) -> String { diff --git a/Sources/JExtractSwiftLib/SymbolTable+Printing.swift b/Sources/JExtractSwiftLib/SymbolTable+Printing.swift index 0f28ebffb..ced09797c 100644 --- a/Sources/JExtractSwiftLib/SymbolTable+Printing.swift +++ b/Sources/JExtractSwiftLib/SymbolTable+Printing.swift @@ -16,7 +16,7 @@ import CodePrinting import SwiftExtract extension SwiftSymbolTable { - package func printImportedModules(_ printer: inout CodePrinter) { + package func printImportedModules(_ printer: inout CodePrinter) { let mainSymbolSourceModules = Set( self.importedModules.values.filter { $0.alternativeModules?.isMainSourceOfSymbols ?? false }.map(\.moduleName) ) diff --git a/Sources/JExtractSwiftLib/TranslatedDocumentation.swift b/Sources/JExtractSwiftLib/TranslatedDocumentation.swift index a6b01e08b..89286054b 100644 --- a/Sources/JExtractSwiftLib/TranslatedDocumentation.swift +++ b/Sources/JExtractSwiftLib/TranslatedDocumentation.swift @@ -22,7 +22,7 @@ enum TranslatedDocumentation { importedFunc: ExtractedFunc, translatedDecl: FFMSwift2JavaGenerator.TranslatedFunctionDecl, config: Configuration, - in printer: inout CodePrinter + in printer: inout JavaPrinter ) { var documentation = SwiftDocumentationParser.parse(importedFunc.swiftDecl) @@ -42,7 +42,7 @@ enum TranslatedDocumentation { importedFunc: ExtractedFunc, translatedDecl: JNISwift2JavaGenerator.TranslatedFunctionDecl, config: Configuration, - in printer: inout CodePrinter + in printer: inout JavaPrinter ) { var documentation = SwiftDocumentationParser.parse(importedFunc.swiftDecl) @@ -62,7 +62,7 @@ enum TranslatedDocumentation { _ parsedDocumentation: SwiftDocumentation?, syntax: some DeclSyntaxProtocol, config: Configuration, - in printer: inout CodePrinter + in printer: inout JavaPrinter ) { var groups = [String]() if let summary = parsedDocumentation?.summary { @@ -104,17 +104,6 @@ enum TranslatedDocumentation { groups.append(annotationsGroup.joined(separator: "\n")) } - printer.print("/**") - let oldIdentationText = printer.indentationText - printer.indentationText += " * " - for (idx, group) in groups.enumerated() { - printer.print(group) - if idx < groups.count - 1 { - printer.print("") - } - } - printer.indentationText = oldIdentationText - printer.print(" */") - + printer.printJavadocComment(groups.joined(separator: "\n\n")) } } diff --git a/Sources/SwiftJavaToolLib/JavaDependencyResolver.swift b/Sources/SwiftJavaToolLib/JavaDependencyResolver.swift index 5a7336bf2..70d831c3c 100644 --- a/Sources/SwiftJavaToolLib/JavaDependencyResolver.swift +++ b/Sources/SwiftJavaToolLib/JavaDependencyResolver.swift @@ -93,7 +93,7 @@ public struct JavaDependencyResolver { dependencies: [JavaDependencyDescriptor], repositories: [MavenRepositoryDescriptor] ) -> String { - var p = CodePrinter() + var p = JavaPrinter() p.indentationPart = " " p.print("plugins { id 'java-library' }") diff --git a/Tests/CodePrintingTests/InlineCommentStyleTests.swift b/Tests/CodePrintingTests/InlineCommentStyleTests.swift index c51a325ff..e3f1be28a 100644 --- a/Tests/CodePrintingTests/InlineCommentStyleTests.swift +++ b/Tests/CodePrintingTests/InlineCommentStyleTests.swift @@ -22,7 +22,7 @@ struct InlineCommentStyleSuite { // MARK: Default behavior is `//` @Test func defaultStyleEmitsSlashSlashSourceLocation() { - var p = CodePrinter() + var p = SwiftPrinter() p.print("hello", .sloc, function: "fn", file: "F.swift", line: 1) #expect(p.contents.contains("// fn @ F.swift:1")) @@ -33,7 +33,7 @@ struct InlineCommentStyleSuite { // MARK: `.hash` flips comment lead @Test func hashStyleEmitsHashSourceLocation() { - var p = CodePrinter() + var p = SwiftPrinter() p.inlineCommentStyle = .hash p.print("hello", .sloc, function: "fn", file: "F.swift", line: 1) @@ -42,7 +42,7 @@ struct InlineCommentStyleSuite { } @Test func hashStyleFlipsPrintSeparatorBanner() { - var p = CodePrinter() + var p = SwiftPrinter() p.inlineCommentStyle = .hash p.printSeparator("section") @@ -55,7 +55,7 @@ struct InlineCommentStyleSuite { // MARK: emitSourceLocations off still respects style @Test func emitSourceLocationsOffSuppressesTrailerRegardlessOfStyle() { - var p = CodePrinter() + var p = SwiftPrinter() p.emitSourceLocations = false p.inlineCommentStyle = .hash p.print("hello", .sloc, function: "fn", file: "F.swift", line: 1) @@ -65,3 +65,78 @@ struct InlineCommentStyleSuite { #expect(p.contents.hasSuffix("hello\n")) } } + +@Suite("JavaPrinter.printJavadocComment") +struct PrintJavadocCommentSuite { + + // ==== ---------------------------------------------------------------------- + // MARK: Pre-Java-23: classic /** ... */ block + + @Test func classicBlockShapeForOlderJava() { + var p = JavaPrinter() + p.emitSourceLocations = false + p.options.sourceVersion = 17 + p.printJavadocComment("First line.\nSecond line.") + + let out = p.contents + #expect(out.contains("/**")) + #expect(out.contains(" * First line.")) + #expect(out.contains(" * Second line.")) + #expect(out.contains(" */")) + #expect(!out.contains("///")) + } + + @Test func blankLineBecomesBareStarSeparator() { + var p = JavaPrinter() + p.emitSourceLocations = false + p.options.sourceVersion = 17 + p.printJavadocComment("Para1\n\nPara2") + + // Blank paragraph separators render as a bare ` *` (no trailing space) + let lines = p.contents.split(separator: "\n", omittingEmptySubsequences: false).map(String.init) + #expect(lines.contains(" *")) + #expect(!lines.contains(" * ")) + } + + // ==== ---------------------------------------------------------------------- + // MARK: Java 23+: Markdown line comments (JEP 467) + + @Test func markdownLineShapeFromJava23() { + var p = JavaPrinter() + p.emitSourceLocations = false + p.options.sourceVersion = 23 + p.printJavadocComment("First line.\nSecond line.") + + let out = p.contents + #expect(out.contains("/// First line.")) + #expect(out.contains("/// Second line.")) + #expect(!out.contains("/**")) + #expect(!out.contains(" */")) + } + + @Test func markdownBlankLineIsBareTripleSlash() { + // JEP 467 - Markdown Documentation Comments (final in Java 23): + // https://openjdk.org/jeps/467 + var p = JavaPrinter() + p.emitSourceLocations = false + p.options.sourceVersion = 23 + p.printJavadocComment("A\n\nB") + + let lines = p.contents.split(separator: "\n", omittingEmptySubsequences: false).map(String.init) + #expect(lines.contains("///")) + #expect(!lines.contains("/// ")) + } + + // ==== ---------------------------------------------------------------------- + // MARK: Default version uses classic block + + @Test func defaultJavaVersionUsesClassicBlock() { + var p = JavaPrinter() // default sourceVersion is 8 + p.emitSourceLocations = false + p.printJavadocComment("hello") + + #expect(p.contents.contains("/**")) + #expect(p.contents.contains(" * hello")) + #expect(p.contents.contains(" */")) + } +} diff --git a/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift b/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift index f00a4bec4..f92ea5e46 100644 --- a/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift +++ b/Tests/JExtractSwiftTests/Asserts/TextAssertions.swift @@ -63,7 +63,6 @@ func assertOutput( try! translator.analyze(path: "/fake/Fake.swiftinterface", text: input) let output: String - var printer: CodePrinter = CodePrinter(mode: .accumulateAll) switch mode { case .ffm: let generator = FFMSwift2JavaGenerator( @@ -76,9 +75,13 @@ func assertOutput( switch renderKind { case .swift: + var printer = SwiftPrinter(mode: .accumulateAll) try generator.writeSwiftThunkSources(printer: &printer) + output = printer.finalize() case .java: + var printer = JavaPrinter(mode: .accumulateAll) try generator.writeExportedJavaSources(printer: &printer) + output = printer.finalize() } case .jni: @@ -94,12 +97,15 @@ func assertOutput( switch renderKind { case .swift: + var printer = SwiftPrinter(mode: .accumulateAll) try generator.writeSwiftThunkSources(&printer) + output = printer.finalize() case .java: + var printer = JavaPrinter(mode: .accumulateAll) try generator.writeExportedJavaSources(&printer) + output = printer.finalize() } } - output = printer.finalize() let sourceLocation = SourceLocation(fileID: fileID, filePath: filePath, line: line, column: column) for notExpectedChunk in notExpectedChunks { diff --git a/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift index 7aa13994b..39bd94311 100644 --- a/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift +++ b/Tests/JExtractSwiftTests/FuncCallbackImportTests.swift @@ -57,7 +57,7 @@ final class FuncCallbackImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printFunctionDowncallMethods(&printer, funcDecl) } @@ -146,7 +146,7 @@ final class FuncCallbackImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printFunctionDowncallMethods(&printer, funcDecl) } @@ -262,7 +262,7 @@ final class FuncCallbackImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printFunctionDowncallMethods(&printer, funcDecl) } diff --git a/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift b/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift index 7bdefe2ea..86a799279 100644 --- a/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift +++ b/Tests/JExtractSwiftTests/FunctionDescriptorImportTests.swift @@ -255,7 +255,7 @@ extension FunctionDescriptorTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printJavaBindingDescriptorClass(&printer, funcDecl) } @@ -295,7 +295,7 @@ extension FunctionDescriptorTests { fatalError("Cannot find descriptor of: \(identifier)") } - let getOutput = CodePrinter.toString { printer in + let getOutput = JavaPrinter.toString { printer in generator.printJavaBindingDescriptorClass(&printer, accessorDecl) } diff --git a/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift b/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift index ffff38b8b..9bb75bb74 100644 --- a/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift +++ b/Tests/JExtractSwiftTests/JNI/JNIEnumTests.swift @@ -358,7 +358,7 @@ struct JNIEnumTests { let translator = makeSwiftJavaAnalyzer(config: config) try! translator.analyze(path: "/fake/Fake.swiftinterface", text: input) - var printer: CodePrinter = CodePrinter(mode: .accumulateAll) + var printer: SwiftPrinter = SwiftPrinter(mode: .accumulateAll) let generator = JNISwift2JavaGenerator( config: config, translator: translator, diff --git a/Tests/JExtractSwiftTests/MethodImportTests.swift b/Tests/JExtractSwiftTests/MethodImportTests.swift index e4c866e14..137b7e566 100644 --- a/Tests/JExtractSwiftTests/MethodImportTests.swift +++ b/Tests/JExtractSwiftTests/MethodImportTests.swift @@ -87,7 +87,7 @@ final class MethodImportTests { let funcDecl = try #require(st.extractedGlobalFuncs.first { $0.name == "helloWorld" }) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printJavaBindingWrapperMethod(&printer, funcDecl) } @@ -133,7 +133,7 @@ final class MethodImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printJavaBindingWrapperMethod(&printer, funcDecl) } @@ -182,7 +182,7 @@ final class MethodImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printJavaBindingWrapperMethod(&printer, funcDecl) } @@ -228,7 +228,7 @@ final class MethodImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printJavaBindingWrapperMethod(&printer, funcDecl) } @@ -274,7 +274,7 @@ final class MethodImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printJavaBindingWrapperMethod(&printer, funcDecl) } @@ -323,7 +323,7 @@ final class MethodImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printJavaBindingWrapperMethod(&printer, funcDecl) } @@ -368,7 +368,7 @@ final class MethodImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printJavaBindingWrapperMethod(&printer, funcDecl) } @@ -419,7 +419,7 @@ final class MethodImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printJavaBindingWrapperMethod(&printer, initDecl) } @@ -474,7 +474,7 @@ final class MethodImportTests { javaOutputDirectory: "/fake" ) - let output = CodePrinter.toString { printer in + let output = JavaPrinter.toString { printer in generator.printJavaBindingWrapperMethod(&printer, initDecl) }