Skip to content

[JExtract/JNI] Omit unsupported types instead of crashing #313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 14, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class MySwiftClass {
let x: Int64
let y: Int64

public let byte: UInt8 = 0
public let constant: Int64 = 100
public var mutable: Int64 = 0
public var product: Int64 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

public class MySwiftClass {

public let byte: UInt8 = 0
public var len: Int
public var cap: Int

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,11 @@ extension JNISwift2JavaGenerator {
}

private func printFunctionBinding(_ printer: inout CodePrinter, _ decl: ImportedFunc) {
guard let _ = translatedDecl(for: decl) else {
// Failed to translate. Skip.
return
}

if decl.isStatic || decl.isInitializer || !decl.hasParent {
printStaticFunctionBinding(&printer, decl)
} else {
Expand All @@ -196,7 +201,7 @@ extension JNISwift2JavaGenerator {
/// and passes it down to another native function along with the arguments
/// to call the Swift implementation.
private func printMemberMethodBindings(_ printer: inout CodePrinter, _ decl: ImportedFunc) {
let translatedDecl = translatedDecl(for: decl)
let translatedDecl = translatedDecl(for: decl)! // We will only call this method if we can translate the decl.

printDeclDocumentation(&printer, decl)
printer.printBraceBlock("public \(renderFunctionSignature(decl))") { printer in
Expand All @@ -220,7 +225,10 @@ extension JNISwift2JavaGenerator {
}

private func printInitializerBindings(_ printer: inout CodePrinter, _ decl: ImportedFunc, type: ImportedNominalType) {
let translatedDecl = translatedDecl(for: decl)
guard let translatedDecl = translatedDecl(for: decl) else {
// Failed to translate. Skip.
return
}

printDeclDocumentation(&printer, decl)
printer.printBraceBlock("public static \(renderFunctionSignature(decl))") { printer in
Expand Down Expand Up @@ -275,7 +283,9 @@ extension JNISwift2JavaGenerator {
/// `func method(x: Int, y: Int) -> Int` becomes
/// `long method(long x, long y)`
private func renderFunctionSignature(_ decl: ImportedFunc) -> String {
let translatedDecl = translatedDecl(for: decl)
guard let translatedDecl = translatedDecl(for: decl) else {
fatalError("Unable to render function signature for a function that cannot be translated: \(decl)")
}
let resultType = translatedDecl.translatedFunctionSignature.resultType
var parameters = translatedDecl.translatedFunctionSignature.parameters.map(\.asParameter)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,19 @@ import JavaTypes
extension JNISwift2JavaGenerator {
func translatedDecl(
for decl: ImportedFunc
) -> TranslatedFunctionDecl {
) -> TranslatedFunctionDecl? {
if let cached = translatedDecls[decl] {
return cached
}

let translation = JavaTranslation(swiftModuleName: self.swiftModuleName)
let translated = translation.translate(decl)
let translated: TranslatedFunctionDecl?
do {
let translation = JavaTranslation(swiftModuleName: swiftModuleName)
translated = try translation.translate(decl)
} catch {
self.logger.debug("Failed to translate: '\(decl.swiftDecl.qualifiedNameForDebug)'; \(error)")
translated = nil
}

translatedDecls[decl] = translated
return translated
Expand All @@ -32,8 +38,8 @@ extension JNISwift2JavaGenerator {
struct JavaTranslation {
let swiftModuleName: String

func translate(_ decl: ImportedFunc) -> TranslatedFunctionDecl {
let translatedFunctionSignature = translate(functionSignature: decl.functionSignature)
func translate(_ decl: ImportedFunc) throws -> TranslatedFunctionDecl {
let translatedFunctionSignature = try translate(functionSignature: decl.functionSignature)
// Types with no parent will be outputted inside a "module" class.
let parentName = decl.parentType?.asNominalType?.nominalTypeDecl.qualifiedName ?? swiftModuleName

Expand All @@ -51,31 +57,31 @@ extension JNISwift2JavaGenerator {
)
}

func translate(functionSignature: SwiftFunctionSignature, isInitializer: Bool = false) -> TranslatedFunctionSignature {
let parameters = functionSignature.parameters.enumerated().map { idx, param in
func translate(functionSignature: SwiftFunctionSignature, isInitializer: Bool = false) throws -> TranslatedFunctionSignature {
let parameters = try functionSignature.parameters.enumerated().map { idx, param in
let parameterName = param.parameterName ?? "arg\(idx))"
return translate(swiftParam: param, parameterName: parameterName)
return try translate(swiftParam: param, parameterName: parameterName)
}

return TranslatedFunctionSignature(
return try TranslatedFunctionSignature(
parameters: parameters,
resultType: translate(swiftType: functionSignature.result.type)
)
}

func translate(swiftParam: SwiftParameter, parameterName: String) -> JavaParameter {
return JavaParameter(
func translate(swiftParam: SwiftParameter, parameterName: String) throws -> JavaParameter {
return try JavaParameter(
name: parameterName,
type: translate(swiftType: swiftParam.type)
)
}

func translate(swiftType: SwiftType) -> JavaType {
func translate(swiftType: SwiftType) throws -> JavaType {
switch swiftType {
case .nominal(let nominalType):
if let knownType = nominalType.nominalTypeDecl.knownTypeKind {
guard let javaType = translate(knownType: knownType) else {
fatalError("unsupported known type: \(knownType)")
throw JavaTranslationError.unsupportedSwiftType(swiftType)
}
return javaType
}
Expand All @@ -86,7 +92,7 @@ extension JNISwift2JavaGenerator {
return .void

case .metatype, .optional, .tuple, .function, .existential, .opaque:
fatalError("unsupported type: \(self)")
throw JavaTranslationError.unsupportedSwiftType(swiftType)
}
}

Expand Down Expand Up @@ -127,4 +133,8 @@ extension JNISwift2JavaGenerator {
let parameters: [JavaParameter]
let resultType: JavaType
}

enum JavaTranslationError: Error {
case unsupportedSwiftType(SwiftType)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,11 @@ extension JNISwift2JavaGenerator {
}

private func printInitializerThunk(_ printer: inout CodePrinter, _ decl: ImportedFunc) {
let translatedDecl = translatedDecl(for: decl)
guard let translatedDecl = translatedDecl(for: decl) else {
// Failed to translate. Skip.
return
}

let typeName = translatedDecl.parentName

printCDecl(
Expand Down Expand Up @@ -146,6 +150,11 @@ extension JNISwift2JavaGenerator {
_ printer: inout CodePrinter,
_ decl: ImportedFunc
) {
guard let _ = translatedDecl(for: decl) else {
// Failed to translate. Skip.
return
}

// Free functions does not have a parent
if decl.isStatic || !decl.hasParent {
self.printSwiftStaticFunctionThunk(&printer, decl)
Expand All @@ -155,7 +164,7 @@ extension JNISwift2JavaGenerator {
}

private func printSwiftStaticFunctionThunk(_ printer: inout CodePrinter, _ decl: ImportedFunc) {
let translatedDecl = self.translatedDecl(for: decl)
let translatedDecl = self.translatedDecl(for: decl)! // We will only call this method if we can translate the decl.

printCDecl(
&printer,
Expand All @@ -172,7 +181,7 @@ extension JNISwift2JavaGenerator {
}

private func printSwiftMemberFunctionThunk(_ printer: inout CodePrinter, _ decl: ImportedFunc) {
let translatedDecl = self.translatedDecl(for: decl)
let translatedDecl = self.translatedDecl(for: decl)! // We will only call this method if can translate the decl.
let swiftParentName = decl.parentType!.asNominalTypeDeclaration!.qualifiedName

printCDecl(
Expand All @@ -199,7 +208,9 @@ extension JNISwift2JavaGenerator {
_ decl: ImportedFunc,
calleeName: String
) {
let translatedDecl = self.translatedDecl(for: decl)
guard let translatedDecl = self.translatedDecl(for: decl) else {
fatalError("Cannot print function downcall for a function that can't be translated: \(decl)")
}
let swiftReturnType = decl.functionSignature.result.type

let tryClause: String = decl.isThrowing ? "try " : ""
Expand Down
Loading