From c85371f2a3bda26cb649f68ba16c7cb91d682d72 Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Mon, 7 Oct 2024 11:16:17 -0700 Subject: [PATCH] Improve lookup of JavaClass instances in an environment Teach the low-level getJNIClass API to properly check for Java exceptions and turn them into Swift errors. Then add a higher-level API `JavaClass.init(in:)` to look up a specific imported Java class within the given environment. --- Sources/JavaKit/AnyJavaObject.swift | 12 ++++++----- .../Exceptions/ExceptionHandling.swift | 2 +- Sources/JavaKit/JavaClass.swift | 10 +++++++++- Sources/JavaKit/JavaObject+Inheritance.swift | 2 +- Sources/JavaKit/JavaObject+MethodCalls.swift | 2 +- Sources/JavaKit/Optional+JavaObject.swift | 2 +- Tests/JavaKitTests/BasicRuntimeTests.swift | 20 +++++++++++++------ 7 files changed, 34 insertions(+), 16 deletions(-) diff --git a/Sources/JavaKit/AnyJavaObject.swift b/Sources/JavaKit/AnyJavaObject.swift index aaddaa9a..8f28c098 100644 --- a/Sources/JavaKit/AnyJavaObject.swift +++ b/Sources/JavaKit/AnyJavaObject.swift @@ -81,10 +81,12 @@ extension AnyJavaObject { } /// Retrieve the Java class for this type. - public static func getJNIClass(in environment: JNIEnvironment) -> jclass? { - return environment.interface.FindClass( - environment, - fullJavaClassNameWithSlashes - ) + public static func getJNIClass(in environment: JNIEnvironment) throws -> jclass { + try environment.translatingJNIExceptions { + environment.interface.FindClass( + environment, + fullJavaClassNameWithSlashes + ) + }! } } diff --git a/Sources/JavaKit/Exceptions/ExceptionHandling.swift b/Sources/JavaKit/Exceptions/ExceptionHandling.swift index 44f1897c..efd98e94 100644 --- a/Sources/JavaKit/Exceptions/ExceptionHandling.swift +++ b/Sources/JavaKit/Exceptions/ExceptionHandling.swift @@ -42,7 +42,7 @@ extension JNIEnvironment { // Otherwise, create a exception with a message. _ = interface.ThrowNew( self, - JavaClass.getJNIClass(in: self), + try! JavaClass.getJNIClass(in: self), String(describing: error) ) } diff --git a/Sources/JavaKit/JavaClass.swift b/Sources/JavaKit/JavaClass.swift index 8cb2e3c2..c9f0fdd2 100644 --- a/Sources/JavaKit/JavaClass.swift +++ b/Sources/JavaKit/JavaClass.swift @@ -17,4 +17,12 @@ import JavaRuntime /// Wrapper around a Java class that provides access to the static members of /// the class. @JavaClass("java.lang.Class") -public struct JavaClass { } +public struct JavaClass { + /// Lookup this Java class within the given environment. + public init(in environment: JNIEnvironment) throws { + self.init( + javaThis: try ObjectType.getJNIClass(in: environment), + environment: environment + ) + } +} diff --git a/Sources/JavaKit/JavaObject+Inheritance.swift b/Sources/JavaKit/JavaObject+Inheritance.swift index 8d6b68fa..cbd27c91 100644 --- a/Sources/JavaKit/JavaObject+Inheritance.swift +++ b/Sources/JavaKit/JavaObject+Inheritance.swift @@ -27,7 +27,7 @@ extension AnyJavaObject { private func isInstanceOf( _ otherClass: OtherClass.Type ) -> jclass? { - guard let otherJavaClass = otherClass.getJNIClass(in: javaEnvironment) else { + guard let otherJavaClass = try? otherClass.getJNIClass(in: javaEnvironment) else { return nil } diff --git a/Sources/JavaKit/JavaObject+MethodCalls.swift b/Sources/JavaKit/JavaObject+MethodCalls.swift index ad091943..e9b97bc2 100644 --- a/Sources/JavaKit/JavaObject+MethodCalls.swift +++ b/Sources/JavaKit/JavaObject+MethodCalls.swift @@ -247,7 +247,7 @@ extension AnyJavaObject { in environment: JNIEnvironment, arguments: repeat each Param ) throws -> Self { - let thisClass = Self.getJNIClass(in: environment)! + let thisClass = try Self.getJNIClass(in: environment) // Compute the method signature so we can find the right method, then look up the // method within the class. diff --git a/Sources/JavaKit/Optional+JavaObject.swift b/Sources/JavaKit/Optional+JavaObject.swift index 0444b4f1..55ad366e 100644 --- a/Sources/JavaKit/Optional+JavaObject.swift +++ b/Sources/JavaKit/Optional+JavaObject.swift @@ -70,7 +70,7 @@ extension Optional: JavaValue where Wrapped: AnyJavaObject { public static func jniNewArray(in environment: JNIEnvironment) -> JNINewArray { return { environment, size in - let jniClass = Wrapped.getJNIClass(in: environment) + let jniClass = try! Wrapped.getJNIClass(in: environment) return environment.interface.NewObjectArray(environment, size, jniClass, nil) } } diff --git a/Tests/JavaKitTests/BasicRuntimeTests.swift b/Tests/JavaKitTests/BasicRuntimeTests.swift index b05579ae..085c907c 100644 --- a/Tests/JavaKitTests/BasicRuntimeTests.swift +++ b/Tests/JavaKitTests/BasicRuntimeTests.swift @@ -59,16 +59,24 @@ struct BasicRuntimeTests { } @Test("Static methods") - func staticMethods() { - let urlConnectionClass = JavaClass( - javaThis: URLConnection.getJNIClass(in: jvm.environment)!, - environment: jvm.environment - ) - + func staticMethods() throws { + let urlConnectionClass = try JavaClass(in: jvm.environment) #expect(urlConnectionClass.getDefaultAllowUserInteraction() == false) } + + @Test("Class instance lookup") + func classInstanceLookup() throws { + do { + _ = try JavaClass(in: jvm.environment) + } catch { + #expect(String(describing: error) == "org/swift/javakit/Nonexistent") + } + } } +@JavaClass("org.swift.javakit.Nonexistent") +struct Nonexistent { } + /// Whether we're running on Linux. var isLinux: Bool { #if os(Linux)