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
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ extension HelloSwift: HelloSwiftNativeMethods {
fatalError("Expected subclass here")
}

// Check escaped name
assert(self.`init`(42) == 42)
assert(self._echo("Hello") == "Hello")

// Check "is" behavior
assert(newHello.is(HelloSwift.self))
assert(!newHello.is(HelloSubclass.self))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ public long init(long value) {
return value;
}

public String $echo(String value) {
return value;
}

public Predicate<Integer> lessThanTen() {
Predicate<Integer> predicate = i -> (i < 10);
return predicate;
Expand Down
2 changes: 1 addition & 1 deletion Sources/JavaStdlib/JavaIO/generated/FileDescriptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ open class FileDescriptor: JavaObject {
open func valid() -> Bool
}
extension JavaClass<FileDescriptor> {
@JavaStaticField(isFinal: true)
@JavaStaticField("in", isFinal: true)
public var `in`: FileDescriptor!

@JavaStaticField(isFinal: true)
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftJava/generated/JavaString.swift
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ open class JavaString: JavaObject {
/// ```java
/// public java.lang.String java.lang.String.repeat(int)
/// ```
@JavaMethod
@JavaMethod("repeat")
open func `repeat`(_ arg0: Int32) -> String

/// Java method `isBlank`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,32 @@
import SwiftJava
import SwiftJavaJNICore

@JavaInterface("org.swift.swiftkit.core.JNISwiftInstance")
@JavaInterface("org.swift.swiftkit.core.JNISwiftInstance", extends: JavaSwiftInstance.self)
public struct JavaJNISwiftInstance {
/// Java method `$typeMetadataAddress`.
///
/// ### Java method signature
/// ```java
/// public abstract long org.swift.swiftkit.core.JNISwiftInstance.$typeMetadataAddress()
/// ```
@JavaMethod("$typeMetadataAddress")
public func _typeMetadataAddress() -> Int64

/// Java method `$memoryAddress`.
///
/// ### Java method signature
/// ```java
/// public abstract long org.swift.swiftkit.core.SwiftInstance.$memoryAddress()
/// ```
@JavaMethod("$memoryAddress")
public func _memoryAddress() -> Int64

/// Java method `$ensureAlive`.
///
/// ### Java method signature
/// ```java
/// public default void org.swift.swiftkit.core.SwiftInstance.$ensureAlive()
/// ```
@JavaMethod("$ensureAlive")
public func _ensureAlive()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import SwiftJavaJNICore

@JavaInterface("org.swift.swiftkit.core.SwiftArena")
public struct JavaSwiftArena {

/// Java method `register`.
///
/// ### Java method signature
/// ```java
/// public abstract void org.swift.swiftkit.core.SwiftArena.register(org.swift.swiftkit.core.SwiftInstance)
/// ```
@JavaMethod
public func register(_ arg0: JavaSwiftInstance?)
}
extension JavaClass<JavaSwiftArena> {
/// Java method `ofAuto`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Auto-generated by Java-to-Swift wrapper generator.
import SwiftJava
import SwiftJavaJNICore

@JavaInterface("org.swift.swiftkit.core.SwiftInstance")
public struct JavaSwiftInstance {
/// Java method `$memoryAddress`.
///
/// ### Java method signature
/// ```java
/// public abstract long org.swift.swiftkit.core.SwiftInstance.$memoryAddress()
/// ```
@JavaMethod("$memoryAddress")
public func _memoryAddress() -> Int64

/// Java method `$ensureAlive`.
///
/// ### Java method signature
/// ```java
/// public default void org.swift.swiftkit.core.SwiftInstance.$ensureAlive()
/// ```
@JavaMethod("$ensureAlive")
public func _ensureAlive()
}
1 change: 1 addition & 0 deletions Sources/SwiftJavaRuntimeSupport/swift-java.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"classpath" : "SwiftKitCore/build/classes/java/main",
"classes" : {
"org.swift.swiftkit.core.SwiftInstance" : "JavaSwiftInstance",
"org.swift.swiftkit.core.JNISwiftInstance" : "JavaJNISwiftInstance",
"org.swift.swiftkit.core.SwiftArena" : "JavaSwiftArena"
}
Expand Down
39 changes: 24 additions & 15 deletions Sources/SwiftJavaToolLib/JavaClassTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -259,11 +259,6 @@ struct JavaClassTranslator {
continue
}

guard method.getName().isValidSwiftFunctionName else {
log.warning("Skipping method \(method.getName()) because it is not a valid Swift function name")
continue
}

addMethod(method, isNative: false)
}

Expand Down Expand Up @@ -949,8 +944,8 @@ extension JavaClassTranslator {

// --- Handle other effects
let throwsStr = javaMethod.throwsCheckedException ? "throws" : ""
let swiftMethodName = javaMethod.getName().escapedSwiftName
let swiftOptionalMethodName = "\(javaMethod.getName())Optional".escapedSwiftName
let (swiftMethodName, swiftMethodNameEscaped) = javaMethod.getName().escapedSwiftName
let (swiftOptionalMethodName, _) = "\(javaMethod.getName())Optional".escapedSwiftName

// --- Handle docs for the generated method.
// Include the original Java signature
Expand Down Expand Up @@ -987,10 +982,8 @@ extension JavaClassTranslator {
}
// Do we need to record any generic information, in order to enable type-erasure for the upcalls?
var parameters: [String] = []
// If the method name is "init", we need to explicitly specify it in the annotation
// because "init" is a Swift keyword and will be escaped in the function name via `init`
if javaMethod.getName() == "init" {
parameters.append("\"init\"")
if swiftMethodNameEscaped {
parameters.append("\"\(javaMethod.getName())\"")
}
if hasTypeEraseGenericResultType {
let returnType = javaMethod.getReturnType()!
Expand Down Expand Up @@ -1085,8 +1078,24 @@ extension JavaClassTranslator {
preferValueTypes: true,
outerOptional: .implicitlyUnwrappedOptional
)
let fieldAttribute: AttributeSyntax = javaField.isStatic ? "@JavaStaticField" : "@JavaField"
let swiftFieldName = javaField.getName().escapedSwiftName
let (swiftFieldName, swiftFieldNameIsEscaped) = javaField.getName().escapedSwiftName
var fieldAttributeStr =
if javaField.isStatic {
"@JavaStaticField"
} else {
"@JavaField"
}
var parameters: [String] = []
if swiftFieldNameIsEscaped {
parameters.append("\"\(javaField.getName())\"")
}
parameters.append("isFinal: \(javaField.isFinal)")
if !parameters.isEmpty {
fieldAttributeStr += "("
fieldAttributeStr.append(parameters.joined(separator: ", "))
fieldAttributeStr += ")"
}
let fieldAttribute: AttributeSyntax = "\(raw: fieldAttributeStr)"
let fieldAnnotations = javaField.getDeclaredAnnotations().compactMap(\.self)
let invisibleFieldAnnotations = runtimeInvisibleAnnotations.annotationsFor(field: javaField.getName())
let availableAttributes = swiftAvailableAttributes(
Expand All @@ -1109,7 +1118,7 @@ extension JavaClassTranslator {
""
}
return """
\(raw: availableAttributes.render())\(fieldAttribute)(isFinal: \(raw: javaField.isFinal))
\(raw: availableAttributes.render())\(fieldAttribute)
public var \(raw: swiftFieldName): \(raw: typeName)


Expand All @@ -1121,7 +1130,7 @@ extension JavaClassTranslator {
"""
} else {
return """
\(raw: availableAttributes.render())\(fieldAttribute)(isFinal: \(raw: javaField.isFinal))
\(raw: availableAttributes.render())\(fieldAttribute)
public var \(raw: swiftFieldName): \(raw: typeName)
"""
}
Expand Down
18 changes: 10 additions & 8 deletions Sources/SwiftJavaToolLib/StringExtras.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,19 @@ extension String {
}

/// Escape a name with backticks if it's a Swift keyword.
var escapedSwiftName: String {
if isValidSwiftIdentifier(for: .variableName) {
return self
var escapedSwiftName: (swiftName: String, escaped: Bool) {
var escaped = false
var copy = self
if starts(with: "$") {
copy = "_\(self.dropFirst())"
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Swift does not allow $ even when wrapped in backticks.
Therefore, always perform replacing.

 echo 'func `$foo`() {}' | swiftc -
<stdin>:1:6: error: cannot declare entity named '$foo'; the '$' prefix is reserved for implicitly-synthesized declarations
1 | func `$foo`() {}
  |      `- error: cannot declare entity named '$foo'; the '$' prefix is reserved for implicitly-synthesized declarations
2 | 

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Sounds good, thanks for checking the ‘’

escaped = true
}

return "`\(self)`"
}
if copy.isValidSwiftIdentifier(for: .variableName) {
return (copy, escaped)
}

/// Returns whether this is a valid Swift function name
var isValidSwiftFunctionName: Bool {
!self.starts(with: "$")
return ("`\(copy)`", true)
}

/// Replace all occurrences of one character in the string with another.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,4 +281,44 @@ final class BasicWrapJavaTests: XCTestCase {
]
)
}

func test_wrapJava_escapedSwiftName() async throws {
let classpathURL = try await compileJava(
"""
package com.example;

class MyClass {
public long init;
public static boolean $foo;
public void func() {}
public static void $bar() {}
}
"""
)

try assertWrapJavaOutput(
javaClassNames: [
"com.example.MyClass"
],
classpath: [classpathURL],
expectedChunks: [
"""
@JavaField("init", isFinal: false)
public var `init`: Int64
""",
"""
@JavaStaticField("$foo", isFinal: false)
public var _foo: Bool
""",
"""
@JavaMethod("func")
open func `func`()
""",
"""
@JavaStaticMethod("$bar")
public func _bar()
""",
]
)
}
}
Loading