Compare commits

...

5 Commits

Author SHA1 Message Date
Andrew Druk 065a7d03a5 Dev: fix warnings after migration to Swift 5 2019-09-24 01:50:40 +03:00
Andrew Druk 048b9dd595 Dev: fix local reference leak 2019-09-24 01:00:31 +03:00
Andrew Druk 3bd5f9bdab Dev: fix race conditions in "double-check lock"
Trying read value from dictionary outside lock is unsafe
2019-09-02 15:29:37 +03:00
Anton Pogonets 20e5079690 Allow swift 5 2019-08-21 04:31:34 +03:00
Anton Pogonets 13de2eeec7 Dev: restore 32bit support 2019-08-09 01:31:00 +03:00
4 changed files with 43 additions and 48 deletions

View File

@ -17,5 +17,5 @@ let package = Package(
targets: [ targets: [
.target(name: "JavaCoder", dependencies: ["java_swift", "AnyCodable"], path: "Sources"), .target(name: "JavaCoder", dependencies: ["java_swift", "AnyCodable"], path: "Sources"),
], ],
swiftLanguageVersions: [4] swiftLanguageVersions: [5, 4]
) )

View File

@ -107,15 +107,15 @@ fileprivate let javaFieldLock = NSLock()
public extension JNICore { public extension JNICore {
public var TRUE: jboolean { var TRUE: jboolean {
return jboolean(JNI_TRUE) return jboolean(JNI_TRUE)
} }
public var FALSE: jboolean { var FALSE: jboolean {
return jboolean(JNI_FALSE) return jboolean(JNI_FALSE)
} }
public enum JNIError: Error { enum JNIError: Error {
case classNotFoundException(String) case classNotFoundException(String)
case methodNotFoundException(String) case methodNotFoundException(String)
@ -135,10 +135,7 @@ public extension JNICore {
} }
// MARK: Global cache functions // MARK: Global cache functions
public func getJavaClass(_ className: String) throws -> jclass { func getJavaClass(_ className: String) throws -> jclass {
if let javaClass = javaClasses[className] {
return javaClass
}
return try javaClassesLock.sync { return try javaClassesLock.sync {
if let javaClass = javaClasses[className] { if let javaClass = javaClasses[className] {
return javaClass return javaClass
@ -153,16 +150,13 @@ public extension JNICore {
} }
} }
public func getJavaEmptyConstructor(forClass className: String) throws -> jmethodID { func getJavaEmptyConstructor(forClass className: String) throws -> jmethodID {
return try getJavaMethod(forClass: className, method: "<init>", sig: "()V") return try getJavaMethod(forClass: className, method: "<init>", sig: "()V")
} }
public func getJavaMethod(forClass className: String, method: String, sig: String) throws -> jmethodID { func getJavaMethod(forClass className: String, method: String, sig: String) throws -> jmethodID {
let key = "\(className).\(method)\(sig)" let key = "\(className).\(method)\(sig)"
let javaClass = try getJavaClass(className) let javaClass = try getJavaClass(className)
if let methodID = javaMethods[key] {
return methodID
}
return try javaMethodLock.sync { return try javaMethodLock.sync {
if let methodID = javaMethods[key] { if let methodID = javaMethods[key] {
return methodID return methodID
@ -177,12 +171,9 @@ public extension JNICore {
} }
} }
public func getStaticJavaMethod(forClass className: String, method: String, sig: String) throws -> jmethodID { func getStaticJavaMethod(forClass className: String, method: String, sig: String) throws -> jmethodID {
let key = "\(className).\(method)\(sig)" let key = "\(className).\(method)\(sig)"
let javaClass = try getJavaClass(className) let javaClass = try getJavaClass(className)
if let methodID = javaStaticMethods[key] {
return methodID
}
return try javaStaticMethodLock.sync { return try javaStaticMethodLock.sync {
if let methodID = javaStaticMethods[key] { if let methodID = javaStaticMethods[key] {
return methodID return methodID
@ -197,12 +188,9 @@ public extension JNICore {
} }
} }
public func getJavaField(forClass className: String, field: String, sig: String) throws -> jfieldID { func getJavaField(forClass className: String, field: String, sig: String) throws -> jfieldID {
let key = "\(className).\(field)\(sig)" let key = "\(className).\(field)\(sig)"
let javaClass = try getJavaClass(className) let javaClass = try getJavaClass(className)
if let fieldID = javaFields[key] {
return fieldID
}
return try javaFieldLock.sync({ return try javaFieldLock.sync({
if let fieldID = javaFields[key] { if let fieldID = javaFields[key] {
return fieldID return fieldID
@ -218,7 +206,7 @@ public extension JNICore {
}) })
} }
public func GlobalFindClass( _ name: UnsafePointer<Int8>, func GlobalFindClass( _ name: UnsafePointer<Int8>,
_ file: StaticString = #file, _ line: Int = #line ) -> jclass? { _ file: StaticString = #file, _ line: Int = #line ) -> jclass? {
guard let clazz: jclass = FindClass(name, file, line ) else { guard let clazz: jclass = FindClass(name, file, line ) else {
return nil return nil
@ -229,87 +217,87 @@ public extension JNICore {
} }
// MARK: Constructors // MARK: Constructors
public func NewObject(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jobject? { func NewObject(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jobject? {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.NewObjectA(env, clazz, methodID, argsPtr) api.NewObjectA(env, clazz, methodID, argsPtr)
}) })
} }
// MARK: Object methods // MARK: Object methods
public func CallBooleanMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jboolean { func CallBooleanMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jboolean {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallBooleanMethodA(env, object, methodID, argsPtr) api.CallBooleanMethodA(env, object, methodID, argsPtr)
}) })
} }
public func CallByteMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jbyte { func CallByteMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jbyte {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallByteMethodA(env, object, methodID, argsPtr) api.CallByteMethodA(env, object, methodID, argsPtr)
}) })
} }
public func CallShortMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jshort { func CallShortMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jshort {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallShortMethodA(env, object, methodID, argsPtr) api.CallShortMethodA(env, object, methodID, argsPtr)
}) })
} }
public func CallIntMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jint { func CallIntMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jint {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallIntMethodA(env, object, methodID, argsPtr) api.CallIntMethodA(env, object, methodID, argsPtr)
}) })
} }
public func CallLongMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jlong { func CallLongMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jlong {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallLongMethodA(env, object, methodID, argsPtr) api.CallLongMethodA(env, object, methodID, argsPtr)
}) })
} }
public func CallObjectMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jobject? { func CallObjectMethod(_ object: jobject, methodID: jmethodID, args: [jvalue] = []) -> jobject? {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallObjectMethodA(env, object, methodID, argsPtr) api.CallObjectMethodA(env, object, methodID, argsPtr)
}) })
} }
// MARK: Static methods // MARK: Static methods
public func CallStaticBooleanMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jboolean { func CallStaticBooleanMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jboolean {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallStaticBooleanMethodA(env, clazz, methodID, argsPtr) api.CallStaticBooleanMethodA(env, clazz, methodID, argsPtr)
}) })
} }
public func CallStaticByteMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jbyte { func CallStaticByteMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jbyte {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallStaticByteMethodA(env, clazz, methodID, argsPtr) api.CallStaticByteMethodA(env, clazz, methodID, argsPtr)
}) })
} }
public func CallStaticShortMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jshort { func CallStaticShortMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jshort {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallStaticShortMethodA(env, clazz, methodID, argsPtr) api.CallStaticShortMethodA(env, clazz, methodID, argsPtr)
}) })
} }
public func CallStaticIntMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jint { func CallStaticIntMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jint {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallStaticIntMethodA(env, clazz, methodID, argsPtr) api.CallStaticIntMethodA(env, clazz, methodID, argsPtr)
}) })
} }
public func CallStaticLongMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jlong { func CallStaticLongMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jlong {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallStaticLongMethodA(env, clazz, methodID, argsPtr) api.CallStaticLongMethodA(env, clazz, methodID, argsPtr)
}) })
} }
public func CallStaticObjectMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jobject? { func CallStaticObjectMethod(_ clazz: jclass, methodID: jmethodID, args: [jvalue] = []) -> jobject? {
return checkArgument(args: args, { argsPtr in return checkArgument(args: args, { argsPtr in
api.CallStaticObjectMethodA(env, clazz, methodID, argsPtr) api.CallStaticObjectMethodA(env, clazz, methodID, argsPtr)
}) })
} }
public func dumpReferenceTables() { func dumpReferenceTables() {
JNI.api.CallStaticVoidMethodA(JNI.env, VMDebugClass, VMDebugDumpReferenceTablesMethod, nil) JNI.api.CallStaticVoidMethodA(JNI.env, VMDebugClass, VMDebugDumpReferenceTablesMethod, nil)
JNI.api.ExceptionClear(JNI.env) JNI.api.ExceptionClear(JNI.env)
JNI.ExceptionReset() JNI.ExceptionReset()
@ -335,25 +323,25 @@ public extension JNICore {
} }
// MARK: New API // MARK: New API
public func CallObjectMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jobject? { func CallObjectMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jobject? {
return checkArgumentAndWrap(args: args, { argsPtr in return checkArgumentAndWrap(args: args, { argsPtr in
api.CallObjectMethodA(env, object, methodID, argsPtr) api.CallObjectMethodA(env, object, methodID, argsPtr)
}) })
} }
public func CallStaticObjectMethod(_ clazz: jclass, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jobject? { func CallStaticObjectMethod(_ clazz: jclass, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) -> jobject? {
return checkArgumentAndWrap(args: args, { argsPtr in return checkArgumentAndWrap(args: args, { argsPtr in
api.CallStaticObjectMethodA(env, clazz, methodID, argsPtr) api.CallStaticObjectMethodA(env, clazz, methodID, argsPtr)
}) })
} }
public func CallVoidMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) { func CallVoidMethod(_ object: jobject, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) {
checkArgumentAndWrap(args: args, { argsPtr in checkArgumentAndWrap(args: args, { argsPtr in
api.CallVoidMethodA(env, object, methodID, argsPtr) api.CallVoidMethodA(env, object, methodID, argsPtr)
}) })
} }
public func CallStaticVoidMethod(_ clazz: jclass, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) { func CallStaticVoidMethod(_ clazz: jclass, _ methodID: jmethodID, _ args: JNIArgumentProtocol...) {
checkArgumentAndWrap(args: args, { argsPtr in checkArgumentAndWrap(args: args, { argsPtr in
api.CallStaticVoidMethodA(env, clazz, methodID, argsPtr) api.CallStaticVoidMethodA(env, clazz, methodID, argsPtr)
}) })

View File

@ -26,31 +26,31 @@ public class JNIObjectWithClass: JNIObject {
public extension JNIObject { public extension JNIObject {
public static func getJavaClassname(javaObject: jobject?) -> String { static func getJavaClassname(javaObject: jobject?) -> String {
let cls = JNI.api.GetObjectClass(JNI.env, javaObject) let cls = JNI.api.GetObjectClass(JNI.env, javaObject)
let javaClassName = JNI.api.CallObjectMethodA(JNI.env, cls, ClassGetNameMethod, nil) let javaClassName = JNI.api.CallObjectMethodA(JNI.env, cls, ClassGetNameMethod, nil)
return String(javaObject: javaClassName).replacingOccurrences(of: ".", with: "/") return String(javaObject: javaClassName).replacingOccurrences(of: ".", with: "/")
} }
public var className: String { var className: String {
if let jniObject = self as? JNIObjectWithClass { if let jniObject = self as? JNIObjectWithClass {
return jniObject.privateClassName return jniObject.privateClassName
} }
return JNIObject.getJavaClassname(javaObject: self.javaObject) return JNIObject.getJavaClassname(javaObject: self.javaObject)
} }
public func callVoidMethod(_ methodID: jmethodID, _ args: JNIArgumentProtocol...) { func callVoidMethod(_ methodID: jmethodID, _ args: JNIArgumentProtocol...) {
checkArgumentAndWrap(args: args, { argsPtr in checkArgumentAndWrap(args: args, { argsPtr in
JNI.api.CallVoidMethodA(JNI.env, javaObject, methodID, argsPtr) JNI.api.CallVoidMethodA(JNI.env, javaObject, methodID, argsPtr)
}) })
} }
public func callStringMethod(method: String? = nil, functionName: String = #function, _ args: JNIArgumentProtocol...) -> String { func callStringMethod(method: String? = nil, functionName: String = #function, _ args: JNIArgumentProtocol...) -> String {
let methodName = method ?? String(functionName.split(separator: "(")[0]) let methodName = method ?? String(functionName.split(separator: "(")[0])
return String(javaObject: self.internalcallObjectMethod(method: methodName, returnType: "Ljava/lang/String;", args)) return String(javaObject: self.internalcallObjectMethod(method: methodName, returnType: "Ljava/lang/String;", args))
} }
public func callObjectMethod(method: String? = nil, functionName: String = #function, returnType: String, _ args: JNIArgumentProtocol...) -> jobject? { func callObjectMethod(method: String? = nil, functionName: String = #function, returnType: String, _ args: JNIArgumentProtocol...) -> jobject? {
let methodName = method ?? String(functionName.split(separator: "(")[0]) let methodName = method ?? String(functionName.split(separator: "(")[0])
return self.internalcallObjectMethod(method: methodName, returnType: returnType, args) return self.internalcallObjectMethod(method: methodName, returnType: returnType, args)
} }

View File

@ -176,13 +176,17 @@ public struct JavaCoderConfig {
RegisterType(type: Data.self, javaClassname: ByteBufferClassname, encodableClosure: { RegisterType(type: Data.self, javaClassname: ByteBufferClassname, encodableClosure: {
let valueData = $0 as! Data let valueData = $0 as! Data
let byteArray = JNI.api.NewByteArray(JNI.env, Int32(valueData.count)) let byteArray = JNI.api.NewByteArray(JNI.env, jint(valueData.count))
if let throwable = JNI.ExceptionCheck() { if let throwable = JNI.ExceptionCheck() {
throw EncodingError.invalidValue($0, EncodingError.Context(codingPath: [], throw EncodingError.invalidValue($0, EncodingError.Context(codingPath: [],
debugDescription: "Can't create NewByteArray \(valueData.count)")) debugDescription: "Can't create NewByteArray \(valueData.count)"))
} }
valueData.withUnsafeBytes({ (pointer: UnsafePointer<Int8>) -> Void in try valueData.withUnsafeBytes({ pointer in
JNI.api.SetByteArrayRegion(JNI.env, byteArray, 0, Int32(valueData.count), pointer) guard let bytes = pointer.baseAddress?.assumingMemoryBound(to: Int8.self) else {
throw EncodingError.invalidValue(valueData, EncodingError.Context(codingPath: [],
debugDescription: "Can't get unsafeBytes \(valueData.count)"))
}
JNI.api.SetByteArrayRegion(JNI.env, byteArray, 0, jint(valueData.count), bytes)
}) })
if let throwable = JNI.ExceptionCheck() { if let throwable = JNI.ExceptionCheck() {
throw EncodingError.invalidValue($0, EncodingError.Context(codingPath: [], throw EncodingError.invalidValue($0, EncodingError.Context(codingPath: [],
@ -195,6 +199,9 @@ public struct JavaCoderConfig {
return JNI.CallStaticObjectMethod(ByteBufferClass, methodID: ByteBufferWrap, args: [jvalue(l: byteArray)])! return JNI.CallStaticObjectMethod(ByteBufferClass, methodID: ByteBufferWrap, args: [jvalue(l: byteArray)])!
}, decodableClosure: { }, decodableClosure: {
let byteArray = JNI.CallObjectMethod($0, methodID: ByteBufferArray) let byteArray = JNI.CallObjectMethod($0, methodID: ByteBufferArray)
defer {
JNI.api.DeleteLocalRef(JNI.env, byteArray)
}
guard let pointer = JNI.api.GetByteArrayElements(JNI.env, byteArray, nil) else { guard let pointer = JNI.api.GetByteArrayElements(JNI.env, byteArray, nil) else {
throw JavaCodingError.cantFindObject("ByteBuffer") throw JavaCodingError.cantFindObject("ByteBuffer")
} }