Skip to content

WIP: wrap-java: print failed-to-import methods and include errors in docc #286

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion Samples/JavaProbablyPrime/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import PackageDescription
let package = Package(
name: "JavaProbablyPrime",
platforms: [
.macOS(.v10_15),
.macOS(.v15),
],

products: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===----------------------------------------------------------------------===//
//
// 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 JavaTypes

/// A type used to represent a Java type that failed to import during swift-java importing.
/// This may be because the type was not known to swift-java, or because the Java type is not
/// representable in Swift.
///
/// See comments on the imported declaration containing this type for further details.
public struct SwiftJavaFailedImportType: JavaValue {
public typealias JNIType = jobject?

public static var jvalueKeyPath: WritableKeyPath<jvalue, JNIType> { \.l }

public static var javaType: JavaType {
.class(package: "java.lang", name: "Object")
}

public init(fromJNI value: JNIType, in environment: JNIEnvironment) {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public func getJNIValue(in environment: JNIEnvironment) -> JNIType {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public static func jniMethodCall(in environment: JNIEnvironment) -> JNIMethodCall<JNIType> {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public static func jniFieldGet(in environment: JNIEnvironment) -> JNIFieldGet<JNIType> {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public static func jniFieldSet(in environment: JNIEnvironment) -> JNIFieldSet<JNIType> {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public static func jniStaticMethodCall(in environment: JNIEnvironment) -> JNIStaticMethodCall<JNIType> {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public static func jniStaticFieldGet(in environment: JNIEnvironment) -> JNIStaticFieldGet<JNIType> {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public static func jniStaticFieldSet(in environment: JNIEnvironment) -> JNIStaticFieldSet<JNIType> {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public static func jniNewArray(in environment: JNIEnvironment) -> JNINewArray {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public static func jniGetArrayRegion(in environment: JNIEnvironment) -> JNIGetArrayRegion<JNIType> {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public static func jniSetArrayRegion(in environment: JNIEnvironment) -> JNISetArrayRegion<JNIType> {
fatalError("\(Self.self) is a placeholder type that means a type failed to import, and cannot be used at runtime!")
}

public static var jniPlaceholderValue: jstring? {
nil
}
}
80 changes: 68 additions & 12 deletions Sources/SwiftJavaLib/JavaClassTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ extension JavaClassTranslator {
// Render all of the instance methods in Swift.
let instanceMethods = methods.methods.compactMap { method in
do {
return try renderMethod(method, implementedInSwift: false)
return try renderMethod(method, implementedInSwift: false, renderFailureAsBestEffortUnavailableDecl: true)
} catch {
translator.logUntranslated("Unable to translate '\(javaClass.getName())' method '\(method.getName())': \(error)")
return nil
Expand Down Expand Up @@ -461,7 +461,8 @@ extension JavaClassTranslator {
do {
return try renderMethod(
method,
implementedInSwift: true
implementedInSwift: true,

)
} catch {
translator.logUntranslated("Unable to translate '\(javaClass.getName())' method '\(method.getName())': \(error)")
Expand Down Expand Up @@ -517,14 +518,20 @@ extension JavaClassTranslator {
package func renderConstructor(
_ javaConstructor: Constructor<some AnyJavaObject>
) throws -> DeclSyntax {
let parameters = try translateParameters(javaConstructor.getParameters()) + ["environment: JNIEnvironment? = nil"]
var errors = TranslationErrorCollector(bestEffortRecover: false)

let parameters = try translateParameters(javaConstructor.getParameters(), translationErrors: &errors) + ["environment: JNIEnvironment? = nil"]
let parametersStr = parameters.map { $0.description }.joined(separator: ", ")
let throwsStr = javaConstructor.throwsCheckedException ? "throws" : ""
let accessModifier = javaConstructor.isPublic ? "public " : ""
let convenienceModifier = translateAsClass ? "convenience " : ""
let nonoverrideAttribute = translateAsClass ? "@_nonoverride " : ""
return """
/**
\(raw: errors.doccCommentsText)
*/
@JavaMethod
\(raw: errors.unavailableDueToImportErrorsText)
\(raw: nonoverrideAttribute)\(raw: accessModifier)\(raw: convenienceModifier)init(\(raw: parametersStr))\(raw: throwsStr)
"""
}
Expand All @@ -534,10 +541,13 @@ extension JavaClassTranslator {
_ javaMethod: Method,
implementedInSwift: Bool,
genericParameterClause: String = "",
whereClause: String = ""
whereClause: String = "",
renderFailureAsBestEffortUnavailableDecl: Bool = false
) throws -> DeclSyntax {
var errors = TranslationErrorCollector(bestEffortRecover: renderFailureAsBestEffortUnavailableDecl)

// Map the parameters.
let parameters = try translateParameters(javaMethod.getParameters())
let parameters = try translateParameters(javaMethod.getParameters(), translationErrors: &errors)

let parametersStr = parameters.map { $0.description }.joined(separator: ", ")

Expand Down Expand Up @@ -589,6 +599,8 @@ extension JavaClassTranslator {


return """
\(raw: errors.doccCommentsText)
\(raw: errors.unavailableDueToImportErrorsText)
\(methodAttribute)\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)\(raw: genericParameterClause)(\(raw: parametersStr))\(raw: throwsStr)\(raw: resultTypeStr)\(raw: whereClause)

\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)Optional\(raw: genericParameterClause)(\(raw: parameters.map(\.clause.description).joined(separator: ", ")))\(raw: throwsStr) -> \(raw: resultOptional)\(raw: whereClause) {
Expand All @@ -597,6 +609,8 @@ extension JavaClassTranslator {
"""
} else {
return """
\(raw: errors.doccCommentsText)
\(raw: errors.unavailableDueToImportErrorsText)
\(methodAttribute)\(raw: accessModifier)\(raw: overrideOpt)func \(raw: swiftMethodName)\(raw: genericParameterClause)(\(raw: parametersStr))\(raw: throwsStr)\(raw: resultTypeStr)\(raw: whereClause)
"""
}
Expand Down Expand Up @@ -700,21 +714,63 @@ extension JavaClassTranslator {
}

// Translate a Java parameter list into Swift parameters.
private func translateParameters(_ parameters: [Parameter?]) throws -> [FunctionParameterSyntax] {
return try parameters.compactMap { javaParameter in
private func translateParameters(_ parameters: [Parameter?], translationErrors: inout TranslationErrorCollector) throws -> [FunctionParameterSyntax] {
return try parameters.compactMap { (javaParameter) -> (FunctionParameterSyntax?) in
guard let javaParameter else { return nil }

let typeName = try translator.getSwiftTypeNameAsString(
javaParameter.getParameterizedType()!,
preferValueTypes: true,
outerOptional: .optional
)
let typeName: String
do {
typeName = try translator.getSwiftTypeNameAsString(
javaParameter.getParameterizedType()!,
preferValueTypes: true,
outerOptional: .optional
)
} catch {
translationErrors.record("Failed to convert parameter '\(javaParameter.getName())' type '\(javaParameter.getParameterizedType()!) to Swift'")
typeName = "SwiftJavaFailedImportType" // best-effort placeholder
}
let paramName = javaParameter.getName()
return "_ \(raw: paramName): \(raw: typeName)"
}
}
}

package struct TranslationErrorCollector {
package let bestEffortRecover: Bool
private var errors: [String] = []

package var doccCommentsText: String {
guard errors.count > 0 else {
return ""
}

return """
///
/// ### Swift-Java import errors
///
/// * \(errors.joined(separator: "\n /// * "))
"""
}

package var unavailableDueToImportErrorsText: String {
guard errors.count > 0 else {
return ""
}

return """
@available(*, unavailable, message: "swift-java was unable to import this method. See doc comments for import error details.")
"""
}

mutating func record(_ errorMessage: String) {
errors.append(errorMessage)
}

package init(bestEffortRecover: Bool) {
self.bestEffortRecover = bestEffortRecover
}
}

/// Helper struct that collects methods, removing any that have been overridden
/// by a covariant method.
struct MethodCollector {
Expand Down
Loading