diff --git a/Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift b/Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift index 3e2ff1814..2c7ee67aa 100644 --- a/Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift +++ b/Samples/JavaKitSampleApp/Sources/JavaKitExample/JavaKitExample.swift @@ -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)) diff --git a/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/HelloSwift.java b/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/HelloSwift.java index 5ac8a2f69..94f41d9a3 100644 --- a/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/HelloSwift.java +++ b/Samples/JavaKitSampleApp/Sources/JavaKitExample/com/example/swift/HelloSwift.java @@ -47,6 +47,10 @@ public long init(long value) { return value; } + public String $echo(String value) { + return value; + } + public Predicate lessThanTen() { Predicate predicate = i -> (i < 10); return predicate; diff --git a/Sources/JavaStdlib/JavaIO/generated/FileDescriptor.swift b/Sources/JavaStdlib/JavaIO/generated/FileDescriptor.swift index 413f93a86..25cfd223b 100644 --- a/Sources/JavaStdlib/JavaIO/generated/FileDescriptor.swift +++ b/Sources/JavaStdlib/JavaIO/generated/FileDescriptor.swift @@ -26,7 +26,7 @@ open class FileDescriptor: JavaObject { open func valid() -> Bool } extension JavaClass { - @JavaStaticField(isFinal: true) + @JavaStaticField("in", isFinal: true) public var `in`: FileDescriptor! @JavaStaticField(isFinal: true) diff --git a/Sources/SwiftJava/generated/JavaString.swift b/Sources/SwiftJava/generated/JavaString.swift index 44736eb97..0da5fa20f 100644 --- a/Sources/SwiftJava/generated/JavaString.swift +++ b/Sources/SwiftJava/generated/JavaString.swift @@ -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`. diff --git a/Sources/SwiftJavaRuntimeSupport/generated/JavaJNISwiftInstance.swift b/Sources/SwiftJavaRuntimeSupport/generated/JavaJNISwiftInstance.swift index 50a0502fd..746a23b23 100644 --- a/Sources/SwiftJavaRuntimeSupport/generated/JavaJNISwiftInstance.swift +++ b/Sources/SwiftJavaRuntimeSupport/generated/JavaJNISwiftInstance.swift @@ -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() } diff --git a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift index bb5a13944..a50cbbc9a 100644 --- a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift +++ b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftArena.swift @@ -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 { /// Java method `ofAuto`. diff --git a/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftInstance.swift b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftInstance.swift new file mode 100644 index 000000000..90a64c9a8 --- /dev/null +++ b/Sources/SwiftJavaRuntimeSupport/generated/JavaSwiftInstance.swift @@ -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() +} diff --git a/Sources/SwiftJavaRuntimeSupport/swift-java.config b/Sources/SwiftJavaRuntimeSupport/swift-java.config index 5b924ef05..4fa790e26 100644 --- a/Sources/SwiftJavaRuntimeSupport/swift-java.config +++ b/Sources/SwiftJavaRuntimeSupport/swift-java.config @@ -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" } diff --git a/Sources/SwiftJavaToolLib/JavaClassTranslator.swift b/Sources/SwiftJavaToolLib/JavaClassTranslator.swift index 4742cd8b1..e2f6ab81d 100644 --- a/Sources/SwiftJavaToolLib/JavaClassTranslator.swift +++ b/Sources/SwiftJavaToolLib/JavaClassTranslator.swift @@ -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) } @@ -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 @@ -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()! @@ -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( @@ -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) @@ -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) """ } diff --git a/Sources/SwiftJavaToolLib/StringExtras.swift b/Sources/SwiftJavaToolLib/StringExtras.swift index 37cf4ffae..b5fb75dde 100644 --- a/Sources/SwiftJavaToolLib/StringExtras.swift +++ b/Sources/SwiftJavaToolLib/StringExtras.swift @@ -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())" + 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. diff --git a/Tests/SwiftJavaToolLibTests/WrapJavaTests/BasicWrapJavaTests.swift b/Tests/SwiftJavaToolLibTests/WrapJavaTests/BasicWrapJavaTests.swift index d2fefc3b8..90b803294 100644 --- a/Tests/SwiftJavaToolLibTests/WrapJavaTests/BasicWrapJavaTests.swift +++ b/Tests/SwiftJavaToolLibTests/WrapJavaTests/BasicWrapJavaTests.swift @@ -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() + """, + ] + ) + } }