Skip to content
Open
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 @@ -23,6 +23,7 @@
import static org.junit.jupiter.api.Assertions.*;

public class SwiftDictionaryMapTest {
@SuppressWarnings("SuspiciousMethodCalls")
@Test
void makeStringToLongDictionary() {
try (var arena = SwiftArena.ofConfined()) {
Expand All @@ -33,6 +34,8 @@ void makeStringToLongDictionary() {
assertTrue(dict.containsKey("hello"));
assertFalse(dict.containsKey("missing"));
assertNull(dict.get("missing"));
assertNull(dict.get(99999L), "Java's Map accepts keys of different types");
assertFalse(dict.containsKey(99999L), "Java's Map accepts keys of different types");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static org.junit.jupiter.api.Assertions.*;

public class SwiftSetTest {
@SuppressWarnings("SuspiciousMethodCalls")
@Test
void makeStringSet() {
try (var arena = SwiftArena.ofConfined()) {
Expand All @@ -31,6 +32,7 @@ void makeStringSet() {
assertTrue(set.contains("hello"));
assertTrue(set.contains("world"));
assertFalse(set.contains("missing"));
assertFalse(set.contains(99999L), "Java's Set accepts keys of different types");
}
}

Expand Down
98 changes: 74 additions & 24 deletions Sources/SwiftJava/BridgedValues/JavaBoxing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,35 +24,54 @@ public protocol JavaBoxable: JavaValue {
/// Convert this Swift value to a boxed Java object.
func toJavaObject(in environment: JNIEnvironment) -> jobject?

/// The Java metatype used to box values of this Swift type.
static var javaBoxClass: jclass { get }

/// Create a Swift value from a boxed Java object.
static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> Self
}

// ==== -----------------------------------------------------------------------
// MARK: JavaBoxable conformances

private func findJavaClass(_ className: String) -> jclass {
let environment = try! JavaVirtualMachine.shared().environment()
let clazz: jobject
if let jniClass = environment.interface.FindClass(environment, className) {
clazz = environment.interface.NewGlobalRef(environment, jniClass)!
environment.interface.DeleteLocalRef(environment, jniClass)
} else {
fatalError("Class \(className) could not be found!")
}
return clazz
}

extension String: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
self.getJNIValue(in: environment)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/String")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> String {
String(fromJNI: obj, in: environment)
}
}

extension Int64: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Long")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(J)V")
var args = [jvalue()]
args[0].j = self.getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Long")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> Int64 {
guard let obj else { return 0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "longValue", "()J")
let result = environment.interface.CallLongMethodA(environment, obj, methodID, nil)
return Int64(fromJNI: result, in: environment)
Expand All @@ -61,16 +80,18 @@ extension Int64: JavaBoxable {

extension Int32: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Integer")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(I)V")
var args = [jvalue()]
args[0].i = self.getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Integer")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> Int32 {
guard let obj else { return 0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "intValue", "()I")
let result = environment.interface.CallIntMethodA(environment, obj, methodID, nil)
return Int32(fromJNI: result, in: environment)
Expand All @@ -79,16 +100,18 @@ extension Int32: JavaBoxable {

extension Double: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Double")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(D)V")
var args = [jvalue()]
args[0].d = self.getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Double")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> Double {
guard let obj else { return 0.0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "doubleValue", "()D")
let result = environment.interface.CallDoubleMethodA(environment, obj, methodID, nil)
return Double(fromJNI: result, in: environment)
Expand All @@ -97,16 +120,18 @@ extension Double: JavaBoxable {

extension Float: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Float")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(F)V")
var args = [jvalue()]
args[0].f = self.getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Float")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> Float {
guard let obj else { return 0.0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "floatValue", "()F")
let result = environment.interface.CallFloatMethodA(environment, obj, methodID, nil)
return Float(fromJNI: result, in: environment)
Expand All @@ -115,16 +140,18 @@ extension Float: JavaBoxable {

extension Bool: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Boolean")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(Z)V")
var args = [jvalue()]
args[0].z = self.getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Boolean")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> Bool {
guard let obj else { return false }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "booleanValue", "()Z")
let result = environment.interface.CallBooleanMethodA(environment, obj, methodID, nil)
return Bool(fromJNI: result, in: environment)
Expand All @@ -136,16 +163,18 @@ extension Bool: JavaBoxable {

extension Int8: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Byte")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(B)V")
var args = [jvalue()]
args[0].b = self.getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Byte")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> Int8 {
guard let obj else { return 0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "byteValue", "()B")
let result = environment.interface.CallByteMethodA(environment, obj, methodID, nil)
return Int8(fromJNI: result, in: environment)
Expand All @@ -154,16 +183,18 @@ extension Int8: JavaBoxable {

extension UInt8: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Byte")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(B)V")
var args = [jvalue()]
args[0].b = self.getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Byte")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> UInt8 {
guard let obj else { return 0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "byteValue", "()B")
let result = environment.interface.CallByteMethodA(environment, obj, methodID, nil)
return UInt8(fromJNI: result, in: environment)
Expand All @@ -175,16 +206,18 @@ extension UInt8: JavaBoxable {

extension Int16: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Short")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(S)V")
var args = [jvalue()]
args[0].s = self.getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Short")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> Int16 {
guard let obj else { return 0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "shortValue", "()S")
let result = environment.interface.CallShortMethodA(environment, obj, methodID, nil)
return Int16(fromJNI: result, in: environment)
Expand All @@ -196,16 +229,18 @@ extension Int16: JavaBoxable {

extension UInt32: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Integer")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(I)V")
var args = [jvalue()]
args[0].i = self.getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Integer")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> UInt32 {
guard let obj else { return 0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "intValue", "()I")
let result = environment.interface.CallIntMethodA(environment, obj, methodID, nil)
return UInt32(fromJNI: result, in: environment)
Expand All @@ -214,16 +249,18 @@ extension UInt32: JavaBoxable {

extension UInt64: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Long")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(J)V")
var args = [jvalue()]
args[0].j = self.getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Long")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> UInt64 {
guard let obj else { return 0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "longValue", "()J")
let result = environment.interface.CallLongMethodA(environment, obj, methodID, nil)
return UInt64(fromJNI: result, in: environment)
Expand All @@ -235,16 +272,18 @@ extension UInt64: JavaBoxable {

extension Int: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Long")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(J)V")
var args = [jvalue()]
args[0].j = Int64(self).getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Long")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> Int {
guard let obj else { return 0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "longValue", "()J")
let result = environment.interface.CallLongMethodA(environment, obj, methodID, nil)
return Int(Int64(fromJNI: result, in: environment))
Expand All @@ -253,16 +292,18 @@ extension Int: JavaBoxable {

extension UInt: JavaBoxable {
public func toJavaObject(in environment: JNIEnvironment) -> jobject? {
let cls = environment.interface.FindClass(environment, "java/lang/Long")
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "<init>", "(J)V")
var args = [jvalue()]
args[0].j = Int64(bitPattern: UInt64(self)).getJNIValue(in: environment)
return environment.interface.NewObjectA(environment, cls, methodID, &args)
}

public static let javaBoxClass: jclass = findJavaClass("java/lang/Long")

public static func fromJavaObject(_ obj: jobject?, in environment: JNIEnvironment) -> UInt {
guard let obj else { return 0 }
let cls = environment.interface.GetObjectClass(environment, obj)
let cls = Self.javaBoxClass
let methodID = environment.interface.GetMethodID(environment, cls, "longValue", "()J")
let result = environment.interface.CallLongMethodA(environment, obj, methodID, nil)
return UInt(UInt64(fromJNI: result, in: environment))
Expand Down Expand Up @@ -304,12 +345,18 @@ final class SwiftDictionaryBox<K: JavaBoxable & Hashable, V: JavaBoxable>: AnySw
}

override func get(key: jobject?, environment: JNIEnvironment) -> jobject? {
guard environment.interface.IsInstanceOf(environment, key, K.javaBoxClass) == JNI_TRUE else {
return nil
}
let swiftKey = K.fromJavaObject(key, in: environment)
guard let value = dictionary[swiftKey] else { return nil }
return value.toJavaObject(in: environment)
}

override func containsKey(key: jobject?, environment: JNIEnvironment) -> Bool {
guard environment.interface.IsInstanceOf(environment, key, K.javaBoxClass) == JNI_TRUE else {
return false
}
let swiftKey = K.fromJavaObject(key, in: environment)
return dictionary[swiftKey] != nil
}
Expand Down Expand Up @@ -369,6 +416,9 @@ final class SwiftSetBox<E: JavaBoxable & Hashable>: AnySwiftSetBox {
}

override func contains(element: jobject?, environment: JNIEnvironment) -> Bool {
guard environment.interface.IsInstanceOf(environment, element, E.javaBoxClass) == JNI_TRUE else {
return false
}
let swiftElement = E.fromJavaObject(element, in: environment)
return set.contains(swiftElement)
}
Expand Down