// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.
package java.internal
import std.collection.ArrayQueue
import std.sync.AtomicBool
import std.sync.AtomicOptionReference
import std.sync.Mutex
protected type jobject = CPointer<Unit>
protected type jarray = jobject
protected type jclass = jobject
protected type jthrowable = jobject
protected type jbooleanArray = jarray
protected type jbyteArray = jarray
protected type jcharArray = jarray
protected type jshortArray = jarray
protected type jintArray = jarray
protected type jlongArray = jarray
protected type jfloatArray = jarray
protected type jdoubleArray = jarray
protected type jobjectArray = jarray
protected type jmethodID = CPointer<Unit>
protected type JNIEnv_ptr = CPointer<CPointer<JNINativeInterface_>>
protected type jlong = Int64
protected type jint = Int32
protected type jstring = CPointer<Unit>
protected type jboolean = UInt8
protected type jfieldID = CPointer<Unit>
protected type jweak = jobject
protected type jvalue = UInt64
protected type jsize = jint
protected type jshort = Int16
protected type jchar = UInt16
protected type jfloat = Float32
protected type jdouble = Float64
protected type jbyte = Int8
type JavaVM = CPointer<JNIInvokeInterface_>
protected class JNIException <: Exception {
init(message: String, jException: jthrowable) {
super(message)
this.jException = jException
}
public var jException = jthrowable()
}
public const INTEROPLIB_VERSION: Int64 = 9
var jvm = CPointer<JavaVM>()
var classLoading = AtomicOptionReference<ClassLoading>()
class ClassLoading {
private let classLoader: jobject
private let forNameID: jmethodID
private let javaLangClass: jclass
init(classLoader: jobject, forNameID: jmethodID, javaLangClass: jclass) {
assert(!classLoader.isNull())
assert(!forNameID.isNull())
assert(!javaLangClass.isNull())
this.classLoader = classLoader
this.forNameID = forNameID
this.javaLangClass = javaLangClass
}
/**
* Loads class by its fully-qualified name.
*
* @return local reference to the loaded class, or null if the class wasn't found
*/
func loadClass(env: JNIEnv_ptr, name: String): jclass {
var result = jclass()
unsafe {
try (searchName = LibC.mallocCString(name.replace("/", ".")).asResource()) {
let envCJ = env.read().read()
var args: VArray<jvalue, $3> = [
UInt64(envCJ.NewStringUTF(env, searchName.value).toUIntNative()),
0,
UInt64(classLoader.toUIntNative())
]
result = envCJ.CallStaticObjectMethodA(env, javaLangClass, forNameID, inout args)
}
}
handlePendingException(env)
result
}
}
@C
func setJavaVmAndInitClassLoading(javaVM: CPointer<JavaVM>, env: JNIEnv_ptr, classLoader: jobject, methodID: jmethodID, javaLangClass: jclass): Unit {
jvm = javaVM
let classLoaderRef = unsafe {
let envCJ = env.read().read()
envCJ.NewGlobalRef(env, classLoader)
}
classLoading.store(ClassLoading(classLoaderRef, methodID, javaLangClass))
}
@C
protected struct JNINativeInterface_ {
private var reserved0 = CPointer<Unit>()
private var reserved1 = CPointer<Unit>()
private var reserved2 = CPointer<Unit>()
private var reserved3 = CPointer<Unit>()
private var GetVersion = CPointer<Unit>()
private var DefineClass = CPointer<Unit>()
var FindClass = CFunc<(env: JNIEnv_ptr, name: CString) -> jclass>(CPointer<Unit>())
private var FromReflectedMethod = CPointer<Unit>()
private var FromReflectedField = CPointer<Unit>()
private var ToReflectedMethod = CPointer<Unit>()
var GetSuperclass = CFunc<(env: JNIEnv_ptr, clazz: jclass) -> jclass>(CPointer<Unit>())
private var IsAssignableFrom = CPointer<Unit>()
private var ToReflectedField = CPointer<Unit>()
var Throw = CFunc<(env: JNIEnv_ptr, obj: jthrowable) -> jint>(CPointer<Unit>())
var ThrowNew = CFunc<(env: JNIEnv_ptr, clazz: jclass, msg: CString) -> jint>(CPointer<Unit>())
var ExceptionOccurred = CFunc<(env: JNIEnv_ptr) -> jthrowable>(CPointer<Unit>())
var ExceptionDescribe = CFunc<(env: JNIEnv_ptr) -> Unit>(CPointer<Unit>())
var ExceptionClear = CFunc<(env: JNIEnv_ptr) -> Unit>(CPointer<Unit>())
private var FatalError = CPointer<Unit>()
var PushLocalFrame = CFunc<(env: JNIEnv_ptr, capacity: jint) -> jint>(CPointer<Unit>())
var PopLocalFrame = CFunc<(env: JNIEnv_ptr, result: jobject) -> jobject>(CPointer<Unit>())
var NewGlobalRef = CFunc<(env: JNIEnv_ptr, obj: jobject) -> jobject>(CPointer<Unit>())
var DeleteGlobalRef = CFunc<(env: JNIEnv_ptr, obj: jobject) -> Unit>(CPointer<Unit>())
var DeleteLocalRef = CFunc<(env: JNIEnv_ptr, obj: jobject) -> Unit>(CPointer<Unit>())
private var IsSameObject = CPointer<Unit>()
var NewLocalRef = CFunc<(env: JNIEnv_ptr, obj: jobject) -> jobject>(CPointer<Unit>())
private var EnsureLocalCapacity = CPointer<Unit>()
private var AllocObject = CPointer<Unit>()
var NewObject = CFunc<(env: JNIEnv_ptr, clazz: jclass, methodID: jmethodID) -> jobject>(CPointer<Unit>())
private var NewObjectV = CPointer<Unit>()
var NewObjectA = CFunc<(env: JNIEnv_ptr, clazz: jclass, methodID: jmethodID, arg: CPointer<jvalue>) -> jobject>(CPointer<Unit>())
var GetObjectClass = CFunc<(env: JNIEnv_ptr, obj: jobject) -> jclass>(CPointer<Unit>())
var IsInstanceOf = CFunc<(env: JNIEnv_ptr, obj: jobject, clazz: jclass) -> jboolean>(CPointer<Unit>())
var GetMethodID = CFunc<(env: JNIEnv_ptr, clazz: jclass, name: CString, sig: CString) -> jmethodID>(CPointer<Unit>())
var CallObjectMethod = CFunc<(JNIEnv_ptr, jobject, jmethodID) -> jobject>(CPointer<Unit>())
private var CallObjectMethodV = CPointer<Unit>()
var CallObjectMethodA = CFunc<(JNIEnv_ptr, jobject, jmethodID, CPointer<jvalue>) -> jobject>(CPointer<Unit>())
var CallBooleanMethod = CFunc<(JNIEnv_ptr, jobject, jmethodID) -> jboolean>(CPointer<Unit>())
private var CallBooleanMethodV = CPointer<Unit>()
var CallBooleanMethodA = CFunc<(JNIEnv_ptr, jobject, jmethodID, CPointer<jvalue>) -> jboolean>(CPointer<Unit>())
var CallByteMethod = CFunc<(JNIEnv_ptr, jobject, jmethodID) -> jbyte>(CPointer<Unit>())
private var CallByteMethodV = CPointer<Unit>()
var CallByteMethodA = CFunc<(JNIEnv_ptr, jobject, jmethodID, CPointer<jvalue>) -> jbyte>(CPointer<Unit>())
var CallCharMethod = CFunc<(JNIEnv_ptr, jobject, jmethodID) -> jchar>(CPointer<Unit>())
private var CallCharMethodV = CPointer<Unit>()
var CallCharMethodA = CFunc<(JNIEnv_ptr, jobject, jmethodID, CPointer<jvalue>) -> jchar>(CPointer<Unit>())
var CallShortMethod = CFunc<(JNIEnv_ptr, jobject, jmethodID) -> jshort>(CPointer<Unit>())
private var CallShortMethodV = CPointer<Unit>()
var CallShortMethodA = CFunc<(JNIEnv_ptr, jobject, jmethodID, CPointer<jvalue>) -> jshort>(CPointer<Unit>())
var CallIntMethod = CFunc<(JNIEnv_ptr, jobject, jmethodID) -> jint>(CPointer<Unit>())
private var CallIntMethodV = CPointer<Unit>()
var CallIntMethodA = CFunc<(JNIEnv_ptr, jobject, jmethodID, CPointer<jvalue>) -> jint>(CPointer<Unit>())
var CallLongMethod = CFunc<(JNIEnv_ptr, jobject, jmethodID) -> jlong>(CPointer<Unit>())
private var CallLongMethodV = CPointer<Unit>()
var CallLongMethodA = CFunc<(JNIEnv_ptr, jobject, jmethodID, CPointer<jvalue>) -> jlong>(CPointer<Unit>())
var CallFloatMethod = CFunc<(JNIEnv_ptr, jobject, jmethodID) -> jfloat>(CPointer<Unit>())
private var CallFloatMethodV = CPointer<Unit>()
var CallFloatMethodA = CFunc<(JNIEnv_ptr, jobject, jmethodID, CPointer<jvalue>) -> jfloat>(CPointer<Unit>())
var CallDoubleMethod = CFunc<(JNIEnv_ptr, jobject, jmethodID) -> jdouble>(CPointer<Unit>())
private var CallDoubleMethodV = CPointer<Unit>()
var CallDoubleMethodA = CFunc<(JNIEnv_ptr, jobject, jmethodID, CPointer<jvalue>) -> jdouble>(CPointer<Unit>())
var CallVoidMethod = CFunc<(env: JNIEnv_ptr, obj: jobject, methodID: jmethodID) -> Unit>(CPointer<Unit>())
private var CallVoidMethodV = CPointer<Unit>()
var CallVoidMethodA = CFunc<(JNIEnv_ptr, jobject, jmethodID, CPointer<jvalue>) -> Unit>(CPointer<Unit>())
var CallNonvirtualObjectMethod = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID) -> jobject>(CPointer<Unit>())
private var CallNonvirtualObjectMethodV = CPointer<Unit>()
var CallNonvirtualObjectMethodA = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID, CPointer<jvalue>) -> jobject>(CPointer<Unit>())
var CallNonvirtualBooleanMethod = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID) -> jboolean>(CPointer<Unit>())
private var CallNonvirtualBooleanMethodV = CPointer<Unit>()
var CallNonvirtualBooleanMethodA = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID, CPointer<jvalue>) -> jboolean>(CPointer<Unit>())
var CallNonvirtualByteMethod = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID) -> jbyte>(CPointer<Unit>())
private var CallNonvirtualByteMethodV = CPointer<Unit>()
var CallNonvirtualByteMethodA = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID, CPointer<jvalue>) -> jbyte>(CPointer<Unit>())
var CallNonvirtualCharMethod = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID) -> jchar>(CPointer<Unit>())
private var CallNonvirtualCharMethodV = CPointer<Unit>()
var CallNonvirtualCharMethodA = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID, CPointer<jvalue>) -> jchar>(CPointer<Unit>())
var CallNonvirtualShortMethod = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID) -> jshort>(CPointer<Unit>())
private var CallNonvirtualShortMethodV = CPointer<Unit>()
var CallNonvirtualShortMethodA = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID, CPointer<jvalue>) -> jshort>(CPointer<Unit>())
var CallNonvirtualIntMethod = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID) -> jint>(CPointer<Unit>())
private var CallNonvirtualIntMethodV = CPointer<Unit>()
var CallNonvirtualIntMethodA = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID, CPointer<jvalue>) -> jint>(CPointer<Unit>())
var CallNonvirtualLongMethod = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID) -> jlong>(CPointer<Unit>())
private var CallNonvirtualLongMethodV = CPointer<Unit>()
var CallNonvirtualLongMethodA = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID, CPointer<jvalue>) -> jlong>(CPointer<Unit>())
var CallNonvirtualFloatMethod = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID) -> jfloat>(CPointer<Unit>())
private var CallNonvirtualFloatMethodV = CPointer<Unit>()
var CallNonvirtualFloatMethodA = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID, CPointer<jvalue>) -> jfloat>(CPointer<Unit>())
var CallNonvirtualDoubleMethod = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID) -> jdouble>(CPointer<Unit>())
private var CallNonvirtualDoubleMethodV = CPointer<Unit>()
var CallNonvirtualDoubleMethodA = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID, CPointer<jvalue>) -> jdouble>(CPointer<Unit>())
var CallNonvirtualVoidMethod = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID) -> Unit>(CPointer<Unit>())
private var CallNonvirtualVoidMethodV = CPointer<Unit>()
var CallNonvirtualVoidMethodA = CFunc<(JNIEnv_ptr, jobject, jclass, jmethodID, CPointer<jvalue>) -> Unit>(CPointer<Unit>())
var GetFieldID = CFunc<(JNIEnv_ptr, jclass, CString, CString) -> jfieldID>(CPointer<Unit>())
var GetObjectField = CFunc<(JNIEnv_ptr, jobject, jfieldID) -> jobject>(CPointer<Unit>())
var GetBooleanField = CFunc<(JNIEnv_ptr, jobject, jfieldID) -> jboolean>(CPointer<Unit>())
var GetByteField = CFunc<(JNIEnv_ptr, jobject, jfieldID) -> jbyte>(CPointer<Unit>())
var GetCharField = CFunc<(JNIEnv_ptr, jobject, jfieldID) -> jchar>(CPointer<Unit>())
var GetShortField = CFunc<(JNIEnv_ptr, jobject, jfieldID) -> jshort>(CPointer<Unit>())
var GetIntField = CFunc<(JNIEnv_ptr, jobject, jfieldID) -> jint>(CPointer<Unit>())
var GetLongField = CFunc<(JNIEnv_ptr, jobject, jfieldID) -> jlong>(CPointer<Unit>())
var GetFloatField = CFunc<(JNIEnv_ptr, jobject, jfieldID) -> jfloat>(CPointer<Unit>())
var GetDoubleField = CFunc<(JNIEnv_ptr, jobject, jfieldID) -> jdouble>(CPointer<Unit>())
var SetObjectField = CFunc<(JNIEnv_ptr, jobject, jfieldID, jobject) -> Unit>(CPointer<Unit>())
var SetBooleanField = CFunc<(JNIEnv_ptr, jobject, jfieldID, jboolean) -> Unit>(CPointer<Unit>())
var SetByteField = CFunc<(JNIEnv_ptr, jobject, jfieldID, jbyte) -> Unit>(CPointer<Unit>())
var SetCharField = CFunc<(JNIEnv_ptr, jobject, jfieldID, jchar) -> Unit>(CPointer<Unit>())
var SetShortField = CFunc<(JNIEnv_ptr, jobject, jfieldID, jshort) -> Unit>(CPointer<Unit>())
var SetIntField = CFunc<(JNIEnv_ptr, jobject, jfieldID, jint) -> Unit>(CPointer<Unit>())
var SetLongField = CFunc<(JNIEnv_ptr, jobject, jfieldID, jlong) -> Unit>(CPointer<Unit>())
var SetFloatField = CFunc<(JNIEnv_ptr, jobject, jfieldID, jfloat) -> Unit>(CPointer<Unit>())
var SetDoubleField = CFunc<(JNIEnv_ptr, jobject, jfieldID, jdouble) -> Unit>(CPointer<Unit>())
var GetStaticMethodID = CFunc<(JNIEnv_ptr, jclass, CString, CString) -> jmethodID>(CPointer<Unit>())
var CallStaticObjectMethod = CFunc<(JNIEnv_ptr, jclass, jmethodID) -> jobject>(CPointer<Unit>())
private var CallStaticObjectMethodV = CPointer<Unit>()
var CallStaticObjectMethodA = CFunc<(JNIEnv_ptr, jclass, jmethodID, CPointer<jvalue>) -> jobject>(CPointer<Unit>())
var CallStaticBooleanMethod = CFunc<(JNIEnv_ptr, jclass, jmethodID) -> jboolean>(CPointer<Unit>())
private var CallStaticBooleanMethodV = CPointer<Unit>()
var CallStaticBooleanMethodA = CFunc<(JNIEnv_ptr, jclass, jmethodID, CPointer<jvalue>) -> jboolean>(CPointer<Unit>())
var CallStaticByteMethod = CFunc<(JNIEnv_ptr, jclass, jmethodID) -> jbyte>(CPointer<Unit>())
private var CallStaticByteMethodV = CPointer<Unit>()
var CallStaticByteMethodA = CFunc<(JNIEnv_ptr, jclass, jmethodID, CPointer<jvalue>) -> jbyte>(CPointer<Unit>())
var CallStaticCharMethod = CFunc<(JNIEnv_ptr, jclass, jmethodID) -> jchar>(CPointer<Unit>())
private var CallStaticCharMethodV = CPointer<Unit>()
var CallStaticCharMethodA = CFunc<(JNIEnv_ptr, jclass, jmethodID, CPointer<jvalue>) -> jchar>(CPointer<Unit>())
var CallStaticShortMethod = CFunc<(JNIEnv_ptr, jclass, jmethodID) -> jshort>(CPointer<Unit>())
private var CallStaticShortMethodV = CPointer<Unit>()
var CallStaticShortMethodA = CFunc<(JNIEnv_ptr, jclass, jmethodID, CPointer<jvalue>) -> jshort>(CPointer<Unit>())
var CallStaticIntMethod = CFunc<(JNIEnv_ptr, jclass, jmethodID) -> jint>(CPointer<Unit>())
private var CallStaticIntMethodV = CPointer<Unit>()
var CallStaticIntMethodA = CFunc<(JNIEnv_ptr, jclass, jmethodID, CPointer<jvalue>) -> jint>(CPointer<Unit>())
var CallStaticLongMethod = CFunc<(JNIEnv_ptr, jclass, jmethodID) -> jlong>(CPointer<Unit>())
private var CallStaticLongMethodV = CPointer<Unit>()
var CallStaticLongMethodA = CFunc<(JNIEnv_ptr, jclass, jmethodID, CPointer<jvalue>) -> jlong>(CPointer<Unit>())
var CallStaticFloatMethod = CFunc<(JNIEnv_ptr, jclass, jmethodID) -> jfloat>(CPointer<Unit>())
private var CallStaticFloatMethodV = CPointer<Unit>()
var CallStaticFloatMethodA = CFunc<(JNIEnv_ptr, jclass, jmethodID, CPointer<jvalue>) -> jfloat>(CPointer<Unit>())
var CallStaticDoubleMethod = CFunc<(JNIEnv_ptr, jclass, jmethodID) -> jdouble>(CPointer<Unit>())
private var CallStaticDoubleMethodV = CPointer<Unit>()
var CallStaticDoubleMethodA = CFunc<(JNIEnv_ptr, jclass, jmethodID, CPointer<jvalue>) -> jdouble>(CPointer<Unit>())
var CallStaticVoidMethod = CFunc<(JNIEnv_ptr, jclass, jmethodID) -> Unit>(CPointer<Unit>())
private var CallStaticVoidMethodV = CPointer<Unit>()
var CallStaticVoidMethodA = CFunc<(JNIEnv_ptr, jclass, jmethodID, CPointer<jvalue>) -> Unit>(CPointer<Unit>())
var GetStaticFieldID = CFunc<(JNIEnv_ptr, jclass, CString, CString) -> jfieldID>(CPointer<Unit>())
var GetStaticObjectField = CFunc<(JNIEnv_ptr, jclass, jfieldID) -> jobject>(CPointer<Unit>())
var GetStaticBooleanField = CFunc<(JNIEnv_ptr, jclass, jfieldID) -> jboolean>(CPointer<Unit>())
var GetStaticByteField = CFunc<(JNIEnv_ptr, jclass, jfieldID) -> jbyte>(CPointer<Unit>())
var GetStaticCharField = CFunc<(JNIEnv_ptr, jclass, jfieldID) -> jchar>(CPointer<Unit>())
var GetStaticShortField = CFunc<(JNIEnv_ptr, jclass, jfieldID) -> jshort>(CPointer<Unit>())
var GetStaticIntField = CFunc<(JNIEnv_ptr, jclass, jfieldID) -> jint>(CPointer<Unit>())
var GetStaticLongField = CFunc<(JNIEnv_ptr, jclass, jfieldID) -> jlong>(CPointer<Unit>())
var GetStaticFloatField = CFunc<(JNIEnv_ptr, jclass, jfieldID) -> jfloat>(CPointer<Unit>())
var GetStaticDoubleField = CFunc<(JNIEnv_ptr, jclass, jfieldID) -> jdouble>(CPointer<Unit>())
var SetStaticObjectField = CFunc<(JNIEnv_ptr, jclass, jfieldID, jobject) -> Unit>(CPointer<Unit>())
var SetStaticBooleanField = CFunc<(JNIEnv_ptr, jclass, jfieldID, jboolean) -> Unit>(CPointer<Unit>())
var SetStaticByteField = CFunc<(JNIEnv_ptr, jclass, jfieldID, jbyte) -> Unit>(CPointer<Unit>())
var SetStaticCharField = CFunc<(JNIEnv_ptr, jclass, jfieldID, jchar) -> Unit>(CPointer<Unit>())
var SetStaticShortField = CFunc<(JNIEnv_ptr, jclass, jfieldID, jshort) -> Unit>(CPointer<Unit>())
var SetStaticIntField = CFunc<(JNIEnv_ptr, jclass, jfieldID, jint) -> Unit>(CPointer<Unit>())
var SetStaticLongField = CFunc<(JNIEnv_ptr, jclass, jfieldID, jlong) -> Unit>(CPointer<Unit>())
var SetStaticFloatField = CFunc<(JNIEnv_ptr, jclass, jfieldID, jfloat) -> Unit>(CPointer<Unit>())
var SetStaticDoubleField = CFunc<(JNIEnv_ptr, jclass, jfieldID, jdouble) -> Unit>(CPointer<Unit>())
private var NewString = CPointer<Unit>()
private var GetStringLength = CPointer<Unit>()
private var GetStringChars = CPointer<Unit>()
private var ReleaseStringChars = CPointer<Unit>()
var NewStringUTF = CFunc<(JNIEnv_ptr, CString) -> jstring>(CPointer<Unit>())
var GetStringUTFLength = CFunc<(JNIEnv_ptr, jstring) -> jsize>(CPointer<Unit>())
var GetStringUTFChars = CFunc<(JNIEnv_ptr, jstring, CPointer<jboolean>) -> CString>(CPointer<Unit>())
var ReleaseStringUTFChars = CFunc<(JNIEnv_ptr, jstring, CString) -> Unit>(CPointer<Unit>())
var GetArrayLength = CFunc<(JNIEnv_ptr, jarray) -> jsize>(CPointer<Unit>())
var NewObjectArray = CFunc<(JNIEnv_ptr, jsize, jclass, jobject) -> jobjectArray>(CPointer<Unit>())
var GetObjectArrayElement = CFunc<(JNIEnv_ptr, jobjectArray, jsize) -> jobject>(CPointer<Unit>())
var SetObjectArrayElement = CFunc<(JNIEnv_ptr, jobjectArray, jsize, jobject) -> Unit>(CPointer<Unit>())
var NewBooleanArray = CFunc<(JNIEnv_ptr, jsize) -> jbooleanArray>(CPointer<Unit>())
var NewByteArray = CFunc<(JNIEnv_ptr, jsize) -> jbyteArray>(CPointer<Unit>())
var NewCharArray = CFunc<(JNIEnv_ptr, jsize) -> jcharArray>(CPointer<Unit>())
var NewShortArray = CFunc<(JNIEnv_ptr, jsize) -> jshortArray>(CPointer<Unit>())
var NewIntArray = CFunc<(JNIEnv_ptr, jsize) -> jintArray>(CPointer<Unit>())
var NewLongArray = CFunc<(JNIEnv_ptr, jsize) -> jlongArray>(CPointer<Unit>())
var NewFloatArray = CFunc<(JNIEnv_ptr, jsize) -> jfloatArray>(CPointer<Unit>())
var NewDoubleArray = CFunc<(JNIEnv_ptr, jsize) -> jdoubleArray>(CPointer<Unit>())
var GetBooleanArrayElements = CFunc<(JNIEnv_ptr, jbooleanArray, CPointer<jboolean>) -> CPointer<jboolean>>(CPointer<Unit>())
var GetByteArrayElements = CFunc<(JNIEnv_ptr, jbyteArray, CPointer<jboolean>) -> CPointer<jbyte>>(CPointer<Unit>())
var GetCharArrayElements = CFunc<(JNIEnv_ptr, jcharArray, CPointer<jboolean>) -> CPointer<jchar>>(CPointer<Unit>())
var GetShortArrayElements = CFunc<(JNIEnv_ptr, jshortArray, CPointer<jboolean>) -> CPointer<jshort>>(CPointer<Unit>())
var GetIntArrayElements = CFunc<(JNIEnv_ptr, jintArray, CPointer<jboolean>) -> CPointer<jint>>(CPointer<Unit>())
var GetLongArrayElements = CFunc<(JNIEnv_ptr, jlongArray, CPointer<jboolean>) -> CPointer<jlong>>(CPointer<Unit>())
var GetFloatArrayElements = CFunc<(JNIEnv_ptr, jfloatArray, CPointer<jboolean>) -> CPointer<jfloat>>(CPointer<Unit>())
var GetDoubleArrayElements = CFunc<(JNIEnv_ptr, jdoubleArray, CPointer<jboolean>) -> CPointer<jdouble>>(CPointer<Unit>())
var ReleaseBooleanArrayElements = CFunc<(JNIEnv_ptr, jbooleanArray, CPointer<jboolean>, jint) -> Unit>(CPointer<Unit>())
var ReleaseByteArrayElements = CFunc<(JNIEnv_ptr, jbyteArray, CPointer<jbyte>, jint) -> Unit>(CPointer<Unit>())
var ReleaseCharArrayElements = CFunc<(JNIEnv_ptr, jcharArray, CPointer<jchar>, jint) -> Unit>(CPointer<Unit>())
var ReleaseShortArrayElements = CFunc<(JNIEnv_ptr, jshortArray, CPointer<jshort>, jint) -> Unit>(CPointer<Unit>())
var ReleaseIntArrayElements = CFunc<(JNIEnv_ptr, jintArray, CPointer<jint>, jint) -> Unit>(CPointer<Unit>())
var ReleaseLongArrayElements = CFunc<(JNIEnv_ptr, jlongArray, CPointer<jlong>, jint) -> Unit>(CPointer<Unit>())
var ReleaseFloatArrayElements = CFunc<(JNIEnv_ptr, jfloatArray, CPointer<jfloat>, jint) -> Unit>(CPointer<Unit>())
var ReleaseDoubleArrayElements = CFunc<(JNIEnv_ptr, jdoubleArray, CPointer<jdouble>, jint) -> Unit>(CPointer<Unit>())
var GetBooleanArrayRegion = CFunc<(JNIEnv_ptr, jbooleanArray, jsize, jsize, CPointer<jboolean>) -> Unit>(CPointer<Unit>())
var GetByteArrayRegion = CFunc<(JNIEnv_ptr, jbyteArray, jsize, jsize, CPointer<jbyte>) -> Unit>(CPointer<Unit>())
var GetCharArrayRegion = CFunc<(JNIEnv_ptr, jcharArray, jsize, jsize, CPointer<jchar>) -> Unit>(CPointer<Unit>())
var GetShortArrayRegion = CFunc<(JNIEnv_ptr, jshortArray, jsize, jsize, CPointer<jshort>) -> Unit>(CPointer<Unit>())
var GetIntArrayRegion = CFunc<(JNIEnv_ptr, jintArray, jsize, jsize, CPointer<jint>) -> Unit>(CPointer<Unit>())
var GetLongArrayRegion = CFunc<(JNIEnv_ptr, jlongArray, jsize, jsize, CPointer<jlong>) -> Unit>(CPointer<Unit>())
var GetFloatArrayRegion = CFunc<(JNIEnv_ptr, jfloatArray, jsize, jsize, CPointer<jfloat>) -> Unit>(CPointer<Unit>())
var GetDoubleArrayRegion = CFunc<(JNIEnv_ptr, jdoubleArray, jsize, jsize, CPointer<jdouble>) -> Unit>(CPointer<Unit>())
var SetBooleanArrayRegion = CFunc<(JNIEnv_ptr, jbooleanArray, jsize, jsize, CPointer<jboolean>) -> Unit>(CPointer<Unit>())
var SetByteArrayRegion = CFunc<(JNIEnv_ptr, jbyteArray, jsize, jsize, CPointer<jbyte>) -> Unit>(CPointer<Unit>())
var SetCharArrayRegion = CFunc<(JNIEnv_ptr, jcharArray, jsize, jsize, CPointer<jchar>) -> Unit>(CPointer<Unit>())
var SetShortArrayRegion = CFunc<(JNIEnv_ptr, jshortArray, jsize, jsize, CPointer<jshort>) -> Unit>(CPointer<Unit>())
var SetIntArrayRegion = CFunc<(JNIEnv_ptr, jintArray, jsize, jsize, CPointer<jint>) -> Unit>(CPointer<Unit>())
var SetLongArrayRegion = CFunc<(JNIEnv_ptr, jlongArray, jsize, jsize, CPointer<jlong>) -> Unit>(CPointer<Unit>())
var SetFloatArrayRegion = CFunc<(JNIEnv_ptr, jfloatArray, jsize, jsize, CPointer<jfloat>) -> Unit>(CPointer<Unit>())
var SetDoubleArrayRegion = CFunc<(JNIEnv_ptr, jdoubleArray, jsize, jsize, CPointer<jdouble>) -> Unit>(CPointer<Unit>())
private var RegisterNatives = CPointer<Unit>()
private var UnregisterNatives = CPointer<Unit>()
var MonitorEnter = CFunc<(JNIEnv_ptr, jobject) -> jint>(CPointer<Unit>())
var MonitorExit = CFunc<(JNIEnv_ptr, jobject) -> jint>(CPointer<Unit>())
private var GetJavaVM = CPointer<Unit>()
private var GetStringRegion = CPointer<Unit>()
private var GetStringUTFRegion = CPointer<Unit>()
private var GetPrimitiveArrayCritical = CPointer<Unit>()
private var ReleasePrimitiveArrayCritical = CPointer<Unit>()
private var GetStringCritical = CPointer<Unit>()
private var ReleaseStringCritical = CPointer<Unit>()
var NewWeakGlobalRef = CFunc<(JNIEnv_ptr, jobject) -> jweak>(CPointer<Unit>())
var DeleteWeakGlobalRef = CFunc<(JNIEnv_ptr, jweak) -> Unit>(CPointer<Unit>())
private var ExceptionCheck = CPointer<Unit>()
private var NewDirectByteBuffer = CPointer<Unit>()
private var GetDirectBufferAddress = CPointer<Unit>()
private var GetDirectBufferCapacity = CPointer<Unit>()
/* New JNI 1.6 Features */
private var GetObjectRefType = CPointer<Unit>()
/* Module Features */
private var GetModule = CPointer<Unit>()
}
@C
protected struct JNIInvokeInterface_ {
private var reserved0 = CPointer<Unit>()
private var reserved1 = CPointer<Unit>()
private var reserved2 = CPointer<Unit>()
var DestroyJavaVM = CFunc<(CPointer<JavaVM>) -> jint>(CPointer<Unit>())
var AttachCurrentThread = CFunc<(CPointer<JavaVM>, CPointer<JNIEnv_ptr>, CPointer<Unit>) -> jint>(CPointer<Unit>())
var DetachCurrentThread = CFunc<(CPointer<JavaVM>) -> jint>(CPointer<Unit>())
var GetEnv = CFunc<(CPointer<JavaVM>, CPointer<JNIEnv_ptr>, jint) -> jint>(CPointer<Unit>())
var AttachCurrentThreadAsDaemon = CFunc<(CPointer<JavaVM>, CPointer<JNIEnv_ptr>, CPointer<Unit>) -> jint>(CPointer<Unit>())
}
protected class InteropLibException <: Exception {
public init(reason: String) {
super(reason)
}
protected override func getClassName(): String {
return "InteropLibException"
}
}
protected enum Java_CFFI_BaseSignature {
| BYTE // b
| CHAR // c
| DOUBLE // d
| FLOAT // f
| INT // i
| LONG // j
| SHORT // s
| BOOLEAN // b
| VOID // v TODO: check that field or param cannot be VOID
public func toString(): String {
match (this) {
case BYTE => "byte"
case CHAR => "char"
case DOUBLE => "double"
case FLOAT => "float"
case INT => "int"
case LONG => "long"
case SHORT => "short"
case BOOLEAN => "boolean"
case VOID => "void"
}
}
func forJNI(): String {
match (this) {
case BYTE => "B"
case CHAR => "C"
case DOUBLE => "D"
case FLOAT => "F"
case INT => "I"
case LONG => "J"
case SHORT => "S"
case BOOLEAN => "Z"
case VOID => "V"
}
}
}
protected enum Java_CFFI_ObjectSignature {
| REFERENCE(String) // LClassName; TODO: class name correctness checks?
| ARRAY(Java_CFFI_ComponentSignature) // [ComponentSignature
public func toString(): String {
match (this) {
case REFERENCE(ref) => "REFERENCE(${ref.toString()})"
case ARRAY(component) => "ARRAY(${component.toString()})"
}
}
func forJNI(): String {
match (this) {
case REFERENCE(ref) => "L" + ref + ";"
case ARRAY(component) => "[" + component.forJNI()
}
}
}
private func checkObjectSignature(signature: String, parsed: Java_CFFI_ComponentSignature, obj: Java_CFFI_ObjectSignature) : Unit {
match (obj) {
case REFERENCE(className) =>
if (className.size + 2 != signature.size /*L and ;*/) {
throw InteropLibException("Cannot parse object signature ${signature}")
}
case ARRAY(_) => { =>
let innerClass = Java_CFFI_innermostClass(parsed)
let innerClassName = Java_CFFI_className(innerClass)
let dimensions = Java_CFFI_arrayDimensions(parsed)
let correction = match(innerClass) {
case PRIMITIVE(_) => -innerClassName.size + 1
case OBJECT(obj) => match(obj) {
case REFERENCE(_) => 2
case ARRAY(_) => assert(false); -123456
}
}
if (dimensions + innerClassName.size + correction != signature.size) {
throw InteropLibException("Cannot parse array signature ${signature}")
}
}()
}
}
protected enum Java_CFFI_ComponentSignature {
| PRIMITIVE(Java_CFFI_BaseSignature)
| OBJECT(Java_CFFI_ObjectSignature)
public func toString(): String {
match (this) {
case PRIMITIVE(base) => "PRIMITIVE(${base.toString()})"
case OBJECT(obj) => "OBJECT(${obj.toString()})"
}
}
func forJNI(): String {
match (this) {
case PRIMITIVE(base) => base.forJNI()
case OBJECT(obj) => obj.forJNI()
}
}
}
protected class Java_CFFI_MethodSignature {
public let params: Array<Java_CFFI_ComponentSignature>
public let ret: Java_CFFI_ComponentSignature
init(params: Array<Java_CFFI_ComponentSignature>, ret: Java_CFFI_ComponentSignature) {
this.params = params
this.ret = ret
}
public func toString(): String {
let buffer = StringBuilder()
buffer.append("(")
for (i in 0..params.size) {
buffer.append(Java_CFFI_className(params[i]))
if (i != params.size - 1) {
buffer.append(", ")
}
}
buffer.append("): ")
buffer.append(Java_CFFI_className(ret))
buffer.toString()
}
func forJNI(): String {
let buffer = StringBuilder()
buffer.append("(")
for (i in 0..params.size) {
buffer.append(params[i].forJNI())
}
buffer.append(")")
buffer.append(ret.forJNI())
buffer.toString()
}
}
protected enum Java_CFFI_EntitySignature {
| FIELD(Java_CFFI_ComponentSignature)
| METHOD(Java_CFFI_MethodSignature)
public func toString(): String {
match (this) {
case FIELD(component) => "FIELD(${component.toString()})"
case METHOD(method) => "METHOD(${method.toString()})"
}
}
func forJNI(): String {
match (this) {
case FIELD(component) => component.forJNI()
case METHOD(method) => method.forJNI()
}
}
}
private func Java_CFFI_arrayDimensions(obj: Java_CFFI_ComponentSignature): Int64 {
func arrayDimensionsStep(o: Java_CFFI_ComponentSignature, step: Int64): Int64 {
match (o) {
case OBJECT(ARRAY(component)) => arrayDimensionsStep(component, step + 1)
case _ => step
}
}
arrayDimensionsStep(obj, 0)
}
private func Java_CFFI_innermostClass(obj: Java_CFFI_ComponentSignature): Java_CFFI_ComponentSignature {
match (obj) {
case OBJECT(ARRAY(component)) => Java_CFFI_innermostClass(component)
case _ => obj
}
}
private func Java_CFFI_className(obj: Java_CFFI_ComponentSignature): String {
let buffer = StringBuilder()
let inner = Java_CFFI_innermostClass(obj)
let innerName = match (inner) {
case PRIMITIVE(base) => base.toString()
case OBJECT(obj) => match (obj) {
case REFERENCE(name) => name
case ARRAY(_) => assert(false); "ERROR"
}
}
buffer.append(innerName)
for (_ in 0..Java_CFFI_arrayDimensions(obj)) {
buffer.append("[]")
}
buffer.toString()
}
private func assert(cond: Bool) {
if (!cond) {
throw Exception("interoplib inner error")
}
}
private func signatureBaseCheck(signature: String) {
if (signature.isEmpty()) {
throw InteropLibException("Cannot parse empty signature")
}
if (!signature.isAscii()) {
throw InteropLibException("Invalid non-ASCII signature ${signature}")
}
}
protected func Java_CFFI_parseComponentSignature(signature: String): Java_CFFI_ComponentSignature {
signatureBaseCheck(signature)
let parsed = Java_CFFI_parseComponentSignatureImpl(unsafe { signature.rawData() }, 0)
// check that parsed all string
match (parsed) {
case OBJECT(obj) => checkObjectSignature(signature, parsed, obj)
case PRIMITIVE(_) =>
if (signature.size != 1) {
throw InteropLibException("Cannot parse primitive signature ${signature}")
}
}
parsed
}
private func classNameBorder(signature: Array<Byte>, begin: Int64): Int64 {
assert(signature[begin] == b'L')
var i = begin + 1
while (signature[i] != b';') {
if (i >= signature.size) {
throw InteropLibException("Cannot parse object signature ${String.fromUtf8(signature)} (from ${begin})")
}
i += 1
}
i
}
private func arrayCopy<T>(dst: Array<T>, dstBegIdx: Int64, src: Array<T>, srcBegIdx: Int64, srcEndIdx: Int64) {
var i = dstBegIdx
var j = srcBegIdx
while (j < srcEndIdx) {
dst[i] = src[j]
i += 1
j += 1
}
}
private func Java_CFFI_parseComponentSignatureImpl(signature: Array<Byte>, begin: Int64): Java_CFFI_ComponentSignature {
let remainSize = signature.size - begin
assert(remainSize > 0)
match (signature[begin]) {
case b'B' => PRIMITIVE(BYTE)
case b'C' => PRIMITIVE(CHAR)
case b'D' => PRIMITIVE(DOUBLE)
case b'F' => PRIMITIVE(FLOAT)
case b'I' => PRIMITIVE(INT)
case b'J' => PRIMITIVE(LONG)
case b'S' => PRIMITIVE(SHORT)
case b'Z' => PRIMITIVE(BOOLEAN)
case b'V' => PRIMITIVE(VOID)
case b'L' => { =>
let classNameBegin = begin + 1
let classNameEnd = classNameBorder(signature, begin)
let classNameLength = classNameEnd - classNameBegin
let classNameBytes = Array<Byte>(classNameLength, repeat: 0)
arrayCopy(classNameBytes, 0, signature, classNameBegin, classNameEnd)
OBJECT(REFERENCE(String.fromUtf8(classNameBytes)))
}()
case b'[' => OBJECT(ARRAY(Java_CFFI_parseComponentSignatureImpl(signature, begin + 1)))
case _ => throw InteropLibException("Cannot parse component signature ${String.fromUtf8(signature)} (from ${begin})")
}
}
private func restoreObjectSignatureSize(signature: Java_CFFI_ComponentSignature, obj: Java_CFFI_ObjectSignature): Int64 {
match(obj) {
case ARRAY(_) => { =>
let dimensions = Java_CFFI_arrayDimensions(signature)
let innerClass = Java_CFFI_innermostClass(signature)
let innerClassCorrection = match (innerClass) {
case PRIMITIVE(_) => 1
case OBJECT(obj) => match(obj) {
case ARRAY(_) => assert(false); -123456
case REFERENCE(name) => name.toString().size + 2
}
}
dimensions + innerClassCorrection
}()
case REFERENCE(name) => name.toString().size + 2
}
}
private func restoreComponentSignatureSize(signature: Java_CFFI_ComponentSignature): Int64 {
match (signature) {
case PRIMITIVE(_) => 1
case OBJECT(obj) => restoreObjectSignatureSize(signature, obj)
}
}
protected func Java_CFFI_parseMethodSignature(signature: String): Java_CFFI_MethodSignature {
signatureBaseCheck(signature)
if (signature.size < 3) {
throw InteropLibException("Cannot parse method signature ${signature}")
}
var pos = 1
let buffer = ArrayQueue<Java_CFFI_ComponentSignature>()
while (signature[pos] != b')') {
let argument = Java_CFFI_parseComponentSignatureImpl(unsafe { signature.rawData() }, pos)
pos += restoreComponentSignatureSize(argument)
buffer.add(argument)
}
pos += 1
if (signature.size - pos <= 0) {
throw InteropLibException("Cannot parse method return type for signature ${signature}")
}
let ret = Java_CFFI_parseComponentSignatureImpl(unsafe { signature.rawData() }, pos)
let args = Array<Java_CFFI_ComponentSignature>(buffer.size, repeat: PRIMITIVE(VOID))
let size = buffer.size
for (i in 0..size) {
args[i] = buffer.remove().getOrThrow()
}
Java_CFFI_MethodSignature(args, ret)
}
protected class Java_CFFI_Class <: ToString {
var obj = jclass()
public let name: String
init(env: JNIEnv_ptr, base: jobject) {
this.name = "<UNNAMED>"
unsafe {
withLocalRef(env, base) { localRef =>
let envCJ = env.read().read()
this.obj = envCJ.GetObjectClass(env, localRef)
}
}
}
public init(env: JNIEnv_ptr, name: String) {
let size = name.size
this.name = if (size > 2 && name[0] == b'L' && name[size-1] == b';') {
name[1..(size - 1)]
} else {
name
}
obj = classLoading.load().getOrThrow().loadClass(env, this.name)
if (obj.isNull()) {
throw InteropLibException("Cannot find class ${name}")
}
}
init(obj: jclass, name: String) {
this.name = name
this.obj = obj
}
public func toString(): String {
name
}
}
protected class Java_CFFI_InvariantFailed <: Exception {
public init() {
super("interoplib invariant failed")
}
protected override func getClassName(): String {
return "Java_CFFI_InvariantFailed"
}
}
protected open class Java_CFFI_EntityID {
let signature: Java_CFFI_EntitySignature
let `static`: Bool
protected init(signature: Java_CFFI_EntitySignature, `static`: Bool) {
this.signature = signature
this.`static` = `static`
}
static func constr(env: JNIEnv_ptr, clazz: Java_CFFI_Class, entityName: String, signature: Java_CFFI_EntitySignature, `static`: Bool): Java_CFFI_EntityID {
let envCJ = unsafe { env.read().read() }
var id = CPointer<Unit>()
unsafe {
let cSignature = LibC.mallocCString(signature.forJNI())
let cEntityName = LibC.mallocCString(entityName)
match (signature) {
case FIELD(_) => if (!`static`) {
id = envCJ.GetFieldID(env, clazz.obj, cEntityName, cSignature)
} else {
id = envCJ.GetStaticFieldID(env, clazz.obj, cEntityName, cSignature)
}
case METHOD(_) => if (!`static`) {
id = envCJ.GetMethodID(env, clazz.obj, cEntityName, cSignature)
} else {
id = envCJ.GetStaticMethodID(env, clazz.obj, cEntityName, cSignature)
}
}
LibC.free(cSignature)
LibC.free(cEntityName)
}
if (id.isNull()) {
if (!`static`) {
throw InteropLibException("Cannot find entity ${entityName} with signature ${signature.toString()} in class ${clazz.name}")
} else {
throw InteropLibException("Cannot find static entity ${entityName} with signature ${signature.toString()} in class ${clazz.name}")
}
}
match (signature) {
case FIELD(_) => Java_CFFI_FieldID(signature, id, `static`)
case METHOD(_) => Java_CFFI_MethodID(signature, id, `static`)
}
}
}
protected class Java_CFFI_FieldID <: Java_CFFI_EntityID {
let id: jfieldID
init(signature: Java_CFFI_EntitySignature, id: jfieldID, `static`: Bool) {
super(signature, `static`)
this.id = id
}
public static func constrStatic(env: JNIEnv_ptr, clazz: Java_CFFI_Class, fieldName: String, signature: Java_CFFI_ComponentSignature): Java_CFFI_FieldID {
match (signature) {
case PRIMITIVE(base) => match (base) {
case VOID => throw InteropLibException("Field cannot have void type")
case _ => ()
}
case OBJECT(_) => ()
}
match (Java_CFFI_EntityID.constr(env, clazz, fieldName, FIELD(signature), true)) {
case field: Java_CFFI_FieldID => field
case _ => throw Java_CFFI_InvariantFailed()
}
}
public static func constr(env: JNIEnv_ptr, clazz: Java_CFFI_Class, fieldName: String, signature: Java_CFFI_ComponentSignature): Java_CFFI_FieldID {
match (signature) {
case PRIMITIVE(base) => match (base) {
case VOID => throw InteropLibException("Field cannot have void type")
case _ => ()
}
case OBJECT(_) => ()
}
match (Java_CFFI_EntityID.constr(env, clazz, fieldName, FIELD(signature), false)) {
case field: Java_CFFI_FieldID => field
case _ => throw Java_CFFI_InvariantFailed()
}
}
}
protected class Java_CFFI_MethodID <: Java_CFFI_EntityID {
let id: jmethodID
init(signature: Java_CFFI_EntitySignature, id: jmethodID, `static`: Bool) {
super(signature, `static`)
this.id = id
}
public static func constrStatic(env: JNIEnv_ptr, clazz: Java_CFFI_Class, methodName: String, signature: Java_CFFI_MethodSignature): Java_CFFI_MethodID {
match (Java_CFFI_EntityID.constr(env, clazz, methodName, METHOD(signature), true)) {
case method: Java_CFFI_MethodID => method
case _ => throw Java_CFFI_InvariantFailed()
}
}
public static func constr(env: JNIEnv_ptr, clazz: Java_CFFI_Class, methodName: String, signature: Java_CFFI_MethodSignature): Java_CFFI_MethodID {
match (Java_CFFI_EntityID.constr(env, clazz, methodName, METHOD(signature), false)) {
case method: Java_CFFI_MethodID => method
case _ => throw Java_CFFI_InvariantFailed()
}
}
}
private func withLocalRef<R>(env: JNIEnv_ptr, obj: jobject, action: (ref: jobject) -> R): R {
unsafe {
let envCJ = env.read().read()
let localRef = envCJ.NewLocalRef(env, obj)
try {
action(localRef)
} finally {
envCJ.DeleteLocalRef(env, localRef)
}
}
}
protected func getLocalRef(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity): Java_CFFI_JavaEntity {
unsafe {
let envCJ = env.read().read()
let localRef = envCJ.NewLocalRef(env, obj.asJObject())
Java_CFFI_JavaEntity(JOBJECT, localRef)
}
}
protected func deleteLocalRef(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity): Unit {
unsafe {
let envCJ = env.read().read()
let localRef = envCJ.DeleteLocalRef(env, obj.asJObject())
}
}
private func withPinnedArray<T, R>(array: Array<T>, action: (array: CPointer<T>) -> R): R where T <: CType {
unsafe {
let handle = acquireArrayRawData<T>(array)
try {
action(handle.pointer)
} finally {
releaseArrayRawData<T>(handle)
}
}
}
public enum Java_CFFI_JavaEntityKind <: ToString {
| JVOID
| JOBJECT
| JWEAK
| JSTRONG
| JLONG
| JINT
| JSTRING
| JBOOLEAN
| JSHORT
| JCHAR
| JBYTE
| JFLOAT
| JDOUBLE
| JVALUE
// TODO: is JARRAY needed? Use JOBJECT instead.
public func toString(): String {
match (this) {
case JVOID => "void"
case JOBJECT => "jobject"
case JWEAK => "jweak"
case JSTRONG => "jstrong"
case JLONG => "jlong"
case JINT => "jint"
case JSTRING => "jstring"
case JBOOLEAN => "jboolean"
case JSHORT => "jshort"
case JCHAR => "jchar"
case JBYTE => "jbyte"
case JFLOAT => "jfloat"
case JDOUBLE => "jdouble"
case JVALUE => "jvalue"
}
}
}
private unsafe func ptrFromInt(value: UInt64): CPointer<Unit> {
CPointer<Unit>(CPointer<UInt8>() + Int64(value))
}
// TODO: implement without heap memory allocation (alloca? intrinsic?)
private unsafe func reinterpretCast<A, B>(value: B): A where A <: CType, B <: CType {
var newValue = zeroValue<A>()
try (fBox = LibC.malloc<B>(count: 1).asResource()) {
fBox.value.write(value)
let iBox = CPointer<A>(fBox.value)
newValue = iBox.read()
}
newValue
}
public struct Java_CFFI_JavaEntity {
let kind: Java_CFFI_JavaEntityKind
private let value: jvalue
public init() {
this.kind = JVOID
this.value = 0
}
public init(kind: Java_CFFI_JavaEntityKind, value: CPointer<Unit>) {
this.kind = kind
this.value = UInt64(value.toUIntNative())
}
public init(kind: Java_CFFI_JavaEntityKind, value: UInt64) {
this.kind = kind
this.value = value
}
public init(kind: Java_CFFI_JavaEntityKind, value: Int64) {
this.kind = kind
this.value = UInt64(value)
}
public init(kind: Java_CFFI_JavaEntityKind, value: Int32) {
this.kind = kind
this.value = UInt64(value)
}
public init(kind: Java_CFFI_JavaEntityKind, value: Int16) {
this.kind = kind
this.value = UInt64(value)
}
public init(kind: Java_CFFI_JavaEntityKind, value: UInt16) {
this.kind = kind
this.value = UInt64(value)
}
public init(kind: Java_CFFI_JavaEntityKind, value: Int8) {
this.kind = kind
this.value = UInt64(value)
}
public init(kind: Java_CFFI_JavaEntityKind, value: UInt8) {
this.kind = kind
this.value = UInt64(value)
}
public init(kind: Java_CFFI_JavaEntityKind, value: Float32) {
this.kind = kind
this.value = unsafe { UInt64(reinterpretCast<UInt32, Float32>(value)) }
}
public init(kind: Java_CFFI_JavaEntityKind, value: Float64) {
this.kind = kind
this.value = unsafe { reinterpretCast<UInt64, Float64>(value) }
}
public init(value: Int64) {
this(JLONG, value)
}
public init(value: Int32) {
this(JINT, value)
}
public init(value: Int16) {
this(JSHORT, value)
}
public init(value: UInt16) {
this(JCHAR, value)
}
public init(value: Int8) {
this(JBYTE, value)
}
public init(value: Bool) {
this(JBOOLEAN, if (value) { 1u8 } else { 0u8 })
}
public init(value: Float32) {
this(JFLOAT, value)
}
public init(value: Float64) {
this(JDOUBLE, value)
}
public unsafe func asUnit(): Unit {
match (kind) {
case JVOID => ()
case _ => throw InteropLibException("Cannot get void from ${kind.toString()}")
}
}
public unsafe func asJObject(): jobject {
match (kind) {
case JVALUE => jobject(ptrFromInt(value))
case JOBJECT => jobject(ptrFromInt(value))
case JSTRING => jobject(ptrFromInt(value))
case JWEAK => jobject(ptrFromInt(value))
case JSTRONG => jobject(ptrFromInt(value))
case _ => throw InteropLibException("Cannot get jobject from ${kind.toString()}")
}
}
public unsafe func asJWeak(): jobject {
match (kind) {
case JWEAK => jobject(ptrFromInt(value))
case _ => throw InteropLibException("Cannot get jweak from ${kind.toString()}")
}
}
public unsafe func asJStrong(): jobject {
match (kind) {
case JSTRONG => jobject(ptrFromInt(value))
case _ => throw InteropLibException("Cannot get jstrong from ${kind.toString()}")
}
}
public unsafe func asJLong(): jlong {
match (kind) {
case JVALUE => Int64(value)
case JLONG => Int64(value)
case _ => throw InteropLibException("Cannot get jlong from ${kind.toString()}")
}
}
public unsafe func asJInt(): jint {
match (kind) {
case JVALUE => Int32(value)
case JINT => Int32(value)
case _ => throw InteropLibException("Cannot get jint from ${kind.toString()}")
}
}
public unsafe func asJString(): jstring {
match (kind) {
case JVALUE => jstring(ptrFromInt(value))
case JOBJECT => jstring(ptrFromInt(value))
case JSTRING => jstring(ptrFromInt(value))
case _ => throw InteropLibException("Cannot get jstring from ${kind.toString()}")
}
}
public unsafe func asJBoolean(): jboolean {
match (kind) {
case JVALUE => UInt8(value)
case JBOOLEAN => UInt8(value)
case _ => throw InteropLibException("Cannot get jboolean from ${kind.toString()}")
}
}
public unsafe func asJShort(): jshort {
match (kind) {
case JVALUE => Int16(value)
case JSHORT => Int16(value)
case _ => throw InteropLibException("Cannot get jshort from ${kind.toString()}")
}
}
public unsafe func asJChar(): jchar {
match (kind) {
case JVALUE => UInt16(value)
case JCHAR => UInt16(value)
case _ => throw InteropLibException("Cannot get jchar from ${kind.toString()}")
}
}
public unsafe func asJByte(): jbyte {
match (kind) {
case JVALUE => Int8(value)
case JBYTE => Int8(value)
case _ => throw InteropLibException("Cannot get jbyte from ${kind.toString()}")
}
}
public unsafe func asJFloat(): jfloat {
match (kind) {
case JVALUE => reinterpretCast<Float32, UInt32>(UInt32(value))
case JFLOAT => reinterpretCast<Float32, UInt32>(UInt32(value))
case _ => throw InteropLibException("Cannot get jfloat from ${kind.toString()}")
}
}
public unsafe func asJDouble(): jdouble {
match (kind) {
case JVALUE => reinterpretCast<Float64, UInt64>(value)
case JDOUBLE => reinterpretCast<Float64, UInt64>(value)
case _ => throw InteropLibException("Cannot get jdouble from ${kind.toString()}")
}
}
/* public */ unsafe func asJValue(): jvalue {
value
}
public prop isNull: Bool {
get() {
return value == 0u64
}
}
}
protected class Java_CFFI_JavaCallNest {
let array: Array<jvalue>
let size: Int64
init() {
array = []
size = 0
}
public init(n: Int64) {
array = Array<jvalue>(n, repeat: 0)
size = array.size
}
}
private unsafe func noParamsCallMethod(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, method: Java_CFFI_MethodID, signature: Java_CFFI_MethodSignature): Java_CFFI_JavaEntity {
match (signature.ret) {
case PRIMITIVE(base) => match (base) {
case BYTE =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualByteMethod(env, localRef, envCJ.GetObjectClass(env, localRef), method.id)
Java_CFFI_JavaEntity(JBYTE, jResult)
}
case CHAR =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualCharMethod(env, localRef, envCJ.GetObjectClass(env, localRef), method.id)
Java_CFFI_JavaEntity(JCHAR, jResult)
}
case DOUBLE =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualDoubleMethod(env, localRef, envCJ.GetObjectClass(env, localRef), method.id)
Java_CFFI_JavaEntity(JDOUBLE, jResult)
}
case FLOAT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualFloatMethod(env, localRef, envCJ.GetObjectClass(env, localRef), method.id)
Java_CFFI_JavaEntity(JFLOAT, jResult)
}
case INT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualIntMethod(env, localRef, envCJ.GetObjectClass(env, localRef), method.id)
Java_CFFI_JavaEntity(JINT, jResult)
}
case LONG =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualLongMethod(env, localRef, envCJ.GetObjectClass(env, localRef), method.id)
Java_CFFI_JavaEntity(JLONG, jResult)
}
case SHORT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualShortMethod(env, localRef, envCJ.GetObjectClass(env, localRef), method.id)
Java_CFFI_JavaEntity(JSHORT, jResult)
}
case BOOLEAN =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualBooleanMethod(env, localRef, envCJ.GetObjectClass(env, localRef), method.id)
Java_CFFI_JavaEntity(JBOOLEAN, jResult)
}
case VOID =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.CallNonvirtualVoidMethod(env, localRef, envCJ.GetObjectClass(env, localRef), method.id)
}
Java_CFFI_JavaEntity()
}
case OBJECT(object) =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualObjectMethod(env, localRef, envCJ.GetObjectClass(env, localRef), method.id)
Java_CFFI_JavaEntity(JOBJECT, jResult)
}
}
}
private unsafe func withParamsCallMethod(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, method: Java_CFFI_MethodID, signature: Java_CFFI_MethodSignature, argsCallNest: Java_CFFI_JavaCallNest): Java_CFFI_JavaEntity {
match (signature.ret) {
case PRIMITIVE(base) => match (base) {
case BYTE =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualByteMethodA(env, localRef, envCJ.GetObjectClass(env, localRef), method.id, argsPtr)
Java_CFFI_JavaEntity(JBYTE, jResult)
}
}
case CHAR =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualCharMethodA(env, localRef, envCJ.GetObjectClass(env, localRef), method.id, argsPtr)
Java_CFFI_JavaEntity(JCHAR, jResult)
}
}
case DOUBLE =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualDoubleMethodA(env, localRef, envCJ.GetObjectClass(env, localRef), method.id, argsPtr)
Java_CFFI_JavaEntity(JDOUBLE, jResult)
}
}
case FLOAT =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualFloatMethodA(env, localRef, envCJ.GetObjectClass(env, localRef), method.id, argsPtr)
Java_CFFI_JavaEntity(JFLOAT, jResult)
}
}
case INT =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualIntMethodA(env, localRef, envCJ.GetObjectClass(env, localRef), method.id, argsPtr)
Java_CFFI_JavaEntity(JINT, jResult)
}
}
case LONG =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualLongMethodA(env, localRef, envCJ.GetObjectClass(env, localRef), method.id, argsPtr)
Java_CFFI_JavaEntity(JLONG, jResult)
}
}
case SHORT =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualShortMethodA(env, localRef, envCJ.GetObjectClass(env, localRef), method.id, argsPtr)
Java_CFFI_JavaEntity(JSHORT, jResult)
}
}
case BOOLEAN =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualBooleanMethodA(env, localRef, envCJ.GetObjectClass(env, localRef), method.id, argsPtr)
Java_CFFI_JavaEntity(JBOOLEAN, jResult)
}
}
case VOID =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
envCJ.CallNonvirtualVoidMethodA(env, localRef, envCJ.GetObjectClass(env, localRef), method.id, argsPtr)
}
}
Java_CFFI_JavaEntity()
}
case OBJECT(object) =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallNonvirtualObjectMethodA(env, localRef, envCJ.GetObjectClass(env, localRef), method.id, argsPtr)
Java_CFFI_JavaEntity(JOBJECT, jResult)
}
}
}
}
protected func Java_CFFI_callMethod(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, method: Java_CFFI_MethodID, args: Array<Java_CFFI_JavaEntity>, argsCallNest: Java_CFFI_JavaCallNest): Java_CFFI_JavaEntity {
if (args.size > argsCallNest.array.size) {
throw InteropLibException("Cannot perform call, too small argsCallNest.array (${argsCallNest.size}, required at least ${args.size})")
}
if (method.`static`) {
throw InteropLibException("Cannot perform non-static call to static entity")
}
let signature = match(method.signature) {
case METHOD(methodSignature) => methodSignature
case FIELD(_) => throw Java_CFFI_InvariantFailed()
}
if (args.size != signature.params.size) {
throw InteropLibException("Given ${args.size} arguments for ${signature.params.size} arguments method")
}
let res = unsafe {
if (signature.params.size == 0) {
noParamsCallMethod(env, obj, method, signature)
} else {
for (i in 0..args.size) {
argsCallNest.array[i] = args[i].asJValue()
}
withParamsCallMethod(env, obj, method, signature, argsCallNest)
}
}
handlePendingException(env)
res
}
protected func Java_CFFI_callVirtualMethod(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, method: Java_CFFI_MethodID, args: Array<Java_CFFI_JavaEntity>, argsCallNest: Java_CFFI_JavaCallNest): Java_CFFI_JavaEntity {
let res = Java_CFFI_callVirtualMethod_no_exception_handling(env, obj, method, args, argsCallNest)
handlePendingException(env)
res
}
private func Java_CFFI_callVirtualMethod_no_exception_handling(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, method: Java_CFFI_MethodID, args: Array<Java_CFFI_JavaEntity>, argsCallNest: Java_CFFI_JavaCallNest): Java_CFFI_JavaEntity {
if (args.size > argsCallNest.array.size) {
throw InteropLibException("Cannot perform call, too small argsCallNest.array (${argsCallNest.size}, required at least ${args.size})")
}
if (method.`static`) {
throw InteropLibException("Cannot perform non-static call to static entity")
}
let signature = match(method.signature) {
case METHOD(methodSignature) => methodSignature
case FIELD(_) => throw Java_CFFI_InvariantFailed()
}
if (args.size != signature.params.size) {
throw InteropLibException("Given ${args.size} arguments for ${signature.params.size} arguments method")
}
unsafe {
if (signature.params.size == 0) {
match (signature.ret) {
case PRIMITIVE(base) => match (base) {
case BYTE =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallByteMethod(env, localRef, method.id)
Java_CFFI_JavaEntity(JBYTE, jResult)
}
case CHAR =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallCharMethod(env, localRef, method.id)
Java_CFFI_JavaEntity(JCHAR, jResult)
}
case DOUBLE =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallDoubleMethod(env, localRef, method.id)
Java_CFFI_JavaEntity(JDOUBLE, jResult)
}
case FLOAT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallFloatMethod(env, localRef, method.id)
Java_CFFI_JavaEntity(JFLOAT, jResult)
}
case INT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallIntMethod(env, localRef, method.id)
Java_CFFI_JavaEntity(JINT, jResult)
}
case LONG =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallLongMethod(env, localRef, method.id)
Java_CFFI_JavaEntity(JLONG, jResult)
}
case SHORT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallShortMethod(env, localRef, method.id)
Java_CFFI_JavaEntity(JSHORT, jResult)
}
case BOOLEAN =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallBooleanMethod(env, localRef, method.id)
Java_CFFI_JavaEntity(JBOOLEAN, jResult)
}
case VOID =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.CallVoidMethod(env, localRef, method.id)
}
Java_CFFI_JavaEntity()
}
case OBJECT(object) =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.CallObjectMethod(env, localRef, method.id)
Java_CFFI_JavaEntity(JOBJECT, jResult)
}
}
} else {
for (i in 0..args.size) {
argsCallNest.array[i] = args[i].asJValue()
}
match (signature.ret) {
case PRIMITIVE(base) => match (base) {
case BYTE =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallByteMethodA(env, localRef, method.id, argsPtr)
Java_CFFI_JavaEntity(JBYTE, jResult)
}
}
case CHAR =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallCharMethodA(env, localRef, method.id, argsPtr)
Java_CFFI_JavaEntity(JCHAR, jResult)
}
}
case DOUBLE =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallDoubleMethodA(env, localRef, method.id, argsPtr)
Java_CFFI_JavaEntity(JDOUBLE, jResult)
}
}
case FLOAT =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallFloatMethodA(env, localRef, method.id, argsPtr)
Java_CFFI_JavaEntity(JFLOAT, jResult)
}
}
case INT =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallIntMethodA(env, localRef, method.id, argsPtr)
Java_CFFI_JavaEntity(JINT, jResult)
}
}
case LONG =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallLongMethodA(env, localRef, method.id, argsPtr)
Java_CFFI_JavaEntity(JLONG, jResult)
}
}
case SHORT =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallShortMethodA(env, localRef, method.id, argsPtr)
Java_CFFI_JavaEntity(JSHORT, jResult)
}
}
case BOOLEAN =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallBooleanMethodA(env, localRef, method.id, argsPtr)
Java_CFFI_JavaEntity(JBOOLEAN, jResult)
}
}
case VOID =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
envCJ.CallVoidMethodA(env, localRef, method.id, argsPtr)
}
}
Java_CFFI_JavaEntity()
}
case OBJECT(object) =>
withLocalRef(env, obj.asJObject()) { localRef =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallObjectMethodA(env, localRef, method.id, argsPtr)
Java_CFFI_JavaEntity(JOBJECT, jResult)
}
}
}
}
}
}
private func Java_CFFI_callStaticMethod(env: JNIEnv_ptr, clazz: Java_CFFI_Class, methodName: String, methodSignature: String, args: Array<Java_CFFI_JavaEntity>): Java_CFFI_JavaEntity {
let signature = Java_CFFI_parseMethodSignature(methodSignature)
let id = Java_CFFI_MethodID.constrStatic(env, clazz, methodName, signature)
let callNest = Java_CFFI_JavaCallNest(args.size)
Java_CFFI_callStaticMethod(env, clazz, id, args, callNest)
}
protected func Java_CFFI_callStaticMethod(env: JNIEnv_ptr, clazz: Java_CFFI_Class, method: Java_CFFI_MethodID, args: Array<Java_CFFI_JavaEntity>, argsCallNest: Java_CFFI_JavaCallNest): Java_CFFI_JavaEntity {
if (args.size > argsCallNest.array.size) {
throw InteropLibException("Cannot perform call, too small argsCallNest.array (${argsCallNest.size}, required at least ${args.size})")
}
if (!method.`static`) {
throw InteropLibException("Cannot perform static call to non-static entity")
}
let signature = match(method.signature) {
case METHOD(methodSignature) => methodSignature
case FIELD(_) => throw Java_CFFI_InvariantFailed()
}
if (args.size != signature.params.size) {
throw InteropLibException("Given ${args.size} arguments for ${signature.params.size} arguments method")
}
let res = unsafe {
if (signature.params.size == 0) {
match (signature.ret) {
case PRIMITIVE(base) => match (base) {
case BYTE =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticByteMethod(env, clazz.obj, method.id)
Java_CFFI_JavaEntity(JBYTE, jResult)
case CHAR =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticCharMethod(env, clazz.obj, method.id)
Java_CFFI_JavaEntity(JCHAR, jResult)
case DOUBLE =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticDoubleMethod(env, clazz.obj, method.id)
Java_CFFI_JavaEntity(JDOUBLE, jResult)
case FLOAT =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticFloatMethod(env, clazz.obj, method.id)
Java_CFFI_JavaEntity(JFLOAT, jResult)
case INT =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticIntMethod(env, clazz.obj, method.id)
Java_CFFI_JavaEntity(JINT, jResult)
case LONG =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticLongMethod(env, clazz.obj, method.id)
Java_CFFI_JavaEntity(JLONG, jResult)
case SHORT =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticShortMethod(env, clazz.obj, method.id)
Java_CFFI_JavaEntity(JSHORT, jResult)
case BOOLEAN =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticBooleanMethod(env, clazz.obj, method.id)
Java_CFFI_JavaEntity(JBOOLEAN, jResult)
case VOID =>
let envCJ = env.read().read()
envCJ.CallStaticVoidMethod(env, clazz.obj, method.id)
Java_CFFI_JavaEntity()
}
case OBJECT(object) =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticObjectMethod(env, clazz.obj, method.id)
Java_CFFI_JavaEntity(JOBJECT, jResult)
}
} else {
for (i in 0..args.size) {
argsCallNest.array[i] = args[i].asJValue()
}
match (signature.ret) {
case PRIMITIVE(base) => match (base) {
case BYTE =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticByteMethodA(env, clazz.obj, method.id, argsPtr)
Java_CFFI_JavaEntity(JBYTE, jResult)
}
case CHAR =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticCharMethodA(env, clazz.obj, method.id, argsPtr)
Java_CFFI_JavaEntity(JCHAR, jResult)
}
case DOUBLE =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticDoubleMethodA(env, clazz.obj, method.id, argsPtr)
Java_CFFI_JavaEntity(JDOUBLE, jResult)
}
case FLOAT =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticFloatMethodA(env, clazz.obj, method.id, argsPtr)
Java_CFFI_JavaEntity(JFLOAT, jResult)
}
case INT =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticIntMethodA(env, clazz.obj, method.id, argsPtr)
Java_CFFI_JavaEntity(JINT, jResult)
}
case LONG =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticLongMethodA(env, clazz.obj, method.id, argsPtr)
Java_CFFI_JavaEntity(JLONG, jResult)
}
case SHORT =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticShortMethodA(env, clazz.obj, method.id, argsPtr)
Java_CFFI_JavaEntity(JSHORT, jResult)
}
case BOOLEAN =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticBooleanMethodA(env, clazz.obj, method.id, argsPtr)
Java_CFFI_JavaEntity(JBOOLEAN, jResult)
}
case VOID =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
envCJ.CallStaticVoidMethodA(env, clazz.obj, method.id, argsPtr)
}
Java_CFFI_JavaEntity()
}
case OBJECT(object) =>
withPinnedArray(argsCallNest.array) { argsPtr =>
let envCJ = env.read().read()
let jResult = envCJ.CallStaticObjectMethodA(env, clazz.obj, method.id, argsPtr)
Java_CFFI_JavaEntity(JOBJECT, jResult)
}
}
}
}
handlePendingException(env)
res
}
private unsafe func getPrimitiveFieldJavaEntity(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity,
base: Java_CFFI_BaseSignature, id: jfieldID): Java_CFFI_JavaEntity {
match (base) {
case BYTE =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.GetByteField(env, localRef, id)
Java_CFFI_JavaEntity(JBYTE, jResult)
}
case CHAR =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.GetCharField(env, localRef, id)
Java_CFFI_JavaEntity(JCHAR, jResult)
}
case DOUBLE =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.GetDoubleField(env, localRef, id)
Java_CFFI_JavaEntity(JDOUBLE, jResult)
}
case FLOAT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.GetFloatField(env, localRef, id)
Java_CFFI_JavaEntity(JFLOAT, jResult)
}
case INT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.GetIntField(env, localRef, id)
Java_CFFI_JavaEntity(JINT, jResult)
}
case LONG =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.GetLongField(env, localRef, id)
Java_CFFI_JavaEntity(JLONG, jResult)
}
case SHORT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.GetShortField(env, localRef, id)
Java_CFFI_JavaEntity(JSHORT, jResult)
}
case BOOLEAN =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.GetBooleanField(env, localRef, id)
Java_CFFI_JavaEntity(JBOOLEAN, jResult)
}
case VOID => throw Java_CFFI_InvariantFailed()
}
}
private unsafe func getFieldJavaEntity(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity,
signature: Java_CFFI_ComponentSignature, id: jfieldID): Java_CFFI_JavaEntity {
match (signature) {
case PRIMITIVE(base) => getPrimitiveFieldJavaEntity(env, obj, base, id)
case OBJECT(object) =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
let jResult = envCJ.GetObjectField(env, localRef, id)
Java_CFFI_JavaEntity(JOBJECT, jResult)
}
}
}
protected func Java_CFFI_getField(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, field: Java_CFFI_FieldID): Java_CFFI_JavaEntity {
if (field.`static`) {
throw InteropLibException("Cannot get static entity from non-static obj")
}
let signature = match(field.signature) {
case METHOD(_) => throw Java_CFFI_InvariantFailed()
case FIELD(fieldSignature) => fieldSignature
}
let res = unsafe { getFieldJavaEntity(env, obj, signature, field.id) }
handlePendingException(env)
res
}
protected func Java_CFFI_getStaticField(env: JNIEnv_ptr, clazz: Java_CFFI_Class, field: Java_CFFI_FieldID): Java_CFFI_JavaEntity {
if (!field.`static`) {
throw InteropLibException("Cannot get non-static entity from class")
}
let signature = match(field.signature) {
case METHOD(_) => throw Java_CFFI_InvariantFailed()
case FIELD(fieldSignature) => fieldSignature
}
let res = unsafe {
match (signature) {
case PRIMITIVE(base) => match (base) {
case BYTE =>
let envCJ = env.read().read()
let jResult = envCJ.GetStaticByteField(env, clazz.obj, field.id)
Java_CFFI_JavaEntity(JBYTE, jResult)
case CHAR =>
let envCJ = env.read().read()
let jResult = envCJ.GetStaticCharField(env, clazz.obj, field.id)
Java_CFFI_JavaEntity(JCHAR, jResult)
case DOUBLE =>
let envCJ = env.read().read()
let jResult = envCJ.GetStaticDoubleField(env, clazz.obj, field.id)
Java_CFFI_JavaEntity(JDOUBLE, jResult)
case FLOAT =>
let envCJ = env.read().read()
let jResult = envCJ.GetStaticFloatField(env, clazz.obj, field.id)
Java_CFFI_JavaEntity(JFLOAT, jResult)
case INT =>
let envCJ = env.read().read()
let jResult = envCJ.GetStaticIntField(env, clazz.obj, field.id)
Java_CFFI_JavaEntity(JINT, jResult)
case LONG =>
let envCJ = env.read().read()
let jResult = envCJ.GetStaticLongField(env, clazz.obj, field.id)
Java_CFFI_JavaEntity(JLONG, jResult)
case SHORT =>
let envCJ = env.read().read()
let jResult = envCJ.GetStaticShortField(env, clazz.obj, field.id)
Java_CFFI_JavaEntity(JSHORT, jResult)
case BOOLEAN =>
let envCJ = env.read().read()
let jResult = envCJ.GetStaticBooleanField(env, clazz.obj, field.id)
Java_CFFI_JavaEntity(JBOOLEAN, jResult)
case VOID => throw Java_CFFI_InvariantFailed()
}
case OBJECT(object) =>
let envCJ = env.read().read()
let jResult = envCJ.GetStaticObjectField(env, clazz.obj, field.id)
Java_CFFI_JavaEntity(JOBJECT, jResult)
}
}
handlePendingException(env)
res
}
private func Java_CFFI_setField(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, fieldName: String, fieldSignature: String, value: Java_CFFI_JavaEntity): Unit {
let clazz = Java_CFFI_Class(unsafe { obj.asJObject() }, "<UNNAMED>")
let signature = Java_CFFI_parseComponentSignature(fieldSignature)
let id = Java_CFFI_FieldID.constr(env, clazz, fieldName, signature)
Java_CFFI_setField(env, obj, id, value)
}
private unsafe func setPrimitiveFieldJavaEntity(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity,
base: Java_CFFI_BaseSignature, id: jfieldID, value: Java_CFFI_JavaEntity): Unit {
match (base) {
case BYTE =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.SetByteField(env, localRef, id, value.asJByte())
}
case CHAR =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.SetCharField(env, localRef, id, value.asJChar())
}
case DOUBLE =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.SetDoubleField(env, localRef, id, value.asJDouble())
}
case FLOAT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.SetFloatField(env, localRef, id, value.asJFloat())
}
case INT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.SetIntField(env, localRef, id, value.asJInt())
}
case LONG =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.SetLongField(env, localRef, id, value.asJLong())
}
case SHORT =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.SetShortField(env, localRef, id, value.asJShort())
}
case BOOLEAN =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.SetBooleanField(env, localRef, id, value.asJBoolean())
}
case VOID => throw Java_CFFI_InvariantFailed()
}
}
private unsafe func setFieldJavaEntity(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity,
signature: Java_CFFI_ComponentSignature, id: jfieldID, value: Java_CFFI_JavaEntity): Unit {
match (signature) {
case PRIMITIVE(base) => setPrimitiveFieldJavaEntity(env, obj, base, id, value)
case OBJECT(object) =>
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.SetObjectField(env, localRef, id, value.asJObject())
}
}
}
protected func Java_CFFI_setField(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, field: Java_CFFI_FieldID, value: Java_CFFI_JavaEntity): Unit {
if (field.`static`) {
throw InteropLibException("Cannot set static entity to non-static obj")
}
let signature = match(field.signature) {
case METHOD(_) => throw Java_CFFI_InvariantFailed()
case FIELD(fieldSignature) => fieldSignature
}
unsafe { setFieldJavaEntity(env, obj, signature, field.id, value)}
handlePendingException(env)
}
protected func Java_CFFI_setStaticField(env: JNIEnv_ptr, clazz: Java_CFFI_Class, field: Java_CFFI_FieldID, value: Java_CFFI_JavaEntity): Unit {
if (!field.`static`) {
throw InteropLibException("Cannot set non-static entity to class")
}
let signature = match(field.signature) {
case METHOD(_) => throw Java_CFFI_InvariantFailed()
case FIELD(fieldSignature) => fieldSignature
}
unsafe {
match (signature) {
case PRIMITIVE(base) => match (base) {
case BYTE =>
let envCJ = env.read().read()
envCJ.SetStaticByteField(env, clazz.obj, field.id, value.asJByte())
case CHAR =>
let envCJ = env.read().read()
envCJ.SetStaticCharField(env, clazz.obj, field.id, value.asJChar())
case DOUBLE =>
let envCJ = env.read().read()
envCJ.SetStaticDoubleField(env, clazz.obj, field.id, value.asJDouble())
case FLOAT =>
let envCJ = env.read().read()
envCJ.SetStaticFloatField(env, clazz.obj, field.id, value.asJFloat())
case INT =>
let envCJ = env.read().read()
envCJ.SetStaticIntField(env, clazz.obj, field.id, value.asJInt())
case LONG =>
let envCJ = env.read().read()
envCJ.SetStaticLongField(env, clazz.obj, field.id, value.asJLong())
case SHORT =>
let envCJ = env.read().read()
envCJ.SetStaticShortField(env, clazz.obj, field.id, value.asJShort())
case BOOLEAN =>
let envCJ = env.read().read()
envCJ.SetStaticBooleanField(env, clazz.obj, field.id, value.asJBoolean())
case VOID => throw Java_CFFI_InvariantFailed()
}
case OBJECT(object) =>
let envCJ = env.read().read()
envCJ.SetStaticObjectField(env, clazz.obj, field.id, value.asJObject())
}
}
handlePendingException(env)
}
protected abstract class Java_CFFI_SafeInteropPreparationMixin {
private static var JavaInteropPreparationLock = AtomicBool(false)
private static var JavaInteropPrepared = AtomicBool(false)
public static func prepareForJavaInterop(env: JNIEnv_ptr, prepareForJavaInteropImpl: (JNIEnv_ptr) -> Unit) {
if (!JavaInteropPrepared.load()) {
if (JavaInteropPreparationLock.compareAndSwap(false, true)) {
prepareForJavaInteropImpl(env)
if (JavaInteropPrepared.swap(true)) {
throw Java_CFFI_InvariantFailed()
}
if (!JavaInteropPreparationLock.swap(false)) {
throw Java_CFFI_InvariantFailed()
}
} else {
while (!JavaInteropPrepared.load()) { }
}
}
}
}
protected func Java_CFFI_JavaStringToCangjie(env: JNIEnv_ptr, str: Java_CFFI_JavaEntity): String {
unsafe {
let jStr = str.asJString()
let envCJ = env.read().read()
let length = Int64(envCJ.GetStringUTFLength(env, jStr))
if (length == 0) {
return ""
}
let chars = envCJ.GetStringUTFChars(env, jStr, CPointer<jboolean>())
try {
if (chars.isNull()) {
throw InteropLibException("Cannot retrieve Java String chars")
}
String.fromUtf8(Array<UInt8>(length, { i: Int64 => (chars.getChars() + i).read() }))
} finally {
if (!chars.isNull()) {
envCJ.ReleaseStringUTFChars(env, jStr, chars)
}
}
}
}
protected func Java_CFFI_CangjieStringToJava(env: JNIEnv_ptr, str: String): Java_CFFI_JavaEntity {
Java_CFFI_JavaEntity(JSTRING, to_jstring(env, str))
}
protected func Java_CFFI_newJavaObject(env: JNIEnv_ptr, clazz: Java_CFFI_Class, constructor: Java_CFFI_MethodID, args: Array<Java_CFFI_JavaEntity>, argsCallNest: Java_CFFI_JavaCallNest): Java_CFFI_JavaEntity {
if (args.size > argsCallNest.size) {
throw InteropLibException("Cannot perform call, too small argsCallNest (${argsCallNest.size}, required at least ${args.size})")
}
if (constructor.`static`) {
throw InteropLibException("Constructor cannot be static")
}
let signature = match(constructor.signature) {
case METHOD(constructorSignature) => constructorSignature
case FIELD(_) => throw Java_CFFI_InvariantFailed()
}
match (signature.ret) {
case PRIMITIVE(base) => match (base) {
case VOID => ()
case _ => throw InteropLibException("Java constructor return type should be void")
}
case OBJECT(_) => throw InteropLibException("Java constructor return type should be void")
}
if (args.size != signature.params.size) {
throw InteropLibException("Given ${args.size} arguments for ${signature.params.size} arguments constructor")
}
let res = if (signature.params.size == 0) {
unsafe {
let envCJ = env.read().read()
let jResult = envCJ.NewObject(env, clazz.obj, constructor.id)
Java_CFFI_JavaEntity(JOBJECT, jResult)
}
} else {
for (i in 0..args.size) {
argsCallNest.array[i] = unsafe { args[i].asJValue() }
}
let result = Box(Java_CFFI_JavaEntity())
withPinnedArray(argsCallNest.array) { argsPtr: CPointer<jvalue> =>
unsafe {
let envCJ = env.read().read()
let jResult = envCJ.NewObjectA(env, clazz.obj, constructor.id, argsPtr)
result.value = Java_CFFI_JavaEntity(JOBJECT, jResult)
}
}
result.value
}
handlePendingException(env)
res
}
// The constructor of the proxy class for structTy contains a special parameter, whereas EnumTy
// does not require it.
protected func Java_CFFI_newJavaProxyObjectForCJMapping(env: JNIEnv_ptr, entity: Object, name: String,
withMarkerParam: Bool): jobject {
let id = Java_CFFI_put_to_registry_1(entity)
let clazz = Java_CFFI_Class(env, name)
let args = ArrayQueue<Java_CFFI_JavaEntity>()
var signStr: String = "(J"
args.add((Java_CFFI_JavaEntity(JLONG, id)))
if (withMarkerParam) {
signStr += "Lcangjie/lang/internal/$$NativeConstructorMarker;"
args.add(Java_CFFI_JavaEntityJobjectNull())
}
signStr += ")V"
let signature = Java_CFFI_parseMethodSignature(signStr)
let methodId = Java_CFFI_MethodID.constr(env, clazz, "<init>", signature)
let result = Java_CFFI_newJavaObject(env, clazz, methodId, args.toArray(), Java_CFFI_JavaCallNest(args.size))
unsafe { result.asJObject() }
}
protected func Java_CFFI_newGlobalReference(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, weak: Bool): Java_CFFI_JavaEntity {
unsafe {
let envCJ = env.read().read()
if (weak) {
Java_CFFI_JavaEntity(JWEAK, envCJ.NewWeakGlobalRef(env, obj.asJObject()))
} else {
Java_CFFI_JavaEntity(JSTRONG, envCJ.NewGlobalRef(env, obj.asJObject()))
}
}
}
protected func Java_CFFI_newJavaArray(env: JNIEnv_ptr, elementType: String, size: jsize): Java_CFFI_JavaEntity {
let elementTypeSignature = Java_CFFI_parseComponentSignature(elementType)
Java_CFFI_newJavaArray(env, elementTypeSignature, size)
}
protected func Java_CFFI_newJavaArray(env: JNIEnv_ptr, elementType: Java_CFFI_ComponentSignature, size: jsize): Java_CFFI_JavaEntity {
let res = unsafe {
let envCJ = env.read().read()
match (elementType) {
case PRIMITIVE(base) => match (base) {
case BYTE => Java_CFFI_JavaEntity(JOBJECT, envCJ.NewByteArray(env, size))
case CHAR => Java_CFFI_JavaEntity(JOBJECT, envCJ.NewCharArray(env, size))
case DOUBLE => Java_CFFI_JavaEntity(JOBJECT, envCJ.NewDoubleArray(env, size))
case FLOAT => Java_CFFI_JavaEntity(JOBJECT, envCJ.NewFloatArray(env, size))
case INT => Java_CFFI_JavaEntity(JOBJECT, envCJ.NewIntArray(env, size))
case LONG => Java_CFFI_JavaEntity(JOBJECT, envCJ.NewLongArray(env, size))
case SHORT => Java_CFFI_JavaEntity(JOBJECT, envCJ.NewShortArray(env, size))
case BOOLEAN => Java_CFFI_JavaEntity(JOBJECT, envCJ.NewBooleanArray(env, size))
case VOID => throw Java_CFFI_InvariantFailed()
}
case OBJECT(object) =>
Java_CFFI_JavaEntity(JOBJECT, envCJ.NewObjectArray(env, size, Java_CFFI_Class(env, object.forJNI()).obj, jobject()))
}
}
handlePendingException(env)
res
}
protected func Java_CFFI_arrayGetLength(env: JNIEnv_ptr, array: Java_CFFI_JavaEntity): jsize {
unsafe {
let envCJ = env.read().read()
envCJ.GetArrayLength(env, array.asJObject())
}
}
protected func Java_CFFI_deleteGlobalReference(env: JNIEnv_ptr, ref: Java_CFFI_JavaEntity): Unit {
unsafe {
let envCJ = env.read().read()
match (ref.kind) {
case JWEAK => envCJ.DeleteWeakGlobalRef(env, ref.asJWeak())
case JSTRONG => envCJ.DeleteGlobalRef(env, ref.asJStrong())
case _ => throw InteropLibException("Cannot delete not global reference entity")
}
}
}
protected func Java_CFFI_arrayGet(env: JNIEnv_ptr, elementType: Java_CFFI_JavaEntityKind, array: Java_CFFI_JavaEntity, index: jint): Java_CFFI_JavaEntity {
unsafe {
let envCJ = env.read().read()
let res = match (elementType) {
case JBYTE =>
var value: jbyte = 0
envCJ.GetByteArrayRegion(env, array.asJObject(), index, 1, inout value)
Java_CFFI_JavaEntity(elementType, value)
case JCHAR =>
var value: jchar = 0
envCJ.GetCharArrayRegion(env, array.asJObject(), index, 1, inout value)
Java_CFFI_JavaEntity(elementType, value)
case JDOUBLE =>
var value: jdouble = 0.0
envCJ.GetDoubleArrayRegion(env, array.asJObject(), index, 1, inout value)
Java_CFFI_JavaEntity(elementType, value)
case JFLOAT =>
var value: jfloat = 0.0
envCJ.GetFloatArrayRegion(env, array.asJObject(), index, 1, inout value)
Java_CFFI_JavaEntity(elementType, value)
case JINT =>
var value: jint = 0
envCJ.GetIntArrayRegion(env, array.asJObject(), index, 1, inout value)
Java_CFFI_JavaEntity(elementType, value)
case JLONG =>
var value: jlong = 0
envCJ.GetLongArrayRegion(env, array.asJObject(), index, 1, inout value)
Java_CFFI_JavaEntity(elementType, value)
case JSHORT =>
var value: jshort = 0
envCJ.GetShortArrayRegion(env, array.asJObject(), index, 1, inout value)
Java_CFFI_JavaEntity(elementType, value)
case JBOOLEAN =>
var value: jboolean = 0
envCJ.GetBooleanArrayRegion(env, array.asJObject(), index, 1, inout value)
Java_CFFI_JavaEntity(elementType, value)
case JOBJECT =>
Java_CFFI_JavaEntity(elementType, envCJ.GetObjectArrayElement(env, array.asJObject(), index))
case _ => throw Java_CFFI_InvariantFailed()
}
handlePendingException(env)
res
}
}
private let cstrSelf = unsafe { LibC.mallocCString("self") }
private let cstrJ = unsafe { LibC.mallocCString("J") }
protected func Java_CFFI_putToRegistry(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, a: Any): Unit {
let id = registry.put(a)
unsafe {
let envCJ : JNINativeInterface_ = env.read().read()
let objClass = envCJ.GetObjectClass(env, obj.asJObject())
let fieldId = envCJ.GetFieldID(env, objClass, cstrSelf, cstrJ)
envCJ.SetLongField(env, obj.asJObject(), fieldId, id)
}
}
protected func Java_CFFI_arraySet(env: JNIEnv_ptr, elementType: Java_CFFI_JavaEntityKind, array: Java_CFFI_JavaEntity, index: jint, valueEntity: Java_CFFI_JavaEntity): Unit {
unsafe {
let envCJ = env.read().read()
let res = match (elementType) {
case JBYTE =>
var value: jbyte = valueEntity.asJByte()
envCJ.SetByteArrayRegion(env, array.asJObject(), index, 1, inout value)
case JCHAR =>
var value: jchar = valueEntity.asJChar()
envCJ.SetCharArrayRegion(env, array.asJObject(), index, 1, inout value)
case JDOUBLE =>
var value: jdouble = valueEntity.asJDouble()
envCJ.SetDoubleArrayRegion(env, array.asJObject(), index, 1, inout value)
case JFLOAT =>
var value: jfloat = valueEntity.asJFloat()
envCJ.SetFloatArrayRegion(env, array.asJObject(), index, 1, inout value)
case JINT =>
var value: jint = valueEntity.asJInt()
envCJ.SetIntArrayRegion(env, array.asJObject(), index, 1, inout value)
case JLONG =>
var value: jlong = valueEntity.asJLong()
envCJ.SetLongArrayRegion(env, array.asJObject(), index, 1, inout value)
case JSHORT =>
var value: jshort = valueEntity.asJShort()
envCJ.SetShortArrayRegion(env, array.asJObject(), index, 1, inout value)
case JBOOLEAN =>
var value: jboolean = valueEntity.asJBoolean()
envCJ.SetBooleanArrayRegion(env, array.asJObject(), index, 1, inout value)
case JOBJECT =>
envCJ.SetObjectArrayElement(env, array.asJObject(), index, valueEntity.asJObject())
case _ => throw Java_CFFI_InvariantFailed()
}
handlePendingException(env)
res
}
}
///////////////////////////////////////////////////////////////////////////////
//// Region OLD
private func to_jstring(env: JNIEnv_ptr, str: String): jstring {
var result = jstring()
unsafe {
let envCJ : JNINativeInterface_ = env.read().read()
try (cstr = LibC.mallocCString(str).asResource()) {
result = envCJ.NewStringUTF(env, cstr.value)
}
}
result
}
protected func Java_CFFI_get_env(): JNIEnv_ptr {
var envPtr = JNIEnv_ptr()
unsafe {
let javaVM: JNIInvokeInterface_ = jvm.read().read()
/*
Thread should be attached as daemon to prevent waiting for Cangjie threads.
Because Cangjie runtime threads are registered (for example, during objects finalization),
JVM is blocked from terminating.
A better solution is to detach Cangjie thread from JVM.
*/
javaVM.AttachCurrentThreadAsDaemon(jvm, inout envPtr, CPointer<Unit>())
}
envPtr
}
protected func Java_CFFI_JavaEntityJobject(jobject: CPointer<Unit>): Java_CFFI_JavaEntity {
Java_CFFI_JavaEntity(JOBJECT, jobject)
}
protected func Java_CFFI_JavaEntityJobjectNull(): Java_CFFI_JavaEntity {
Java_CFFI_JavaEntity(JOBJECT, CPointer<Unit>())
}
protected func Java_CFFI_put_to_registry_1(any: Any): Int64 {
registry.put(any)
}
protected func Java_CFFI_getFromRegistryByObjOption<T>(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity): ?T {
unsafe {
let envCJ : JNINativeInterface_ = env.read().read()
let jobj = obj.asJObject()
if (jobj.isNull()) {
return None
}
let objClass = envCJ.GetObjectClass(env, jobj)
let fieldId = envCJ.GetFieldID(env, objClass, cstrSelf, cstrJ)
if (fieldId.isNull()) {
return None
}
let id = envCJ.GetLongField(env, obj.asJObject(), fieldId)
Java_CFFI_getFromRegistryOption<T>(id)
}
}
protected func Java_CFFI_getFromRegistryByObj<T>(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity): T {
return Java_CFFI_getFromRegistryByObjOption<T>(env, obj) ?? throw InteropLibException("CJ object does not exist")
}
protected func Java_CFFI_getFromRegistry<T>(env: JNIEnv_ptr, self: jlong): T {
if (let Some(v) <- registry.get<T>(self)) {
v
} else {
throw InteropLibException("CJ object ${self} does not exist")
}
}
protected func Java_CFFI_getFromRegistryOption<T>(self: jlong): Option<T> {
registry.get<T>(self)
}
protected func Java_CFFI_removeFromRegistry(self: jlong): Unit {
registry.remove(self)
}
protected func Java_CFFI_deleteCJObject<T>(env: JNIEnv_ptr, self: jlong, getEntity: (T) -> Java_CFFI_JavaEntity): Unit {
let v = Java_CFFI_getFromRegistry<T>(env, self)
Java_CFFI_deleteGlobalReference(env, getEntity(v))
registry.remove(self)
}
protected func Java_CFFI_deleteCJObjectOneWay<T>(env: JNIEnv_ptr, self: jlong): Unit {
let v = Java_CFFI_getFromRegistry<T>(env, self)
registry.remove(self)
}
protected func Java_CFFI_unwrapJavaEntityAsValue<T>(entity: Java_CFFI_JavaEntity): T {
unsafe {
match (entity.kind) {
case JVOID where entity.asUnit() is T => (entity.asUnit() as T).getOrThrow()
case JVALUE | JOBJECT | JWEAK | JSTRONG where entity.asJObject() is T => (entity.asJObject() as T).getOrThrow()
case JVALUE | JLONG where entity.asJLong() is T => (entity.asJLong() as T).getOrThrow()
case JVALUE | JINT where entity.asJInt() is T => (entity.asJInt() as T).getOrThrow()
case JVALUE | JSTRING where entity.asJString() is T => (entity.asJString() as T).getOrThrow()
case JVALUE | JBOOLEAN where entity.asJBoolean().asBool() is T => (entity.asJBoolean().asBool() as T).getOrThrow()
case JVALUE | JSHORT where entity.asJShort() is T => (entity.asJShort() as T).getOrThrow()
case JVALUE | JCHAR where entity.asJChar() is T => (entity.asJChar() as T).getOrThrow()
case JVALUE | JBYTE where entity.asJByte() is T => (entity.asJByte() as T).getOrThrow()
case JVALUE | JFLOAT where entity.asJFloat() is T => (entity.asJFloat() as T).getOrThrow()
case JVALUE | JDOUBLE where entity.asJDouble() is T => (entity.asJDouble() as T).getOrThrow()
case _ => throw InteropLibException("Cannot get value of expected type from ${entity.kind.toString()}")
}
}
}
protected func Java_CFFI_ensure_not_null(entity: Java_CFFI_JavaEntity): Java_CFFI_JavaEntity {
match (entity) {
case e where e.isNull => throw NoneValueException("Unexpected `null` has come from java")
case e => e
}
}
protected func Java_CFFI_getJavaEntityOrNull(entity: Java_CFFI_JavaEntity): ?Java_CFFI_JavaEntity {
match (entity) {
case e where e.isNull => None
case e => e
}
}
protected func Java_CFFI_isInstanceOf(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity, className: String): Bool {
let clazz = Java_CFFI_Class(env, className)
unsafe {
withLocalRef(env, obj.asJObject()) { localRef =>
let envCJ = env.read().read()
envCJ.IsInstanceOf(env, localRef, clazz.obj).asBool()
}
}
}
extend jboolean {
func asBool() {
this != 0
}
}
private func Java_CFFI_create_java_exception_object(env: JNIEnv_ptr, msg: String): jobject {
unsafe {
let msgObj = [Java_CFFI_CangjieStringToJava(env, msg)]
let clazz = Java_CFFI_Class(env, "java/lang/Exception")
let signature = Java_CFFI_parseMethodSignature("(Ljava/lang/String;)V")
let id = Java_CFFI_MethodID.constr(env, clazz, "<init>", signature)
let callNest = Java_CFFI_JavaCallNest(msgObj.size)
let throwObj = Java_CFFI_newJavaObject(env, clazz, id, msgObj, callNest)
throwObj.asJObject()
}
}
/*
* The function is called to process jump from CJ world backward to Java.
* It does not throw JNIException because it is called in handler of JNIException.
*/
private func Java_CFFI_throw_exception(env: JNIEnv_ptr, exception: jthrowable) {
unsafe {
let envCJ : JNINativeInterface_ = env.read().read()
envCJ.Throw(env, exception)
}
}
private func handlePendingException(env: JNIEnv_ptr) {
unsafe {
let envCJ : JNINativeInterface_ = env.read().read()
let pendingException = envCJ.ExceptionOccurred(env)
if (!pendingException.isNull()) {
envCJ.ExceptionClear(env)
let clazz = Java_CFFI_Class(env, "java/lang/Throwable")
let signature = Java_CFFI_parseMethodSignature("()Ljava/lang/String;")
let id = Java_CFFI_MethodID.constr(env, clazz, "toString", signature)
let jMessage = Java_CFFI_callVirtualMethod_no_exception_handling(env, Java_CFFI_JavaEntity(JOBJECT, pendingException),
id, [], Java_CFFI_JavaCallNest())
let getMessageException = envCJ.ExceptionOccurred(env)
var cjMessage: String
if (getMessageException.isNull()) {
cjMessage = Java_CFFI_JavaStringToCangjie(env, jMessage)
} else {
cjMessage = "<Java Throwable.getMessage() has thrown an exception>"
envCJ.ExceptionClear(env)
}
throw JNIException(cjMessage, pendingException)
}
}
}
private func Java_CFFI_wrap_cj_exception(env: JNIEnv_ptr, ex: Exception): JNIException {
let throwObj = Java_CFFI_create_java_exception_object(env, ex.message)
JNIException(ex.message, throwObj)
}
protected func withExceptionHandling<R>(env: JNIEnv_ptr, action: () -> R): R where R <: CType {
try {
action()
} catch (ex: JNIException) {
Java_CFFI_throw_exception(env, ex.jException)
unsafe { zeroValue<R>() }
} catch (ex: Exception) { // TODO: handle Cangjie Error type?
let wrapped = Java_CFFI_wrap_cj_exception(env, ex)
Java_CFFI_throw_exception(env, wrapped.jException)
unsafe { zeroValue<R>() }
}
}
protected func Java_CFFI_MethodIDConstr(env: JNIEnv_ptr, clazz: Java_CFFI_Class, methodName: String, signature: Java_CFFI_MethodSignature): Java_CFFI_MethodID {
Java_CFFI_MethodID.constr(env, clazz, methodName, signature)
}
protected func Java_CFFI_MethodIDConstrStatic(env: JNIEnv_ptr, clazz: Java_CFFI_Class, methodName: String, signature: Java_CFFI_MethodSignature): Java_CFFI_MethodID {
Java_CFFI_MethodID.constrStatic(env, clazz, methodName, signature)
}
protected func Java_CFFI_FieldIDConstr(env: JNIEnv_ptr, clazz: Java_CFFI_Class, fieldName: String, signature: Java_CFFI_ComponentSignature): Java_CFFI_FieldID {
Java_CFFI_FieldID.constr(env, clazz, fieldName, signature)
}
protected func Java_CFFI_FieldIDConstrStatic(env: JNIEnv_ptr, clazz: Java_CFFI_Class, fieldName: String, signature: Java_CFFI_ComponentSignature): Java_CFFI_FieldID {
Java_CFFI_FieldID.constrStatic(env, clazz, fieldName, signature)
}
protected func Java_CFFI_ClassInit(env: JNIEnv_ptr, name: String) : Java_CFFI_Class {
Java_CFFI_Class(env, name)
}
protected func Java_CFFI_JavaCallNestInit(n: Int64) : Java_CFFI_JavaCallNest {
Java_CFFI_JavaCallNest(n)
}
protected func Java_CFFI_monitorEnter(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity): Unit {
unsafe {
let envCJ = env.read().read()
let jobj = obj.asJObject()
let result = envCJ.MonitorEnter(env, jobj)
if (result != 0) {
throw InteropLibException("Error entering monitor: ${result}")
}
}
}
protected func Java_CFFI_monitorExit(env: JNIEnv_ptr, obj: Java_CFFI_JavaEntity): Unit {
unsafe {
let envCJ = env.read().read()
let jobj = obj.asJObject()
let result = envCJ.MonitorExit(env, jobj)
if (result != 0) {
throw InteropLibException("Error entering monitor: ${result}")
}
}
}
protected func Java_CFFI_withLocalFrame(env: JNIEnv_ptr, capacity: jint, action: () -> Java_CFFI_JavaEntity): Java_CFFI_JavaEntity {
unsafe {
let envCJ = env.read().read()
let status = envCJ.PushLocalFrame(env, capacity)
if (status != 0) {
throw InteropLibException("Error entering local frame: ${status}")
}
var result = Java_CFFI_JavaEntityJobjectNull()
try {
result = action()
} finally {
result = Java_CFFI_JavaEntity(JOBJECT, envCJ.PopLocalFrame(env, result.asJObject()))
}
return result
}
}
protected func Java_CFFI_withLocalFrameUnit(env: JNIEnv_ptr, capacity: jint, action: () -> Unit): Unit {
unsafe {
let envCJ = env.read().read()
let status = envCJ.PushLocalFrame(env, capacity)
if (status != 0) {
throw InteropLibException("Error entering local frame: ${status}")
}
try {
action()
} finally {
envCJ.PopLocalFrame(env, jobject())
}
}
}
protected func getJavaLambdaObject(fun : Any, className: String) : jobject {
unsafe {
getJavaLambdaEntity(fun, className).asJObject()
}
}
protected func getJavaLambdaEntity(fun : Any, className: String) : Java_CFFI_JavaEntity {
let cjId = Java_CFFI_put_to_registry_1(fun)
let env = Java_CFFI_get_env()
let clazz = Java_CFFI_ClassInit(env, className)
let methodId = Java_CFFI_MethodIDConstr(env, clazz, "<init>", Java_CFFI_parseMethodSignature("(J)V"));
let obj = Java_CFFI_newJavaObject(env, clazz, methodId, [Java_CFFI_JavaEntity(cjId)], Java_CFFI_JavaCallNest(1))
obj
}
protected class JavaObjectController<T> {
var obj: Java_CFFI_JavaEntity
var cjObj: ?T = None
var detached = false
var skipDetach = false
var cjLock: ?Java_CFFI_JavaEntity = None
var waitId: ?Java_CFFI_MethodID = None
var attachCJObjectId: ?Java_CFFI_MethodID = None
public init(obj: Java_CFFI_JavaEntity, className: String) {
this.obj = obj
let env = Java_CFFI_get_env()
let clazz = Java_CFFI_Class(env, className)
let cjLockFieldId = Java_CFFI_FieldID.constrStatic(env, clazz, "cjLock", Java_CFFI_parseComponentSignature("Ljava/lang/Object;"))
let cjLockLocal = Java_CFFI_getStaticField(env, clazz, cjLockFieldId)
cjLock = Java_CFFI_newGlobalReference(env, cjLockLocal, false)
let objectClass = Java_CFFI_Class(env, "java/lang/Object")
waitId = Java_CFFI_MethodID.constr(env, objectClass, "wait", Java_CFFI_parseMethodSignature("()V"))
attachCJObjectId = Java_CFFI_MethodID.constr(env, clazz, "attachCJObject", Java_CFFI_parseMethodSignature("(J)V"))
}
private func waitAndGetJavaObject(env: JNIEnv_ptr): Java_CFFI_JavaEntity {
var ref = getLocalRef(env, obj)
while (ref.isNull) {
skipDetach = true
Java_CFFI_callVirtualMethod(env, cjLock.getOrThrow(), waitId.getOrThrow(), [], Java_CFFI_JavaCallNest(0))
ref = getLocalRef(env, obj)
}
ref
}
public func attachCJObject(env: JNIEnv_ptr): Java_CFFI_JavaEntity {
acquireCJLock(env)
try {
let localRef = waitAndGetJavaObject(env)
if (detached) {
println("Attaching CJ object")
let cjObjId = Java_CFFI_put_to_registry_1(cjObj.getOrThrow())
Java_CFFI_callMethod(env, localRef, attachCJObjectId.getOrThrow(), [Java_CFFI_JavaEntity(JLONG, cjObjId)], Java_CFFI_JavaCallNest(1))
Java_CFFI_deleteGlobalReference(env, this.obj)
this.obj = Java_CFFI_newGlobalReference(env, localRef, true)
detached = false
}
localRef
} finally {
releaseCJLock(env)
}
}
public func detachCJObject(env: JNIEnv_ptr, javaObj: Java_CFFI_JavaEntity, cjObjId: Int64): Bool {
Java_CFFI_deleteGlobalReference(env, this.obj)
if (skipDetach) {
this.obj = Java_CFFI_newGlobalReference(env, javaObj, true)
skipDetach = false
} else {
this.obj = Java_CFFI_newGlobalReference(env, javaObj, false)
cjObj = Java_CFFI_getFromRegistry<T>(env, cjObjId)
Java_CFFI_deleteCJObjectOneWay<T>(env, cjObjId)
detached = true
}
detached
}
private func acquireCJLock(env: JNIEnv_ptr) {
Java_CFFI_monitorEnter(env, cjLock.getOrThrow())
}
private func releaseCJLock(env: JNIEnv_ptr) {
Java_CFFI_monitorExit(env, cjLock.getOrThrow())
}
public func finalize(): Unit {
let env = Java_CFFI_get_env()
Java_CFFI_deleteGlobalReference(env, obj)
}
}