diff --git a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift index e3a61dc7..c7347d53 100644 --- a/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/FFM/FFMSwift2JavaGenerator+JavaTranslation.swift @@ -913,10 +913,10 @@ extension FFMSwift2JavaGenerator { func translate( swiftType: SwiftType ) throws -> JavaType { - guard let nominalName = swiftType.asNominalTypeDeclaration?.name else { + guard let nominalDecl = swiftType.asNominalTypeDeclaration else { throw JavaTranslationError.unhandledType(swiftType) } - return .class(package: nil, name: nominalName) + return .class(package: nil, name: nominalDecl.qualifiedName) } } diff --git a/Sources/JExtractSwiftLib/ImportedDecls.swift b/Sources/JExtractSwiftLib/ImportedDecls.swift index e736f9a9..33c4e793 100644 --- a/Sources/JExtractSwiftLib/ImportedDecls.swift +++ b/Sources/JExtractSwiftLib/ImportedDecls.swift @@ -170,9 +170,17 @@ package final class ImportedNominalType: ImportedDecl { .nominal(.init(nominalTypeDecl: swiftNominal)) } + /// Structured Java-facing type name — "FishBox" for specialized, "Box" for base + package var effectiveJavaTypeName: SwiftQualifiedTypeName { + if let specializedTypeName { + return SwiftQualifiedTypeName(specializedTypeName) + } + return swiftNominal.qualifiedTypeName + } + /// The effective Java-facing name — "FishBox" for specialized, "Box" for base var effectiveJavaName: String { - specializedTypeName ?? swiftNominal.qualifiedName + effectiveJavaTypeName.fullName } /// The simple Java class name (no qualification) for file naming purposes diff --git a/Sources/JExtractSwiftLib/JNI/JNICaching.swift b/Sources/JExtractSwiftLib/JNI/JNICaching.swift index 07f56876..f91d7b7d 100644 --- a/Sources/JExtractSwiftLib/JNI/JNICaching.swift +++ b/Sources/JExtractSwiftLib/JNI/JNICaching.swift @@ -14,15 +14,15 @@ enum JNICaching { static func cacheName(for type: ImportedNominalType) -> String { - cacheName(for: type.effectiveJavaName) + cacheName(for: type.effectiveJavaTypeName) } static func cacheName(for type: SwiftNominalType) -> String { - cacheName(for: type.nominalTypeDecl.qualifiedName) + cacheName(for: type.nominalTypeDecl.qualifiedTypeName) } - private static func cacheName(for qualifiedName: String) -> String { - "_JNI_\(qualifiedName.replacingOccurrences(of: ".", with: "_"))" + private static func cacheName(for typeName: SwiftQualifiedTypeName) -> String { + "_JNI_\(typeName.fullFlatName)" } static func cacheMemberName(for enumCase: ImportedEnumCase) -> String { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 42c4ce38..8fcb074e 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -190,7 +190,7 @@ extension JNISwift2JavaGenerator { private func printConcreteType(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { let savedPrintingTypeName = self.currentPrintingTypeName let savedPrintingType = self.currentPrintingType - self.currentPrintingTypeName = decl.effectiveJavaName + self.currentPrintingTypeName = decl.effectiveJavaTypeName self.currentPrintingType = decl defer { self.currentPrintingTypeName = savedPrintingTypeName @@ -757,7 +757,7 @@ extension JNISwift2JavaGenerator { // using the registry? let effectiveParentName = self.currentPrintingTypeName ?? translatedDecl.parentName let downcall = - "\(effectiveParentName).\(translatedDecl.nativeFunctionName)(\(arguments.joined(separator: ", ")))" + "\(effectiveParentName.fullName).\(translatedDecl.nativeFunctionName)(\(arguments.joined(separator: ", ")))" //=== Part 4: Convert the return value. if translatedFunctionSignature.resultType.javaType.isVoid { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 53db4d40..06c43062 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -102,7 +102,7 @@ extension JNISwift2JavaGenerator { ) let methodName = "" // TODO: Used for closures, replace with better name? - let parentName = "" // TODO: Used for closures, replace with better name? + let parentName = SwiftQualifiedTypeName("") // TODO: Used for closures, replace with better name? let translatedValues = try self.translateParameters( enumCase.parameters.map { ($0.name, $0.type) }, @@ -153,7 +153,7 @@ extension JNISwift2JavaGenerator { isThrowing: false, isAsync: false, nativeFunctionName: "$\(getAsCaseName)", - parentName: enumName, + parentName: SwiftQualifiedTypeName(enumName), functionTypes: [], translatedFunctionSignature: TranslatedFunctionSignature( selfParameter: TranslatedParameter( @@ -230,12 +230,12 @@ extension JNISwift2JavaGenerator { // Types with no parent will be outputted inside a "module" class. // For specialized types, use the Java-facing name as the parent scope - let parentName: String + let parentName: SwiftQualifiedTypeName if let parentNominal = decl.parentType?.asNominalType?.nominalTypeDecl { let importedParent = importedTypes.values.first { $0.swiftNominal === parentNominal } - parentName = importedParent?.effectiveJavaName ?? parentNominal.qualifiedName + parentName = importedParent?.effectiveJavaTypeName ?? parentNominal.qualifiedTypeName } else { - parentName = swiftModuleName + parentName = SwiftQualifiedTypeName(swiftModuleName) } // Name. @@ -300,7 +300,7 @@ extension JNISwift2JavaGenerator { func translateFunctionType( name: String, swiftType: SwiftFunctionType, - parentName: String, + parentName: SwiftQualifiedTypeName, ) throws -> TranslatedFunctionType { var translatedParams: [TranslatedParameter] = [] @@ -332,7 +332,7 @@ extension JNISwift2JavaGenerator { func translate( functionSignature: SwiftFunctionSignature, methodName: String, - parentName: String, + parentName: SwiftQualifiedTypeName, ) throws -> TranslatedFunctionSignature { let parameters = try translateParameters( functionSignature.parameters.map { ($0.parameterName, $0.type) }, @@ -384,7 +384,7 @@ extension JNISwift2JavaGenerator { func translateParameters( _ parameters: [(name: String?, type: SwiftType)], methodName: String, - parentName: String, + parentName: SwiftQualifiedTypeName, genericParameters: [SwiftGenericParameterDeclaration], genericRequirements: [SwiftGenericRequirement], ) throws -> [TranslatedParameter] { @@ -405,7 +405,7 @@ extension JNISwift2JavaGenerator { func translateSelfParameter( _ selfParameter: SwiftSelfParameter?, methodName: String, - parentName: String, + parentName: SwiftQualifiedTypeName, genericParameters: [SwiftGenericParameterDeclaration], genericRequirements: [SwiftGenericRequirement], ) throws -> TranslatedParameter? { @@ -428,7 +428,7 @@ extension JNISwift2JavaGenerator { func translateSelfTypeParameter( _ selfParameter: SwiftSelfParameter?, methodName: String, - parentName: String, + parentName: SwiftQualifiedTypeName, genericParameters: [SwiftGenericParameterDeclaration], genericRequirements: [SwiftGenericRequirement], ) throws -> TranslatedParameter? { @@ -456,7 +456,7 @@ extension JNISwift2JavaGenerator { swiftType: SwiftType, parameterName: String, methodName: String, - parentName: String, + parentName: SwiftQualifiedTypeName, genericParameters: [SwiftGenericParameterDeclaration], genericRequirements: [SwiftGenericRequirement], parameterPosition: Int?, @@ -577,7 +577,7 @@ extension JNISwift2JavaGenerator { return TranslatedParameter( parameter: JavaParameter( name: parameterName, - type: .class(package: javaPackage, name: "\(parentName).\(methodName).\(parameterName)"), + type: .class(package: javaPackage, name: "\(parentName.fullName).\(methodName).\(parameterName)"), annotations: parameterAnnotations, ), conversion: .placeholder, @@ -640,7 +640,7 @@ extension JNISwift2JavaGenerator { elements: [SwiftTupleElement], parameterName: String, methodName: String, - parentName: String, + parentName: SwiftQualifiedTypeName, genericParameters: [SwiftGenericParameterDeclaration], genericRequirements: [SwiftGenericRequirement], parameterPosition: Int?, @@ -1668,7 +1668,7 @@ extension JNISwift2JavaGenerator { let nativeFunctionName: String /// The name of the Java parent scope this function is declared in - let parentName: String + let parentName: SwiftQualifiedTypeName /// Functional interfaces required for the Java method. let functionTypes: [TranslatedFunctionType] diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift index 2351ea47..954c865a 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+NativeTranslation.swift @@ -31,7 +31,7 @@ extension JNISwift2JavaGenerator { functionSignature: SwiftFunctionSignature, translatedFunctionSignature: TranslatedFunctionSignature, methodName: String, - parentName: String + parentName: SwiftQualifiedTypeName ) throws -> NativeFunctionSignature { let parameters = try zip(translatedFunctionSignature.parameters, functionSignature.parameters).map { translatedParameter, @@ -93,7 +93,7 @@ extension JNISwift2JavaGenerator { type: SwiftType, parameterName: String, methodName: String, - parentName: String, + parentName: SwiftQualifiedTypeName, genericParameters: [SwiftGenericParameterDeclaration], genericRequirements: [SwiftGenericRequirement] ) throws -> NativeParameter { @@ -343,7 +343,7 @@ extension JNISwift2JavaGenerator { elements: [SwiftTupleElement], parameterName: String, methodName: String, - parentName: String, + parentName: SwiftQualifiedTypeName, genericParameters: [SwiftGenericParameterDeclaration], genericRequirements: [SwiftGenericRequirement] ) throws -> NativeParameter { @@ -376,7 +376,7 @@ extension JNISwift2JavaGenerator { protocolType: SwiftType, methodName: String, parameterName: String, - parentName: String? + parentName: SwiftQualifiedTypeName? ) throws -> NativeParameter { switch protocolType { case .nominal(let nominalType): @@ -411,7 +411,7 @@ extension JNISwift2JavaGenerator { protocolTypes: [SwiftNominalType], methodName: String, parameterName: String, - parentName: String? + parentName: SwiftQualifiedTypeName? ) throws -> NativeParameter { // We allow Java implementations if we are able to generate the needed // Swift wrappers for all the protocol types. diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 4f0fb6c3..65eec3af 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -322,7 +322,7 @@ extension JNISwift2JavaGenerator { private func printConcreteTypeThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) { let savedPrintingTypeName = self.currentPrintingTypeName let savedPrintingType = self.currentPrintingType - self.currentPrintingTypeName = type.effectiveJavaName + self.currentPrintingTypeName = type.effectiveJavaTypeName self.currentPrintingType = type defer { self.currentPrintingTypeName = savedPrintingTypeName @@ -558,7 +558,7 @@ extension JNISwift2JavaGenerator { let swiftClassName = JNISwift2JavaGenerator.protocolParameterWrapperClassName( methodName: decl.name, parameterName: parameterName, - parentName: decl.parentType?.asNominalType?.nominalTypeDecl.qualifiedName ?? swiftModuleName, + parentName: decl.parentType?.asNominalType?.nominalTypeDecl.qualifiedTypeName ?? SwiftQualifiedTypeName(swiftModuleName), ) let implementingProtocols = protocolWrappers.map(\.wrapperName).joined(separator: ", ") @@ -791,7 +791,7 @@ extension JNISwift2JavaGenerator { private func printCDecl( _ printer: inout CodePrinter, javaMethodName: String, - parentName: String, + parentName: SwiftQualifiedTypeName, parameters: [JavaParameter], resultType: JavaType, _ body: (inout CodePrinter) -> Void, @@ -803,7 +803,7 @@ extension JNISwift2JavaGenerator { let cName = "Java_" + self.javaPackage.replacingOccurrences(of: ".", with: "_") - + "_\(parentName.replacingOccurrences(of: ".", with: "$").escapedJNIIdentifier)_" + + "_\(parentName.jniEscapedName.escapedJNIIdentifier)_" + javaMethodName.escapedJNIIdentifier + "__" + jniSignature.escapedJNIIdentifier @@ -860,7 +860,7 @@ extension JNISwift2JavaGenerator { printCDecl( &printer, javaMethodName: "$typeMetadataAddressDowncall", - parentName: type.effectiveJavaName, + parentName: type.effectiveJavaTypeName, parameters: [], resultType: .long, ) { printer in @@ -896,7 +896,7 @@ extension JNISwift2JavaGenerator { printCDecl( &printer, javaMethodName: "$toByteArray", - parentName: type.effectiveJavaName, + parentName: type.effectiveJavaTypeName, parameters: [ selfPointerParam ], @@ -917,7 +917,7 @@ extension JNISwift2JavaGenerator { printCDecl( &printer, javaMethodName: "$toByteArrayIndirectCopy", - parentName: type.effectiveJavaName, + parentName: type.effectiveJavaTypeName, parameters: [ selfPointerParam ], @@ -1081,11 +1081,11 @@ extension JNISwift2JavaGenerator { static func protocolParameterWrapperClassName( methodName: String, parameterName: String, - parentName: String?, + parentName: SwiftQualifiedTypeName?, ) -> String { let parent = if let parentName { - "\(parentName)_" + "\(parentName.fullFlatName)_" } else { "" } @@ -1095,7 +1095,7 @@ extension JNISwift2JavaGenerator { extension SwiftNominalTypeDeclaration { private var safeProtocolName: String { - self.qualifiedName.replacingOccurrences(of: ".", with: "_") + self.flatName } /// The name of the corresponding `@JavaInterface` of this type. diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index beffe9fb..76825c7b 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -55,7 +55,7 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { /// The Java-facing name of the type currently being printed. /// Used to override cached parentName in translations (needed for specializations /// where the same ImportedFunc is shared between base and specialized types) - var currentPrintingTypeName: String? + var currentPrintingTypeName: SwiftQualifiedTypeName? /// The type currently being printed (Java class or Swift thunks). /// Used to determine specialization context for correct code generation diff --git a/Sources/JExtractSwiftLib/SwiftKit+Printing.swift b/Sources/JExtractSwiftLib/SwiftKit+Printing.swift index b8ceb604..f5ac6a4a 100644 --- a/Sources/JExtractSwiftLib/SwiftKit+Printing.swift +++ b/Sources/JExtractSwiftLib/SwiftKit+Printing.swift @@ -52,7 +52,7 @@ extension SwiftKitPrinting { extension SwiftKitPrinting.Names { static func getType(module: String, nominal: ImportedNominalType) -> String { - "swiftjava_getType_\(module)_\(nominal.swiftNominal.qualifiedName)" + "swiftjava_getType_\(module)_\(nominal.swiftNominal.qualifiedTypeName.fullFlatName)" } } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift index d4414971..ba70a578 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift @@ -147,14 +147,25 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration { return SwiftKnownTypeDeclKind(rawValue: "\(moduleName).\(name)") } - package var qualifiedName: String { + /// Structured qualified type name built from the parent chain + package var qualifiedTypeName: SwiftQualifiedTypeName { if let parent = self.parent { - return parent.qualifiedName + "." + name + return SwiftQualifiedTypeName(parent.qualifiedTypeName.components + [name]) } else { - return name + return SwiftQualifiedTypeName(name) } } + package var qualifiedName: String { + qualifiedTypeName.fullName + } + + /// Like `qualifiedName` but with dots replaced by underscores, suitable for + /// use in C symbol names and Java identifiers + package var flatName: String { + qualifiedTypeName.fullFlatName + } + var isReferenceType: Bool { switch kind { case .actor, .class: diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftQualifiedTypeName.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftQualifiedTypeName.swift new file mode 100644 index 00000000..2c170ddb --- /dev/null +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftQualifiedTypeName.swift @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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 +// +//===----------------------------------------------------------------------===// + +/// A structured representation of a Swift qualified type name such as +/// `Logger.Message`, providing self-documenting conversions to the various +/// string forms needed across the codebase: +/// - **qualifiedName** (`Logger.Message`) - for Swift source +/// - **flatName** (`Logger_Message`) - for C symbols / `@_cdecl` and Java identifiers +/// - **leafName** (`Message`) - innermost component only +package struct SwiftQualifiedTypeName: Hashable, Sendable, CustomStringConvertible { + /// Name components from outermost to innermost, e.g. ["Logger", "Message"] + let components: [String] + + init(_ components: [String]) { + precondition(!components.isEmpty) + self.components = components + } + + init(_ leafName: String) { + self.components = [leafName] + } + + /// Leaf name (innermost), e.g. "Message" + var leafName: String { components.last! } + + /// Dot-separated for Swift source, e.g. "Logger.Message" + var fullName: String { components.joined(separator: ".") } + + /// Underscore-separated for C symbols and Java identifiers, e.g. "Logger_Message" + var fullFlatName: String { components.joined(separator: "_") } + + /// Dollar-separated for JNI C symbol parent names, e.g. "Logger$Message" + var jniEscapedName: String { components.joined(separator: "$") } + + /// CustomStringConvertible - uses fullName + package var description: String { fullName } + + var isNested: Bool { components.count > 1 } +} diff --git a/Sources/JExtractSwiftLib/ThunkNameRegistry.swift b/Sources/JExtractSwiftLib/ThunkNameRegistry.swift index 03e25aa5..ac783f5c 100644 --- a/Sources/JExtractSwiftLib/ThunkNameRegistry.swift +++ b/Sources/JExtractSwiftLib/ThunkNameRegistry.swift @@ -43,12 +43,13 @@ package struct ThunkNameRegistry { .joined() } - let name = - if let parent = decl.parentType { - "swiftjava_\(decl.module)_\(parent)_\(decl.name)\(suffix)" - } else { - "swiftjava_\(decl.module)_\(decl.name)\(suffix)" - } + let name: String + if let parent = decl.parentType, let nominalDecl = parent.asNominalTypeDeclaration { + let parentName = nominalDecl.flatName + name = "swiftjava_\(decl.module)_\(parentName)_\(decl.name)\(suffix)" + } else { + name = "swiftjava_\(decl.module)_\(decl.name)\(suffix)" + } let emittedCount = self.duplicateNames[name, default: 0] defer { self.duplicateNames[name] = emittedCount + 1 } diff --git a/Tests/JExtractSwiftTests/NestedTypeThunkTests.swift b/Tests/JExtractSwiftTests/NestedTypeThunkTests.swift new file mode 100644 index 00000000..cddba368 --- /dev/null +++ b/Tests/JExtractSwiftTests/NestedTypeThunkTests.swift @@ -0,0 +1,85 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 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 JExtractSwiftLib +import Testing + +final class NestedTypeThunkTests { + let input = + """ + import Swift + + public class Outer { + public class Inner { + public var value: Int + public init(value: Int) {} + public func describe() -> String { "" } + } + } + """ + + @Test("Nested type thunks: dots replaced with underscores in cdecl names") + func thunk_nestedType_swift() throws { + try assertOutput( + input: input, + .ffm, + .swift, + swiftModuleName: "FakeModule", + detectChunkByInitialLines: 1, + expectedChunks: [ + // The getType thunk should use Outer_Inner, not Outer.Inner + """ + @_cdecl("swiftjava_getType_FakeModule_Outer_Inner") + public func swiftjava_getType_FakeModule_Outer_Inner() -> UnsafeMutableRawPointer /* Any.Type */ { + return unsafeBitCast(Outer.Inner.self, to: UnsafeMutableRawPointer.self) + } + """, + // Member thunks should also use Outer_Inner + """ + @_cdecl("swiftjava_FakeModule_Outer_Inner_init_value") + public func swiftjava_FakeModule_Outer_Inner_init_value(_ value: Int, _ _result: UnsafeMutableRawPointer) { + _result.assumingMemoryBound(to: Outer.Inner.self).initialize(to: Outer.Inner(value: value)) + } + """, + """ + @_cdecl("swiftjava_FakeModule_Outer_Inner_value$get") + public func swiftjava_FakeModule_Outer_Inner_value$get(_ self: UnsafeRawPointer) -> Int { + return self.assumingMemoryBound(to: Outer.Inner.self).pointee.value + } + """, + ] + ) + } + + @Test("Nested type Java bindings: class names use underscores not dots") + func thunk_nestedType_java() throws { + try assertOutput( + input: input, + .ffm, + .java, + swiftModuleName: "FakeModule", + detectChunkByInitialLines: 1, + expectedChunks: [ + // Java class name for the inner class descriptor should not contain dots + "swiftjava_FakeModule_Outer_Inner_init_value", + "swiftjava_FakeModule_Outer_Inner_value$get", + "swiftjava_FakeModule_Outer_Inner_value$set", + ], + // Must NOT contain the dotted version + notExpectedChunks: [ + "swiftjava_FakeModule_Outer.Inner" + ] + ) + } +}