Skip to content

Add Support for Static Fields to Java2Swift tool #61

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 4 commits into from
Oct 10, 2024
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
32 changes: 18 additions & 14 deletions Sources/Java2Swift/JavaTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ extension JavaTranslator {
do {
return try translateField(field)
} catch {
logUntranslated("Unable to translate '\(fullName)' field '\(field.getName())': \(error)")
logUntranslated("Unable to translate '\(fullName)' static field '\(field.getName())': \(error)")
return nil
}
}
Expand Down Expand Up @@ -347,23 +347,28 @@ extension JavaTranslator {

// Format the class declaration.
classDecl = classDecl.formatted(using: format).cast(DeclSyntax.self)

// TODO: Handle static fields in https://github.com/swiftlang/swift-java/issues/39

if staticMethods.isEmpty {

if staticMethods.isEmpty && staticFields.isEmpty {
return [classDecl]
}

// Translate static members.
var staticMembers: [DeclSyntax] = []
staticMembers.append(
contentsOf: javaClass.getMethods().compactMap {
$0.flatMap { method in
// Skip the instance methods; they were handled above.
if !method.isStatic {
return nil
}

staticMembers.append(
contentsOf: staticFields.compactMap { field in
// Translate each static field.
do {
return try translateField(field)
} catch {
logUntranslated("Unable to translate '\(fullName)' field '\(field.getName())': \(error)")
return nil
}
}
)

staticMembers.append(
contentsOf: staticMethods.compactMap { method in
// Translate each static method.
do {
return try translateMethod(
Expand All @@ -376,7 +381,6 @@ extension JavaTranslator {
return nil
}
}
}
)

if staticMembers.isEmpty {
Expand Down Expand Up @@ -447,7 +451,7 @@ extension JavaTranslator {

func translateField(_ javaField: Field) throws -> DeclSyntax {
let typeName = try getSwiftTypeNameAsString(javaField.getGenericType()!, outerOptional: true)
let fieldAttribute: AttributeSyntax = "@JavaField";
let fieldAttribute: AttributeSyntax = javaField.isStatic ? "@JavaStaticField" : "@JavaField";
return """
\(fieldAttribute)
public var \(raw: javaField.getName()): \(raw: typeName)
Expand Down
14 changes: 14 additions & 0 deletions Sources/JavaKit/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,20 @@ public macro JavaInterface(_ fullClassName: String, extends: (any AnyJavaObject.
@attached(accessor)
public macro JavaField(_ javaFieldName: String? = nil) = #externalMacro(module: "JavaKitMacros", type: "JavaFieldMacro")


/// Attached macro that turns a Swift property into one that accesses a Java static field on the underlying Java object.
///
/// The macro must be used within a specific JavaClass instance.
///
/// ```swift
/// extension JavaClass<HelloSwift> {
/// @JavaStaticField
/// var initialValue: Double
/// }
/// ```
@attached(accessor)
public macro JavaStaticField(_ javaFieldName: String? = nil) = #externalMacro(module: "JavaKitMacros", type: "JavaFieldMacro")

/// Attached macro that turns a Swift method into one that wraps a Java method on the underlying Java object.
///
/// The macro must be used in an AnyJavaObject-conforming type.
Expand Down
4 changes: 2 additions & 2 deletions Sources/JavaKitExample/JavaKitExample.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ struct HelloSwift {
}

extension JavaClass<HelloSwift> {
@JavaField
@JavaStaticField
var initialValue: Double
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be a static var?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an instance in JavaClass similar to how static methods are instance methods on their Java class. I was thinking of how to get this be a static var, but everything needs the JNIEnvironment (from my understanding), which is an instance var on all Java structs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. Java static methods/vars become non-static members on the appropriately-specialized JavaClass instance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For bonus points, one could have the @JavaStaticField macro detect the static and produce an error diagnosing the problem, but it's not really necessary. Folks shouldn't have to write these macro uses themselves.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm do we have access to the surrounding context of a macro? I know you did some work there, but I lost where that ended up.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the lexicalContext of the macro captures this information. To be very clear, I don't think this diagnostic is something that should block your PR from being merged. I just wanted to note that we can implement these kinds of checks in the macro implementation if we want.

}

Expand All @@ -117,4 +117,4 @@ struct HelloSubclass {

@JavaMethod
init(greeting: String, environment: JNIEnvironment)
}
}
Loading