Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 65 additions & 91 deletions Sources/JExtractSwiftLib/ImportedDecls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
"""
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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<Type>`
///
/// - Returns: name of the created "self" variable
Expand Down
9 changes: 0 additions & 9 deletions Sources/JExtractSwiftLib/JNI/JNISwift2JavaGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand Down
Loading
Loading