diff --git a/Sources/JExtractSwiftLib/ImportedDecls.swift b/Sources/JExtractSwiftLib/ImportedDecls.swift index 12becf55..1f6b80d2 100644 --- a/Sources/JExtractSwiftLib/ImportedDecls.swift +++ b/Sources/JExtractSwiftLib/ImportedDecls.swift @@ -48,81 +48,12 @@ package final class ImportedNominalType: ImportedDecl { } // Backing storage for member collections - private var _initializers: [ImportedFunc] = [] - private var _methods: [ImportedFunc] = [] - private var _variables: [ImportedFunc] = [] - private var _cases: [ImportedEnumCase] = [] - private var _inheritedTypes: [SwiftType] - private var _parent: SwiftNominalTypeDeclaration? - - // Additional members from constrained extensions that only apply to this specialization - package var constrainedInitializers: [ImportedFunc] = [] - package var constrainedMethods: [ImportedFunc] = [] - package var constrainedVariables: [ImportedFunc] = [] - - package var initializers: [ImportedFunc] { - get { - if let specializationBaseType { specializationBaseType.initializers + constrainedInitializers } else { _initializers } - } - set { - if let specializationBaseType { - let baseSet = Set(specializationBaseType.initializers.map { ObjectIdentifier($0) }) - constrainedInitializers = newValue.filter { !baseSet.contains(ObjectIdentifier($0)) } - } else { - _initializers = newValue - } - } - } - package var methods: [ImportedFunc] { - get { - if let specializationBaseType { specializationBaseType.methods + constrainedMethods } else { _methods } - } - set { - if let specializationBaseType { - let baseSet = Set(specializationBaseType.methods.map { ObjectIdentifier($0) }) - constrainedMethods = newValue.filter { !baseSet.contains(ObjectIdentifier($0)) } - } else { - _methods = newValue - } - } - } - package var variables: [ImportedFunc] { - get { - if let specializationBaseType { specializationBaseType.variables + constrainedVariables } else { _variables } - } - set { - if let specializationBaseType { - let baseSet = Set(specializationBaseType.variables.map { ObjectIdentifier($0) }) - constrainedVariables = newValue.filter { !baseSet.contains(ObjectIdentifier($0)) } - } else { - _variables = newValue - } - } - } - package var cases: [ImportedEnumCase] { - get { - if let specializationBaseType { specializationBaseType.cases } else { _cases } - } - set { - if let specializationBaseType { specializationBaseType.cases = newValue } else { _cases = newValue } - } - } - var inheritedTypes: [SwiftType] { - get { - if let specializationBaseType { specializationBaseType.inheritedTypes } else { _inheritedTypes } - } - set { - if let specializationBaseType { specializationBaseType.inheritedTypes = newValue } else { _inheritedTypes = newValue } - } - } - package var parent: SwiftNominalTypeDeclaration? { - get { - if let specializationBaseType { specializationBaseType.parent } else { _parent } - } - set { - if let specializationBaseType { specializationBaseType.parent = newValue } else { _parent = newValue } - } - } + package var initializers: [ImportedFunc] = [] + package var methods: [ImportedFunc] = [] + package var variables: [ImportedFunc] = [] + package var cases: [ImportedEnumCase] = [] + var inheritedTypes: [SwiftType] + package var parent: SwiftNominalTypeDeclaration? /// The Swift base type name, e.g. "Box" — always the unparameterized name package var baseTypeName: String { swiftNominal.qualifiedName } @@ -150,25 +81,45 @@ package final class ImportedNominalType: ImportedDecl { init(swiftNominal: SwiftNominalTypeDeclaration, lookupContext: SwiftTypeLookupContext) throws { self.swiftNominal = swiftNominal self.specializationBaseType = nil - self._inheritedTypes = + self.inheritedTypes = swiftNominal.inheritanceTypes?.compactMap { try? SwiftType($0.type, lookupContext: lookupContext) } ?? [] - self._parent = swiftNominal.parent + self.parent = swiftNominal.parent + self.swiftType = swiftNominal.asSwiftType } /// Init for creating a specialization private init(base: ImportedNominalType, specializedTypeName: String, genericArguments: [String: String]) { self.swiftNominal = base.swiftNominal self.specializationBaseType = base + + let selfType = SwiftType.nominal( + SwiftNominalType( + parent: swiftNominal.parent?.asSwiftNominalType, + nominalTypeDecl: SwiftNominalTypeDeclaration( + name: specializedTypeName, + sourceFilePath: swiftNominal.sourceFilePath, + moduleName: swiftNominal.moduleName, + parent: swiftNominal.parent, + node: swiftNominal.syntax + ), + genericArguments: [] + ) + ) + self.initializers = base.initializers.map { $0.clone(for: selfType) } + self.methods = base.methods.map { $0.clone(for: selfType) } + self.variables = base.variables.map { $0.clone(for: selfType) } + self.cases = base.cases.map { $0.clone(for: selfType) } + self.inheritedTypes = base.inheritedTypes + self.parent = base.parent + self.specializedTypeName = specializedTypeName self.genericArguments = genericArguments - self._inheritedTypes = [] + self.swiftType = selfType } - var swiftType: SwiftType { - swiftNominal.asSwiftType - } + let swiftType: SwiftType /// Structured Java-facing type name — "FishBox" for specialized, "Box" for base package var effectiveJavaTypeName: SwiftQualifiedTypeName { @@ -258,17 +209,17 @@ struct SpecializationError: Error { public final class ImportedEnumCase: ImportedDecl, CustomStringConvertible { /// The case name - public var name: String + public let name: String /// The enum parameters - var parameters: [SwiftEnumCaseParameter] + let parameters: [SwiftEnumCaseParameter] - var swiftDecl: any DeclSyntaxProtocol + let swiftDecl: any DeclSyntaxProtocol - var enumType: SwiftNominalType + let enumType: SwiftNominalType /// A function that represents the Swift static "initializer" for cases - var caseFunction: ImportedFunc + let caseFunction: ImportedFunc init( name: String, @@ -295,6 +246,16 @@ public final class ImportedEnumCase: ImportedDecl, CustomStringConvertible { } """ } + + func clone(for parent: SwiftType) -> ImportedEnumCase { + ImportedEnumCase( + name: name, + parameters: parameters, + swiftDecl: swiftDecl, + enumType: enumType, + caseFunction: caseFunction.clone(for: parent) + ) + } } extension ImportedEnumCase: Hashable { @@ -308,17 +269,17 @@ extension ImportedEnumCase: Hashable { public final class ImportedFunc: ImportedDecl, CustomStringConvertible { /// Swift module name (e.g. the target name where a type or function was declared) - public var module: String + public let module: String /// The function name. /// e.g., "init" for an initializer or "foo" for "foo(a:b:)". - public var name: String + public let name: String - public var swiftDecl: any DeclSyntaxProtocol + public let swiftDecl: any DeclSyntaxProtocol - package var apiKind: SwiftAPIKind + package let apiKind: SwiftAPIKind - var functionSignature: SwiftFunctionSignature + let functionSignature: SwiftFunctionSignature public var signatureString: String { self.swiftDecl.signatureString @@ -403,6 +364,19 @@ public final class ImportedFunc: ImportedDecl, CustomStringConvertible { } """ } + + func clone(for parent: SwiftType) -> ImportedFunc { + var functionSignature = functionSignature + assert(functionSignature.selfParameter?.selfType != nil) + functionSignature.selfParameter?.selfType = parent + return ImportedFunc( + module: module, + swiftDecl: swiftDecl, + name: name, + apiKind: apiKind, + functionSignature: functionSignature + ) + } } extension ImportedFunc: Hashable { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift index 3455cc2f..e9f13626 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaBindingsPrinting.swift @@ -208,15 +208,6 @@ extension JNISwift2JavaGenerator { } private func printConcreteType(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - let savedPrintingTypeName = self.currentPrintingTypeName - let savedPrintingType = self.currentPrintingType - self.currentPrintingTypeName = decl.effectiveJavaTypeName - self.currentPrintingType = decl - defer { - self.currentPrintingTypeName = savedPrintingTypeName - self.currentPrintingType = savedPrintingType - } - printNominal(&printer, decl) { printer in printer.print( """ @@ -837,9 +828,8 @@ extension JNISwift2JavaGenerator { //=== Part 3: Downcall. // TODO: If we always generate a native method and a "public" method, we can actually choose our own thunk names // using the registry? - let effectiveParentName = self.currentPrintingTypeName ?? translatedDecl.parentName let downcall = - "\(effectiveParentName.fullName).\(translatedDecl.nativeFunctionName)(\(arguments.joined(separator: ", ")))" + "\(translatedDecl.parentName).\(translatedDecl.nativeFunctionName)(\(arguments.joined(separator: ", ")))" //=== Part 4: Convert the return value. if translatedFunctionSignature.result.javaType.isVoid { diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift index 1045bb3f..1f97f13e 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+JavaTranslation.swift @@ -162,14 +162,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: SwiftQualifiedTypeName - if let parentNominal = decl.parentType?.asNominalType?.nominalTypeDecl { - let importedParent = importedTypes.values.first { $0.swiftNominal === parentNominal } - parentName = importedParent?.effectiveJavaTypeName ?? parentNominal.qualifiedTypeName - } else { - parentName = SwiftQualifiedTypeName(swiftModuleName) - } + let parentName = + if let parent = decl.parentType?.asNominalTypeDeclaration { + parent.qualifiedTypeName + } else { + SwiftQualifiedTypeName(swiftModuleName) + } // Name. let javaName = javaIdentifiers.makeJavaMethodName(decl) diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift index 4d92206e..23269bc7 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator+SwiftThunkPrinting.swift @@ -302,15 +302,6 @@ extension JNISwift2JavaGenerator { } private func printConcreteTypeThunks(_ printer: inout CodePrinter, _ type: ImportedNominalType) { - let savedPrintingTypeName = self.currentPrintingTypeName - let savedPrintingType = self.currentPrintingType - self.currentPrintingTypeName = type.effectiveJavaTypeName - self.currentPrintingType = type - defer { - self.currentPrintingTypeName = savedPrintingTypeName - self.currentPrintingType = savedPrintingType - } - // Specialized types are treated as concrete even if the underlying Swift type is generic let isEffectivelyGeneric = type.swiftNominal.isGeneric && !type.isSpecialization @@ -436,13 +427,8 @@ extension JNISwift2JavaGenerator { &printer, translatedDecl, ) { printer in - if let parent = decl.parentType?.asNominalType, parent.nominalTypeDecl.isGeneric { - if self.currentPrintingType?.isSpecialization == true { - // Specializations use direct calls with concrete type, not protocol opening - self.printFunctionDowncall(&printer, decl) - } else { - self.printFunctionOpenerCall(&printer, decl) - } + if let parent = decl.parentType?.asNominalType, parent.hasGenericParameter { + self.printFunctionOpenerCall(&printer, decl) } else { self.printFunctionDowncall(&printer, decl) } @@ -599,19 +585,10 @@ extension JNISwift2JavaGenerator { let callee: String = switch decl.functionSignature.selfParameter { case .instance: - if let specializedType = self.currentPrintingType, specializedType.isSpecialization { - // For specializations, use the concrete Swift type for pointer casting - // (the cached conversion uses the raw generic type name which won't compile) - self.renderSpecializedSelfPointer( - &printer, - concreteSwiftType: specializedType.effectiveSwiftTypeName, - ) - } else { - nativeSignature.selfParameter!.conversion.render( - &printer, - "selfPointer", - ) - } + nativeSignature.selfParameter!.conversion.render( + &printer, + "selfPointer", + ) case .staticMethod(let selfType), .initializer(let selfType): "\(selfType)" case .none: @@ -722,7 +699,7 @@ extension JNISwift2JavaGenerator { printCDecl( &printer, javaMethodName: translatedDecl.nativeFunctionName, - parentName: self.currentPrintingTypeName ?? translatedDecl.parentName, + parentName: translatedDecl.parentName, parameters: parameters, resultType: nativeSignature.result.javaType, ) { printer in @@ -982,27 +959,6 @@ extension JNISwift2JavaGenerator { } } - /// Renders self pointer extraction for a specialized (concrete) type. - /// Used instead of the generic opener mechanism when we know the exact type at compile time. - /// - /// - Returns: name of the created "self" variable (e.g., "selfPointer$") - private func renderSpecializedSelfPointer( - _ printer: inout CodePrinter, - concreteSwiftType: String, - ) -> String { - printer.print( - """ - assert(selfPointer != 0, "selfPointer memory address was null") - let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: environment)) - let selfPointer$ = UnsafeMutablePointer<\(concreteSwiftType)>(bitPattern: selfPointerBits$) - guard let selfPointer$ else { - fatalError("selfPointer memory address was null in call to \\(#function)!") - } - """ - ) - return "selfPointer$.pointee" - } - /// Print the necessary conversion logic to go from a `jlong` to a `UnsafeMutablePointer` /// /// - Returns: name of the created "self" variable diff --git a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift index e1006a8c..adcef96f 100644 --- a/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift +++ b/Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift @@ -56,15 +56,6 @@ package class JNISwift2JavaGenerator: Swift2JavaGenerator { /// Duplicate identifier tracking for the current batch of methods being generated. var currentJavaIdentifiers: JavaIdentifierFactory = JavaIdentifierFactory() - /// 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: SwiftQualifiedTypeName? - - /// The type currently being printed (Java class or Swift thunks). - /// Used to determine specialization context for correct code generation - var currentPrintingType: ImportedNominalType? - /// Because we need to write empty files for SwiftPM, keep track which files we didn't write yet, /// and write an empty file for those. /// diff --git a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift index cb58e819..b780eb70 100644 --- a/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift +++ b/Sources/JExtractSwiftLib/Swift2JavaTranslator.swift @@ -138,12 +138,12 @@ extension Swift2JavaTranslator { let dataProtocolDecl = (self.symbolTable[.foundationDataProtocol] ?? self.symbolTable[.essentialsDataProtocol])! if self.isUsing(where: { $0 == dataDecl || $0 == dataProtocolDecl }) { visitor.visit( - nominalDecl: dataDecl.syntax!.asNominal!, + nominalDecl: dataDecl.syntax.asNominal!, in: nil, sourceFilePath: "Foundation/FAKE_FOUNDATION_DATA.swift", ) visitor.visit( - nominalDecl: dataProtocolDecl.syntax!.asNominal!, + nominalDecl: dataProtocolDecl.syntax.asNominal!, in: nil, sourceFilePath: "Foundation/FAKE_FOUNDATION_DATAPROTOCOL.swift", ) @@ -154,7 +154,7 @@ extension Swift2JavaTranslator { if let dateDecl = self.symbolTable[.foundationDate] ?? self.symbolTable[.essentialsDate] { if self.isUsing(where: { $0 == dateDecl }) { visitor.visit( - nominalDecl: dateDecl.syntax!.asNominal!, + nominalDecl: dateDecl.syntax.asNominal!, in: nil, sourceFilePath: "Foundation/FAKE_FOUNDATION_DATE.swift", ) @@ -278,7 +278,7 @@ extension Swift2JavaTranslator { return nil } - guard swiftNominalDecl.syntax!.shouldExtract(config: config, log: log, in: nil) else { + guard swiftNominalDecl.syntax.shouldExtract(config: config, log: log, in: nil) else { return nil } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift index 805f0b49..a957c0ea 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftFunctionSignature.swift @@ -69,9 +69,21 @@ enum SwiftSelfParameter: Equatable { case initializer(SwiftType) var selfType: SwiftType { - switch self { - case .instance(_, let swiftType), .staticMethod(let swiftType), .initializer(let swiftType): - return swiftType + get { + switch self { + case .instance(_, let swiftType), .staticMethod(let swiftType), .initializer(let swiftType): + return swiftType + } + } + set { + switch self { + case .instance(let convention, _): + self = .instance(convention: convention, swiftType: newValue) + case .staticMethod: + self = .staticMethod(newValue) + case .initializer: + self = .initializer(newValue) + } } } } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift index 21edbfd9..6a9ab6ca 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftNominalTypeDeclaration.swift @@ -63,8 +63,7 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration { } /// The syntax node this declaration is derived from. - /// Can be `nil` if this is loaded from a .swiftmodule. - let syntax: NominalTypeDeclSyntaxNode? + let syntax: NominalTypeDeclSyntaxNode /// The kind of nominal type. let kind: Kind @@ -78,17 +77,18 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration { /// Identify this nominal declaration as one of the known standard library /// types, like 'Swift.Int[. - lazy var knownTypeKind: SwiftKnownTypeDeclKind? = { + private(set) lazy var knownTypeKind: SwiftKnownTypeDeclKind? = { self.computeKnownStandardLibraryType() }() /// Create a nominal type declaration from the syntax node for a nominal type /// declaration. init( + name: String, sourceFilePath: String, moduleName: String, parent: SwiftNominalTypeDeclaration?, - node: NominalTypeDeclSyntaxNode + node: NominalTypeDeclSyntaxNode, ) { self.parent = parent self.syntax = node @@ -106,11 +106,11 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration { case .structDecl: self.kind = .struct default: fatalError("Not a nominal type declaration") } - super.init(sourceFilePath: sourceFilePath, moduleName: moduleName, name: node.name.text) + super.init(sourceFilePath: sourceFilePath, moduleName: moduleName, name: name) } - lazy var firstInheritanceType: TypeSyntax? = { - guard let firstInheritanceType = self.syntax?.inheritanceClause?.inheritedTypes.first else { + private(set) lazy var firstInheritanceType: TypeSyntax? = { + guard let firstInheritanceType = self.syntax.inheritanceClause?.inheritedTypes.first else { return nil } @@ -118,13 +118,13 @@ package class SwiftNominalTypeDeclaration: SwiftTypeDeclaration { }() var inheritanceTypes: InheritedTypeListSyntax? { - self.syntax?.inheritanceClause?.inheritedTypes + self.syntax.inheritanceClause?.inheritedTypes } /// Returns true if this type conforms to `Sendable` and therefore is "threadsafe". - lazy var isSendable: Bool = { + private(set) lazy var isSendable: Bool = { // Check if Sendable is in the inheritance list - guard let inheritanceClause = self.syntax?.inheritanceClause else { + guard let inheritanceClause = self.syntax.inheritanceClause else { return false } diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift index 900f2c00..8691cdfa 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftParsedModuleSymbolTableBuilder.swift @@ -124,6 +124,7 @@ extension SwiftParsedModuleSymbolTableBuilder { // Otherwise, create the nominal type declaration. let nominalTypeDecl = SwiftNominalTypeDeclaration( + name: node.name.text, sourceFilePath: sourceFilePath, moduleName: moduleName, parent: parent, diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift index 69480ee2..6c758acd 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftType.swift @@ -228,6 +228,15 @@ struct SwiftNominalType: Equatable { SwiftKnownType(kind: $0, genericArguments: genericArguments) } } + + var hasGenericParameter: Bool { + genericArguments.contains { + if case .genericParameter = $0 { + return true + } + return false + } + } } extension SwiftNominalType: CustomStringConvertible { @@ -268,7 +277,7 @@ extension SwiftNominalType.Parent: CustomStringConvertible { extension SwiftNominalType { var isSwiftJavaWrapper: Bool { - nominalTypeDecl.syntax?.attributes.contains(where: \.isSwiftJavaMacro) ?? false + nominalTypeDecl.syntax.attributes.contains(where: \.isSwiftJavaMacro) } var isProtocol: Bool { diff --git a/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift b/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift index 408ef6fd..ffc1154b 100644 --- a/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift +++ b/Sources/JExtractSwiftLib/SwiftTypes/SwiftTypeLookupContext.swift @@ -181,6 +181,7 @@ class SwiftTypeLookupContext { } return SwiftNominalTypeDeclaration( + name: node.name.text, sourceFilePath: sourceFilePath, moduleName: self.symbolTable.moduleName, parent: try parentTypeDecl(for: node), diff --git a/Tests/JExtractSwiftTests/SpecializationTests.swift b/Tests/JExtractSwiftTests/SpecializationTests.swift index 3ca1e678..47e174d2 100644 --- a/Tests/JExtractSwiftTests/SpecializationTests.swift +++ b/Tests/JExtractSwiftTests/SpecializationTests.swift @@ -118,6 +118,14 @@ struct SpecializationTests { // Both wrappers delegate to the same base type #expect(fishBox.specializationBaseType === toolBox.specializationBaseType, "Both should wrap the same base Box type") #expect(fishBox.specializationBaseType === translator.importedTypes["Box"], "Base should be the original Box") + + // Both wrappers have owned method models + let baseCountFunc: ImportedFunc = try #require(baseBox.methods.first(where: { $0.name == "count" })) + let fishCountFunc: ImportedFunc = try #require(fishBox.methods.first(where: { $0.name == "count" })) + let toolCountFunc: ImportedFunc = try #require(toolBox.methods.first(where: { $0.name == "count" })) + #expect(baseCountFunc.parentType?.description == "Box") + #expect(fishCountFunc.parentType?.description == "FishBox") + #expect(toolCountFunc.parentType?.description == "ToolBox") } @Test("Specializations keyed by base type contain all entries") @@ -279,7 +287,7 @@ struct SpecializationTests { public func Java_com_example_swift_FishBox__00024observeTheFish__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong, selfTypePointer: jlong) { assert(selfPointer != 0, "selfPointer memory address was null") let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: environment)) - let selfPointer$ = UnsafeMutablePointer>(bitPattern: selfPointerBits$) + let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) guard let selfPointer$ else { fatalError("selfPointer memory address was null in call to \\(#function)!") } @@ -292,7 +300,7 @@ struct SpecializationTests { public func Java_com_example_swift_FishBox__00024count__JJ(environment: UnsafeMutablePointer!, thisClass: jclass, selfPointer: jlong, selfTypePointer: jlong) -> jlong { assert(selfPointer != 0, "selfPointer memory address was null") let selfPointerBits$ = Int(Int64(fromJNI: selfPointer, in: environment)) - let selfPointer$ = UnsafeMutablePointer>(bitPattern: selfPointerBits$) + let selfPointer$ = UnsafeMutablePointer(bitPattern: selfPointerBits$) guard let selfPointer$ else { fatalError("selfPointer memory address was null in call to \\(#function)!") }