From 980940428993d56b95f16ed12b394680d0f0dbd1 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 2 Oct 2024 15:53:03 -0700 Subject: [PATCH 1/3] Introduce the @JavaStaticMethod and use it for calling static Java methods The use of the `@JavaMethod` macro within an extension of `JavaClass` to represent static Java methods on that class didn't quite work, because JavaClass is itself an AnyJavaObject with its own methods. Introduce a `@JavaStaticMethod` macro that will be used for this purpose, and have it call through a separate API (`dynamicJavaStaticMethodCall`). Thank you to Lokesh for reporting this bug! --- Sources/Java2Swift/JavaTranslator.swift | 3 ++- Sources/JavaKit/JavaObject+MethodCalls.swift | 13 +++++++------ Sources/JavaKit/Macros.swift | 14 +++++++++++++- Sources/JavaKitMacros/JavaMethodMacro.swift | 3 ++- .../JavaKitNetwork/generated/URLConnection.swift | 2 +- Tests/JavaKitTests/BasicRuntimeTests.swift | 10 ++++++++++ 6 files changed, 35 insertions(+), 10 deletions(-) diff --git a/Sources/Java2Swift/JavaTranslator.swift b/Sources/Java2Swift/JavaTranslator.swift index c83e5c85..bc2140a0 100644 --- a/Sources/Java2Swift/JavaTranslator.swift +++ b/Sources/Java2Swift/JavaTranslator.swift @@ -416,8 +416,9 @@ extension JavaTranslator { let throwsStr = javaMethod.throwsCheckedException ? "throws" : "" + let methodAttribute: AttributeSyntax = javaMethod.isStatic ? "@JavaStaticMethod" : "@JavaMethod"; return """ - @JavaMethod + \(methodAttribute) public func \(raw: javaMethod.getName())\(raw: genericParameterClause)(\(raw: parametersStr))\(raw: throwsStr)\(raw: resultTypeStr)\(raw: whereClause) """ } diff --git a/Sources/JavaKit/JavaObject+MethodCalls.swift b/Sources/JavaKit/JavaObject+MethodCalls.swift index 62a5617e..ad091943 100644 --- a/Sources/JavaKit/JavaObject+MethodCalls.swift +++ b/Sources/JavaKit/JavaObject+MethodCalls.swift @@ -301,9 +301,10 @@ extension AnyJavaObject { extension JavaClass { /// Call a Java static method with the given name and arguments, which must be /// of the correct type, that produces the given result type. - public func dynamicJavaMethodCall( + public func dynamicJavaStaticMethodCall( methodName: String, - args: repeat each Param + arguments: repeat each Param, + resultType: Result.Type ) throws -> Result { let thisClass = javaThis let environment = javaEnvironment @@ -325,7 +326,7 @@ extension JavaClass { // Retrieve the method that performs this call, then let jniMethod = Result.jniStaticMethodCall(in: environment) - let jniArgs = getJValues(repeat each args, in: environment) + let jniArgs = getJValues(repeat each arguments, in: environment) let jniResult = try environment.translatingJNIExceptions { jniMethod(environment, thisClass, methodID, jniArgs) } @@ -335,9 +336,9 @@ extension JavaClass { /// Call a Java static method with the given name and arguments, which must be /// of the correct type, that produces the given result type. - public func dynamicJavaMethodCall( + public func dynamicJavaStaticMethodCall( methodName: String, - args: repeat each Param + arguments: repeat each Param ) throws { let thisClass = javaThis let environment = javaEnvironment @@ -359,7 +360,7 @@ extension JavaClass { // Retrieve the method that performs this call, then let jniMethod = environment.interface.CallStaticVoidMethodA - let jniArgs = getJValues(repeat each args, in: environment) + let jniArgs = getJValues(repeat each arguments, in: environment) try environment.translatingJNIExceptions { jniMethod!(environment, thisClass, methodID, jniArgs) } diff --git a/Sources/JavaKit/Macros.swift b/Sources/JavaKit/Macros.swift index 24ce8d32..772138f8 100644 --- a/Sources/JavaKit/Macros.swift +++ b/Sources/JavaKit/Macros.swift @@ -95,7 +95,7 @@ public macro JavaField(_ javaFieldName: String? = nil) = #externalMacro(module: /// Attached macro that turns a Swift method into one that wraps a Java method on the underlying Java object. /// -/// The macro must be used within either a AnyJavaObject-conforming type or a specific JavaClass instance. +/// The macro must be used in an AnyJavaObject-conforming type. /// /// ```swift /// @JavaMethod @@ -115,6 +115,18 @@ public macro JavaField(_ javaFieldName: String? = nil) = #externalMacro(module: @attached(body) public macro JavaMethod() = #externalMacro(module: "JavaKitMacros", type: "JavaMethodMacro") +/// Attached macro that turns a Swift method on JavaClass into one that wraps +/// a Java static method on the underlying Java class object. +/// +/// The macro must be used within a specific JavaClass instance. +/// +/// ```swift +/// @JavaMethod +/// func sayHelloBack(_ i: Int32) -> Double +/// ``` +@attached(body) +public macro JavaStaticMethod() = #externalMacro(module: "JavaKitMacros", type: "JavaMethodMacro") + /// Macro that exposes the given Swift method as a native method in Java. /// /// The macro must be used within a struct type marked with `@JavaClass`, and there diff --git a/Sources/JavaKitMacros/JavaMethodMacro.swift b/Sources/JavaKitMacros/JavaMethodMacro.swift index 7996fc8c..b16d8ab2 100644 --- a/Sources/JavaKitMacros/JavaMethodMacro.swift +++ b/Sources/JavaKitMacros/JavaMethodMacro.swift @@ -34,6 +34,7 @@ extension JavaMethodMacro: BodyMacro { fatalError("not a function") } + let isStatic = node.attributeName.trimmedDescription == "JavaStaticMethod" let funcName = funcDecl.name.text let params = funcDecl.signature.parameterClause.parameters let resultType: String = @@ -54,7 +55,7 @@ extension JavaMethodMacro: BodyMacro { ? "try" : "try!" return [ - "return \(raw: tryKeyword) dynamicJavaMethodCall(methodName: \(literal: funcName)\(raw: parametersAsArgs)\(raw: resultType))" + "return \(raw: tryKeyword) dynamicJava\(raw: isStatic ? "Static" : "")MethodCall(methodName: \(literal: funcName)\(raw: parametersAsArgs)\(raw: resultType))" ] } diff --git a/Sources/JavaKitNetwork/generated/URLConnection.swift b/Sources/JavaKitNetwork/generated/URLConnection.swift index f229b8be..94fd6f52 100644 --- a/Sources/JavaKitNetwork/generated/URLConnection.swift +++ b/Sources/JavaKitNetwork/generated/URLConnection.swift @@ -146,7 +146,7 @@ extension JavaClass { @JavaMethod public func setDefaultAllowUserInteraction(_ arg0: Bool) - @JavaMethod + @JavaStaticMethod public func getDefaultAllowUserInteraction() -> Bool @JavaMethod diff --git a/Tests/JavaKitTests/BasicRuntimeTests.swift b/Tests/JavaKitTests/BasicRuntimeTests.swift index 33a93fd4..e182c453 100644 --- a/Tests/JavaKitTests/BasicRuntimeTests.swift +++ b/Tests/JavaKitTests/BasicRuntimeTests.swift @@ -66,4 +66,14 @@ struct BasicRuntimeTests { #expect(String(describing: error) == "no protocol: bad url") } } + + @Test("Static methods") + func staticMethods() { + let urlConnectionClass = JavaClass( + javaThis: URLConnection.getJNIClass(in: jvm.environment)!, + environment: jvm.environment + ) + + #expect(urlConnectionClass.getDefaultAllowUserInteraction() == false) + } } From c749211239a66c9b49eb65da4d1731599169d769 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 2 Oct 2024 16:00:10 -0700 Subject: [PATCH 2/3] Regenerate JavaKit module sources using @JavaStaticMethod --- Sources/JavaKitNetwork/generated/URI.swift | 2 +- Sources/JavaKitNetwork/generated/URLClassLoader.swift | 6 +++--- Sources/JavaKitReflection/generated/AccessibleObject.swift | 2 +- Sources/JavaKitReflection/generated/Constructor.swift | 2 +- Sources/JavaKitReflection/generated/Executable.swift | 2 +- Sources/JavaKitReflection/generated/Method.swift | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/JavaKitNetwork/generated/URI.swift b/Sources/JavaKitNetwork/generated/URI.swift index 2f035938..6fdad52c 100644 --- a/Sources/JavaKitNetwork/generated/URI.swift +++ b/Sources/JavaKitNetwork/generated/URI.swift @@ -125,6 +125,6 @@ public struct URI { public func wait() throws } extension JavaClass { - @JavaMethod + @JavaStaticMethod public func create(_ arg0: String) -> URI? } diff --git a/Sources/JavaKitNetwork/generated/URLClassLoader.swift b/Sources/JavaKitNetwork/generated/URLClassLoader.swift index db46e5e0..849d5715 100644 --- a/Sources/JavaKitNetwork/generated/URLClassLoader.swift +++ b/Sources/JavaKitNetwork/generated/URLClassLoader.swift @@ -74,12 +74,12 @@ public struct URLClassLoader { public func wait() throws } extension JavaClass { - @JavaMethod + @JavaStaticMethod public func newInstance(_ arg0: [URL?]) -> URLClassLoader? - @JavaMethod + @JavaStaticMethod public func getSystemResource(_ arg0: String) -> URL? - @JavaMethod + @JavaStaticMethod public func getSystemResources(_ arg0: String) throws -> Enumeration? } diff --git a/Sources/JavaKitReflection/generated/AccessibleObject.swift b/Sources/JavaKitReflection/generated/AccessibleObject.swift index 8cbc33a5..c54fd33a 100644 --- a/Sources/JavaKitReflection/generated/AccessibleObject.swift +++ b/Sources/JavaKitReflection/generated/AccessibleObject.swift @@ -65,6 +65,6 @@ public struct AccessibleObject { public func wait() throws } extension JavaClass { - @JavaMethod + @JavaStaticMethod public func setAccessible(_ arg0: [AccessibleObject?], _ arg1: Bool) } diff --git a/Sources/JavaKitReflection/generated/Constructor.swift b/Sources/JavaKitReflection/generated/Constructor.swift index d7806d24..e7ea0476 100644 --- a/Sources/JavaKitReflection/generated/Constructor.swift +++ b/Sources/JavaKitReflection/generated/Constructor.swift @@ -122,6 +122,6 @@ public struct Constructor { public func wait() throws } extension JavaClass { - @JavaMethod + @JavaStaticMethod public func setAccessible(_ arg0: [AccessibleObject?], _ arg1: Bool) where ObjectType == Constructor } diff --git a/Sources/JavaKitReflection/generated/Executable.swift b/Sources/JavaKitReflection/generated/Executable.swift index 55e2911d..76b34325 100644 --- a/Sources/JavaKitReflection/generated/Executable.swift +++ b/Sources/JavaKitReflection/generated/Executable.swift @@ -119,6 +119,6 @@ public struct Executable { public func wait() throws } extension JavaClass { - @JavaMethod + @JavaStaticMethod public func setAccessible(_ arg0: [AccessibleObject?], _ arg1: Bool) } diff --git a/Sources/JavaKitReflection/generated/Method.swift b/Sources/JavaKitReflection/generated/Method.swift index c8ae49b4..84d0b90a 100644 --- a/Sources/JavaKitReflection/generated/Method.swift +++ b/Sources/JavaKitReflection/generated/Method.swift @@ -137,6 +137,6 @@ public struct Method { public func wait() throws } extension JavaClass { - @JavaMethod + @JavaStaticMethod public func setAccessible(_ arg0: [AccessibleObject?], _ arg1: Bool) } From 2d89c14ae9c6af9d6d48a16ddd83919ec8bb8ff8 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Wed, 2 Oct 2024 16:12:55 -0700 Subject: [PATCH 3/3] Unbreak macOS build because Mutex requires macOS 15 --- Sources/_Subprocess/Subprocess+IO.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/_Subprocess/Subprocess+IO.swift b/Sources/_Subprocess/Subprocess+IO.swift index 73b1f99e..b085a991 100644 --- a/Sources/_Subprocess/Subprocess+IO.swift +++ b/Sources/_Subprocess/Subprocess+IO.swift @@ -145,10 +145,10 @@ extension Subprocess { case fileDescriptor(FileDescriptor?, Bool) } - let storage: Mutex + let storage: LockedState internal init(storage: Storage) { - self.storage = .init(storage) + self.storage = .init(initialState: storage) } internal func getReadFileDescriptor() -> FileDescriptor? { @@ -250,10 +250,10 @@ extension Subprocess { case collected(Int, FileDescriptor?, FileDescriptor?) } - private let storage: Mutex + private let storage: LockedState internal init(storage: Storage) { - self.storage = .init(storage) + self.storage = .init(initialState: storage) } internal func getWriteFileDescriptor() -> FileDescriptor? {