diff --git a/jni.cabal b/jni.cabal
--- a/jni.cabal
+++ b/jni.cabal
@@ -1,5 +1,5 @@
 name:                jni
-version:             0.5.0
+version:             0.5.1
 synopsis:            Complete JNI raw bindings.
 description:         Please see README.md.
 homepage:            https://github.com/tweag/inline-java/tree/master/jni#readme
@@ -32,16 +32,16 @@
   other-modules:
     Foreign.JNI.NativeMethod
   build-depends:
-    base >= 4.7 && < 5,
+    base >=4.7 && <5,
     bytestring >=0.10,
-    choice >= 0.2.0,
+    choice >=0.2.0,
     containers >=0.5,
     constraints >=0.8,
-    deepseq >= 1.4.2,
-    singletons >= 2.0
+    deepseq >=1.4.2,
+    singletons >=2.0
   if impl(ghc < 8.2.1)
     build-depends:
-      inline-c >=0.5.6.1
+      inline-c >=0.5.6.1 && <0.6
   else
     build-depends:
       inline-c >=0.6
diff --git a/src/Foreign/JNI.c b/src/Foreign/JNI.c
--- a/src/Foreign/JNI.c
+++ b/src/Foreign/JNI.c
@@ -0,0 +1,745 @@
+
+#include <jni.h>
+
+#include <stdio.h>
+
+#include <errno.h>
+
+#include <stdlib.h>
+
+static __thread JNIEnv* jniEnv; 
+
+jthrowable inline_c_Foreign_JNI_0_547acbc2ed14c804f8e5133e90bd68e8a83f9c40(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1) {
+return ( (*env_inline_c_0)->ExceptionOccurred(env_inline_c_1) );
+}
+
+
+void inline_c_Foreign_JNI_1_78814a28f200ba50ae6a800d1c57dc6512afa523(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1) {
+ (*env_inline_c_0)->ExceptionDescribe(env_inline_c_1) ;
+}
+
+
+void inline_c_Foreign_JNI_2_fa3d0bf0766f27928a66a4776f60d76979f8c97d(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1) {
+ (*env_inline_c_0)->ExceptionClear(env_inline_c_1) ;
+}
+
+
+jint inline_c_Foreign_JNI_3_4732b21c46e22bd0b636479445ab969eac58c90f() {
+return ( JNI_OK );
+}
+
+
+jint inline_c_Foreign_JNI_4_e4f86790d79a32d6ee762a76428f9737dcfd28e4() {
+return ( JNI_EDETACHED );
+}
+
+
+jint inline_c_Foreign_JNI_5_11ef4c1fdcde263336fdb13b08ff0f2e19d0df2f(JavaVM * jvm_inline_c_0, JavaVM * jvm_inline_c_1) {
+return (
+        (*jvm_inline_c_0)->AttachCurrentThreadAsDaemon(jvm_inline_c_1, &jniEnv, NULL)
+      );
+}
+
+
+jint inline_c_Foreign_JNI_6_ea397df40babee4a972104da3a793b58426edf8b(JavaVM * jvm_inline_c_0, JavaVM * jvm_inline_c_1) {
+
+      int rc = (*jvm_inline_c_0)->DetachCurrentThread(jvm_inline_c_1);
+      if (rc == JNI_OK)
+        jniEnv = NULL;
+      return rc;
+    
+}
+
+
+jint inline_c_Foreign_JNI_7_abd19e7710ee92af6af62dba0642a93990484215(JavaVM ** pjvm_inline_c_0, jsize * pnum_jvms_inline_c_1) {
+return (
+        JNI_GetCreatedJavaVMs(pjvm_inline_c_0, 1, pnum_jvms_inline_c_1)
+      );
+}
+
+
+JNIEnv * inline_c_Foreign_JNI_8_43fe8189512df285da66021808b0754ca40f395a() {
+return ( jniEnv );
+}
+
+
+jint inline_c_Foreign_JNI_9_8e0aa50b329d6b67b682632f070f9d76ef38dd38(JavaVM * jvm_inline_c_0, JavaVM * jvm_inline_c_1) {
+return (
+          (*jvm_inline_c_0)->GetEnv(jvm_inline_c_1, (void**)&jniEnv, JNI_VERSION_1_6)
+        );
+}
+
+
+JNIEnv * inline_c_Foreign_JNI_10_43fe8189512df285da66021808b0754ca40f395a() {
+return ( jniEnv );
+}
+
+
+JavaVM * inline_c_Foreign_JNI_11_567b31ebdc829d5f3987ca20c011e0704e8241fa(int n_inline_c_0, int n_inline_c_1, char ** coptions_inline_c_2, int n_inline_c_3) {
+
+          JavaVM *jvm;
+          JavaVMInitArgs vm_args;
+          JavaVMOption *options = malloc(sizeof(JavaVMOption) * n_inline_c_0);
+          for(int i = 0; i < n_inline_c_1; i++)
+                  options[i].optionString = coptions_inline_c_2[i];
+          vm_args.version = JNI_VERSION_1_6;
+          vm_args.nOptions = n_inline_c_3;
+          vm_args.options = options;
+          vm_args.ignoreUnrecognized = 0;
+          JNI_CreateJavaVM(&jvm, (void**)&jniEnv, &vm_args);
+          free(options);
+          return jvm; 
+}
+
+
+void inline_c_Foreign_JNI_12_1292fdfa6a16275159c7a7a90ac6ee443f45ad6c(JavaVM * jvm_inline_c_0, JavaVM * jvm_inline_c_1) {
+
+        (*jvm_inline_c_0)->DestroyJavaVM(jvm_inline_c_1);
+        jniEnv = NULL;
+    
+}
+
+
+jclass inline_c_Foreign_JNI_13_8c440e181516a3630ffa1c69f19cdd49c5ea5146(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, char * namep_inline_c_2, jobject loader_inline_c_3, char * buf_inline_c_4, long buf_inline_c_5) {
+return (
+      (*env_inline_c_0)->DefineClass(env_inline_c_1,
+                                     namep_inline_c_2,
+                                     loader_inline_c_3,
+                                     buf_inline_c_4,
+                                     buf_inline_c_5) );
+}
+
+
+jint inline_c_Foreign_JNI_14_b2b4b4a025c54c183ac34fda20ed77aaa1cbd6ed(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, JNINativeMethod * cmethods_inline_c_3, int numMethods_inline_c_4) {
+return (
+             (*env_inline_c_0)->RegisterNatives(env_inline_c_1,
+                                                cls_inline_c_2,
+                                                cmethods_inline_c_3,
+                                                numMethods_inline_c_4) );
+}
+
+
+jint inline_c_Foreign_JNI_15_7da57142503fa6323136b7c159710e69a3507ecd(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2) {
+return (
+       (*env_inline_c_0)->Throw(env_inline_c_1,
+                                obj_inline_c_2) );
+}
+
+
+jint inline_c_Foreign_JNI_16_28bfcee12881af1c4a1c7de9b19c8263659c32d9(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, char * msgp_inline_c_3) {
+return (
+       (*env_inline_c_0)->ThrowNew(env_inline_c_1,
+                                   cls_inline_c_2,
+                                   msgp_inline_c_3) );
+}
+
+
+jclass inline_c_Foreign_JNI_17_98f2efdfb85a70a64012d4a321b2e1b53ec6d783(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, char * namep_inline_c_2) {
+return ( (*env_inline_c_0)->FindClass(env_inline_c_1, namep_inline_c_2) );
+}
+
+
+jobject inline_c_Foreign_JNI_18_db79f2d25dd672f2593a68b078850f23bf59b54d(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID constr_inline_c_3, jvalue * cargs_inline_c_4) {
+return (
+        (*env_inline_c_0)->NewObjectA(env_inline_c_1,
+                                      cls_inline_c_2,
+                                      constr_inline_c_3,
+                                      cargs_inline_c_4) );
+}
+
+
+jfieldID inline_c_Foreign_JNI_19_24f9a5b843f700dd367457745083b0f737727165(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, char * fieldnamep_inline_c_3, char * sigp_inline_c_4) {
+return (
+      (*env_inline_c_0)->GetFieldID(env_inline_c_1,
+                                    cls_inline_c_2,
+                                    fieldnamep_inline_c_3,
+                                    sigp_inline_c_4) );
+}
+
+
+jfieldID inline_c_Foreign_JNI_20_7f51c9e3dfc10d19fa55f2bad6ee5d30c55df36e(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, char * fieldnamep_inline_c_3, char * sigp_inline_c_4) {
+return (
+      (*env_inline_c_0)->GetStaticFieldID(env_inline_c_1,
+                                          cls_inline_c_2,
+                                          fieldnamep_inline_c_3,
+                                          sigp_inline_c_4) );
+}
+
+
+jobject inline_c_Foreign_JNI_21_d3bbd4f49ae66a171a74a554b1ad29de1e653fbe(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetObjectField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3) );
+}
+
+
+jboolean inline_c_Foreign_JNI_22_a6c318bded4eb76e31688bb79797fe16d6ad53cd(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetBooleanField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3) );
+}
+
+
+jbyte inline_c_Foreign_JNI_23_ce49e2a35bd9168e5441c414c23484ab5ed3bfd1(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetByteField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3) );
+}
+
+
+jchar inline_c_Foreign_JNI_24_c943f6f26c7dc18be8ee2dbae90e076a32d0bc7b(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetCharField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3) );
+}
+
+
+jshort inline_c_Foreign_JNI_25_4f9206f7afb17d7f7a4ca9c25e5e6ed84e09389c(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetShortField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3) );
+}
+
+
+jint inline_c_Foreign_JNI_26_0163f5dc997937b24f9a57dbb7fac4a42db7f087(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetIntField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3) );
+}
+
+
+jlong inline_c_Foreign_JNI_27_46309075736c07065202cd83573f912c5501b005(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetLongField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3) );
+}
+
+
+jfloat inline_c_Foreign_JNI_28_2c66977fbc363fd8fb05ce6ab75fd556531d5a17(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetFloatField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3) );
+}
+
+
+jdouble inline_c_Foreign_JNI_29_272ef095b5f25c1f0f9482e390b6a782adff8c47(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetDoubleField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3) );
+}
+
+
+jobject inline_c_Foreign_JNI_30_5c71747967eb4276bca3e20b379750babbebda8e(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetStaticObjectField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3) );
+}
+
+
+jboolean inline_c_Foreign_JNI_31_b176fa5abc4a517382107db7f9cf6e9d39f42790(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetStaticBooleanField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3) );
+}
+
+
+jbyte inline_c_Foreign_JNI_32_27a626282a93b144decfac0d91b1620bd593f9b3(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetStaticByteField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3) );
+}
+
+
+jchar inline_c_Foreign_JNI_33_a04d9bdb806b0e16000ef35f7159e5dd16b3dd70(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetStaticCharField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3) );
+}
+
+
+jshort inline_c_Foreign_JNI_34_5bbf8ff1a31ec47edf1a6f51423acec6a8c165d9(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetStaticShortField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3) );
+}
+
+
+jint inline_c_Foreign_JNI_35_e412724b75f372f7be355611dc8047fb9ae1126b(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetStaticIntField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3) );
+}
+
+
+jlong inline_c_Foreign_JNI_36_89d32b3c2be6d4e4e01285d42a1845567da8d5ef(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetStaticLongField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3) );
+}
+
+
+jfloat inline_c_Foreign_JNI_37_fb5c2b1d3de9b233f712bd7c5d5e38deb356b1e9(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetStaticFloatField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3) );
+}
+
+
+jdouble inline_c_Foreign_JNI_38_492512f55c5b7ee515200d9a8a08009e7788aa22(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3) {
+return (       (*env_inline_c_0)->GetStaticDoubleField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3) );
+}
+
+
+void inline_c_Foreign_JNI_39_584abfc1ea057f29617db577aa8a7bcf87c50277(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3, jobject x_inline_c_4) {
+       (*env_inline_c_0)->SetObjectField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3,                                               x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_40_d740752f4e956bdbcfdeb49ad9c4daac79454740(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3, jboolean x_inline_c_4) {
+       (*env_inline_c_0)->SetBooleanField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3,                                               x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_41_7c7e899dc92453c6973878ad7b630dca1101ec2f(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3, jbyte x_inline_c_4) {
+       (*env_inline_c_0)->SetByteField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3,                                               x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_42_77351519d67b4f4ab49c6b1dc32a190a8f7a2524(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3, jchar x_inline_c_4) {
+       (*env_inline_c_0)->SetCharField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3,                                               x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_43_cbc8c11ddfd08b4a88953c743e6ea91feb0ac607(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3, jshort x_inline_c_4) {
+       (*env_inline_c_0)->SetShortField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3,                                               x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_44_f318d1d52db33d7bf0dc7e5c6e9e0a71484ecb5d(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3, jint x_inline_c_4) {
+       (*env_inline_c_0)->SetIntField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3,                                               x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_45_0d005c04266d114f3826b408c1b80edae3cd0fe9(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3, jlong x_inline_c_4) {
+       (*env_inline_c_0)->SetLongField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3,                                               x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_46_6a8a9ce4e7e8acae90231495db2fd8854b488d5d(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3, jfloat x_inline_c_4) {
+       (*env_inline_c_0)->SetFloatField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3,                                               x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_47_c3947546832da8a398f347c27908ee58b78f6b9d(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jfieldID field_inline_c_3, jdouble x_inline_c_4) {
+       (*env_inline_c_0)->SetDoubleField(env_inline_c_1,                                               obj_inline_c_2,                                               field_inline_c_3,                                               x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_48_b84acff3f2e26a2a2d4e3f2a7fa46cdfbc099912(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3, jobject x_inline_c_4) {
+       (*env_inline_c_0)->SetStaticObjectField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3,                                                     x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_49_a02725e5169b86b9c1c527aaf96872a055940707(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3, jboolean x_inline_c_4) {
+       (*env_inline_c_0)->SetStaticBooleanField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3,                                                     x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_50_a3232ebc0e49f3217ef3dd1bce52bbb83462aede(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3, jbyte x_inline_c_4) {
+       (*env_inline_c_0)->SetStaticByteField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3,                                                     x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_51_6e323e9e26073d2c2b53ede3444cdf73ce5b4aad(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3, jchar x_inline_c_4) {
+       (*env_inline_c_0)->SetStaticCharField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3,                                                     x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_52_f6cfa9b49f03261d0c67c68fb9595a818e3e28cb(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3, jshort x_inline_c_4) {
+       (*env_inline_c_0)->SetStaticShortField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3,                                                     x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_53_f382d7603879ddb1eca55b052c49ed4b418e2cc7(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3, jint x_inline_c_4) {
+       (*env_inline_c_0)->SetStaticIntField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3,                                                     x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_54_02c96789057bffbad5c0921c8e94f8ffc7f6a392(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3, jlong x_inline_c_4) {
+       (*env_inline_c_0)->SetStaticLongField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3,                                                     x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_55_2f1ae23c6909cb66e439ed94bb49a5b5b117268c(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3, jfloat x_inline_c_4) {
+       (*env_inline_c_0)->SetStaticFloatField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3,                                                     x_inline_c_4); 
+}
+
+
+void inline_c_Foreign_JNI_56_3f17c8ae6483640141674f2ae41409e830a60690(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass klass_inline_c_2, jfieldID field_inline_c_3, jdouble x_inline_c_4) {
+       (*env_inline_c_0)->SetStaticDoubleField(env_inline_c_1,                                                     klass_inline_c_2,                                                     field_inline_c_3,                                                     x_inline_c_4); 
+}
+
+
+jmethodID inline_c_Foreign_JNI_57_e722a7a0d6891db8a6cda0ec21cc6312aaeec3c0(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, char * methodnamep_inline_c_3, char * sigp_inline_c_4) {
+return (
+      (*env_inline_c_0)->GetMethodID(env_inline_c_1,
+                                     cls_inline_c_2,
+                                     methodnamep_inline_c_3,
+                                     sigp_inline_c_4) );
+}
+
+
+jmethodID inline_c_Foreign_JNI_58_196e9055f6891bfa3cf856c108612c9572ecc877(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, char * methodnamep_inline_c_3, char * sigp_inline_c_4) {
+return (
+      (*env_inline_c_0)->GetStaticMethodID(env_inline_c_1,
+                                           cls_inline_c_2,
+                                           methodnamep_inline_c_3,
+                                           sigp_inline_c_4) );
+}
+
+
+jclass inline_c_Foreign_JNI_59_9f847198f949d76076adbd2e63e17b2ed0e73c41(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2) {
+return (
+      (*env_inline_c_0)->GetObjectClass(env_inline_c_1,
+                                        obj_inline_c_2) );
+}
+
+
+jobject inline_c_Foreign_JNI_60_16595ba5c6f78926bf8bd31d9d0e8bf072131bb0(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2) {
+return (
+        (*env_inline_c_0)->NewGlobalRef(env_inline_c_1,
+                                        obj_inline_c_2) );
+}
+
+
+jobject inline_c_Foreign_JNI_61_16595ba5c6f78926bf8bd31d9d0e8bf072131bb0(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2) {
+return (
+        (*env_inline_c_0)->NewGlobalRef(env_inline_c_1,
+                                        obj_inline_c_2) );
+}
+
+
+void inline_c_Foreign_JNI_62_f542a39c35a9eb0c083a4e59c4f21de5ad94e141(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2) {
+ (*env_inline_c_0)->DeleteGlobalRef(env_inline_c_1
+                                                            ,obj_inline_c_2);
+                        
+}
+
+
+jobject inline_c_Foreign_JNI_63_286b821e24496c30618bcd36d10e07939424a96e(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2) {
+return (
+      (*env_inline_c_0)->NewLocalRef(env_inline_c_1,
+                                     obj_inline_c_2) );
+}
+
+
+void inline_c_Foreign_JNI_64_f46c3362caa2b895c87351e1914a06c012eee294(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2) {
+
+      (*env_inline_c_0)->DeleteLocalRef(env_inline_c_1,
+                                        obj_inline_c_2) ;
+}
+
+
+jint inline_c_Foreign_JNI_65_c49bfdb1320e2f77592a15068a422089ae09feba(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jint capacity_inline_c_2) {
+
+      (*env_inline_c_0)->PushLocalFrame(env_inline_c_1,
+                                        capacity_inline_c_2); 
+}
+
+
+jobject inline_c_Foreign_JNI_66_53148f1b21d55375d63ea7ade9238bc4f16e149e(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2) {
+return (
+      (*env_inline_c_0)->PopLocalFrame(env_inline_c_1,
+                                       obj_inline_c_2) );
+}
+
+
+void inline_c_Foreign_JNI_67_8d66436d754c77c4f92234fe2c72cb9e8a9ad940(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+       (*env_inline_c_0)->CallVoidMethodA(env_inline_c_1,                                                  obj_inline_c_2,                                                  method_inline_c_3,                                                  cargs_inline_c_4) ;
+}
+
+
+jobject inline_c_Foreign_JNI_68_15d6a4ae1fac1a6ba84db740b2bfac9d44b23329(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallObjectMethodA(env_inline_c_1,                                                  obj_inline_c_2,                                                  method_inline_c_3,                                                  cargs_inline_c_4) );
+}
+
+
+jboolean inline_c_Foreign_JNI_69_71e1f15d31cfe83371d148dbf20d2aebf0293c53(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallBooleanMethodA(env_inline_c_1,                                                  obj_inline_c_2,                                                  method_inline_c_3,                                                  cargs_inline_c_4) );
+}
+
+
+jbyte inline_c_Foreign_JNI_70_5c031f42ce827bc7ef3320331f4333dfb7ddaa3a(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallByteMethodA(env_inline_c_1,                                                  obj_inline_c_2,                                                  method_inline_c_3,                                                  cargs_inline_c_4) );
+}
+
+
+jchar inline_c_Foreign_JNI_71_f67cb9064f7392b8f942ac21b300b8594ddc8043(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallCharMethodA(env_inline_c_1,                                                  obj_inline_c_2,                                                  method_inline_c_3,                                                  cargs_inline_c_4) );
+}
+
+
+jshort inline_c_Foreign_JNI_72_3dc48e937e27459da9000a1fba7694c91562fbd6(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallShortMethodA(env_inline_c_1,                                                  obj_inline_c_2,                                                  method_inline_c_3,                                                  cargs_inline_c_4) );
+}
+
+
+jint inline_c_Foreign_JNI_73_0d9906f84552f67ba8fc08d4403ca6919417f4de(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallIntMethodA(env_inline_c_1,                                                  obj_inline_c_2,                                                  method_inline_c_3,                                                  cargs_inline_c_4) );
+}
+
+
+jlong inline_c_Foreign_JNI_74_ff3b913f1c1e13665522e2fa9f46cf345cef625e(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallLongMethodA(env_inline_c_1,                                                  obj_inline_c_2,                                                  method_inline_c_3,                                                  cargs_inline_c_4) );
+}
+
+
+jfloat inline_c_Foreign_JNI_75_7d9a7c851ecb7d144f8aadac5cb17df79f87edfc(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallFloatMethodA(env_inline_c_1,                                                  obj_inline_c_2,                                                  method_inline_c_3,                                                  cargs_inline_c_4) );
+}
+
+
+jdouble inline_c_Foreign_JNI_76_9928358856d291d7c670b6bb00810c66255dcc65(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject obj_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallDoubleMethodA(env_inline_c_1,                                                  obj_inline_c_2,                                                  method_inline_c_3,                                                  cargs_inline_c_4) );
+}
+
+
+void inline_c_Foreign_JNI_77_5e76a5cd24826d8a13100c53ae77823083465978(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+       (*env_inline_c_0)->CallStaticVoidMethodA(env_inline_c_1,                                                        cls_inline_c_2,                                                        method_inline_c_3,                                                        cargs_inline_c_4) ;
+}
+
+
+jobject inline_c_Foreign_JNI_78_6b65c20aae594d93c02434b3de002e443794ad14(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallStaticObjectMethodA(env_inline_c_1,                                                        cls_inline_c_2,                                                        method_inline_c_3,                                                        cargs_inline_c_4) );
+}
+
+
+jboolean inline_c_Foreign_JNI_79_58bcbcce6c8bf92799bd1db44c288a7b0a260a36(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallStaticBooleanMethodA(env_inline_c_1,                                                        cls_inline_c_2,                                                        method_inline_c_3,                                                        cargs_inline_c_4) );
+}
+
+
+jbyte inline_c_Foreign_JNI_80_1f0c41a419f29557514552df4d07022a006f2ca3(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallStaticByteMethodA(env_inline_c_1,                                                        cls_inline_c_2,                                                        method_inline_c_3,                                                        cargs_inline_c_4) );
+}
+
+
+jchar inline_c_Foreign_JNI_81_51a3cb1cefc6391ba5958a887d84196690a418f2(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallStaticCharMethodA(env_inline_c_1,                                                        cls_inline_c_2,                                                        method_inline_c_3,                                                        cargs_inline_c_4) );
+}
+
+
+jshort inline_c_Foreign_JNI_82_5822dfce2b301e3b90f80885a4a5838bdc57cd44(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallStaticShortMethodA(env_inline_c_1,                                                        cls_inline_c_2,                                                        method_inline_c_3,                                                        cargs_inline_c_4) );
+}
+
+
+jint inline_c_Foreign_JNI_83_4fdc28424fe7ef156763f715ef79463ed387abb2(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallStaticIntMethodA(env_inline_c_1,                                                        cls_inline_c_2,                                                        method_inline_c_3,                                                        cargs_inline_c_4) );
+}
+
+
+jlong inline_c_Foreign_JNI_84_93382b358dc7f0275c6a620eaa67fdfd75167d54(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallStaticLongMethodA(env_inline_c_1,                                                        cls_inline_c_2,                                                        method_inline_c_3,                                                        cargs_inline_c_4) );
+}
+
+
+jfloat inline_c_Foreign_JNI_85_89ea2435847f3d94e8b71bcceba739a077829ca9(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallStaticFloatMethodA(env_inline_c_1,                                                        cls_inline_c_2,                                                        method_inline_c_3,                                                        cargs_inline_c_4) );
+}
+
+
+jdouble inline_c_Foreign_JNI_86_0b9bb41f21f19495338507ff5779f50a17ac0245(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jclass cls_inline_c_2, jmethodID method_inline_c_3, jvalue * cargs_inline_c_4) {
+return (       (*env_inline_c_0)->CallStaticDoubleMethodA(env_inline_c_1,                                                        cls_inline_c_2,                                                        method_inline_c_3,                                                        cargs_inline_c_4) );
+}
+
+
+jobjectArray inline_c_Foreign_JNI_87_0b57533e83360b5a6660bf703521b3b6c2fbec66(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jsize sz_inline_c_2, jclass cls_inline_c_3) {
+return (
+      (*env_inline_c_0)->NewObjectArray(env_inline_c_1,
+                                        sz_inline_c_2,
+                                        cls_inline_c_3,
+                                        NULL) );
+}
+
+
+jbooleanArray inline_c_Foreign_JNI_88_b1841567b8f0e99405458b9e8a3b7f3a3e20812f(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jsize sz_inline_c_2) {
+return (       (*env_inline_c_0)->NewBooleanArray(env_inline_c_1,                                               sz_inline_c_2) );
+}
+
+
+jbyteArray inline_c_Foreign_JNI_89_1a9345ff427557126ba3aa5f0fb460f3df632ff8(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jsize sz_inline_c_2) {
+return (       (*env_inline_c_0)->NewByteArray(env_inline_c_1,                                               sz_inline_c_2) );
+}
+
+
+jcharArray inline_c_Foreign_JNI_90_b00869a9422815fdc3aa43f15e3b782dc6f19e03(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jsize sz_inline_c_2) {
+return (       (*env_inline_c_0)->NewCharArray(env_inline_c_1,                                               sz_inline_c_2) );
+}
+
+
+jshortArray inline_c_Foreign_JNI_91_06a1e38de0ed2d7764757388457ed0797c72d8aa(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jsize sz_inline_c_2) {
+return (       (*env_inline_c_0)->NewShortArray(env_inline_c_1,                                               sz_inline_c_2) );
+}
+
+
+jintArray inline_c_Foreign_JNI_92_af7c087746afd64be9d977ebf078c2df4b3d918e(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jsize sz_inline_c_2) {
+return (       (*env_inline_c_0)->NewIntArray(env_inline_c_1,                                               sz_inline_c_2) );
+}
+
+
+jlongArray inline_c_Foreign_JNI_93_270c75c62cc97fdc3741af544fa35a61abf37fba(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jsize sz_inline_c_2) {
+return (       (*env_inline_c_0)->NewLongArray(env_inline_c_1,                                               sz_inline_c_2) );
+}
+
+
+jfloatArray inline_c_Foreign_JNI_94_312dee8af1c4b2058cb1b2703277cf297ba34224(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jsize sz_inline_c_2) {
+return (       (*env_inline_c_0)->NewFloatArray(env_inline_c_1,                                               sz_inline_c_2) );
+}
+
+
+jdoubleArray inline_c_Foreign_JNI_95_103070e78986a21762e18c9aebfa9162a3565d9b(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jsize sz_inline_c_2) {
+return (       (*env_inline_c_0)->NewDoubleArray(env_inline_c_1,                                               sz_inline_c_2) );
+}
+
+
+jstring inline_c_Foreign_JNI_96_d5ed5b314231e7dc3292c2329a2cb4191e262633(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jchar * ptr_inline_c_2, jsize len_inline_c_3) {
+return (
+      (*env_inline_c_0)->NewString(env_inline_c_1,
+                                   ptr_inline_c_2,
+                                   len_inline_c_3) );
+}
+
+
+jsize inline_c_Foreign_JNI_97_9be0134c89fa74c4d07a2601d81d89982ce8bdf6(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jarray array_inline_c_2) {
+return (
+      (*env_inline_c_0)->GetArrayLength(env_inline_c_1,
+                                        array_inline_c_2) );
+}
+
+
+jsize inline_c_Foreign_JNI_98_2e55ce4ded52c43b7b69c0c49c4425aef4f8a80b(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jstring jstr_inline_c_2) {
+return (
+      (*env_inline_c_0)->GetStringLength(env_inline_c_1,
+                                         jstr_inline_c_2) );
+}
+
+
+jboolean * inline_c_Foreign_JNI_99_153a97a3a91d03dbe7d10fe0e3d48afaa842ee96(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2) {
+return (       (*env_inline_c_0)->GetBooleanArrayElements(env_inline_c_1,                                                       array_inline_c_2,                                                       NULL) );
+}
+
+
+jbyte * inline_c_Foreign_JNI_100_b630cdc25e3d575cad69a3fc3bec490a99a6ab49(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2) {
+return (       (*env_inline_c_0)->GetByteArrayElements(env_inline_c_1,                                                       array_inline_c_2,                                                       NULL) );
+}
+
+
+jchar * inline_c_Foreign_JNI_101_7c0f806fe4d40ac0c1d06b4ccccc42fdc562c8ec(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2) {
+return (       (*env_inline_c_0)->GetCharArrayElements(env_inline_c_1,                                                       array_inline_c_2,                                                       NULL) );
+}
+
+
+jshort * inline_c_Foreign_JNI_102_66e42eb3b8a0e71cc0cc2b722b925689a20ed23f(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2) {
+return (       (*env_inline_c_0)->GetShortArrayElements(env_inline_c_1,                                                       array_inline_c_2,                                                       NULL) );
+}
+
+
+jint * inline_c_Foreign_JNI_103_9ca3666fbed332c3a833881ceba71295e27d5d16(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2) {
+return (       (*env_inline_c_0)->GetIntArrayElements(env_inline_c_1,                                                       array_inline_c_2,                                                       NULL) );
+}
+
+
+jlong * inline_c_Foreign_JNI_104_3bd0969839fbd68b98c00a27f6197704f6e2718b(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2) {
+return (       (*env_inline_c_0)->GetLongArrayElements(env_inline_c_1,                                                       array_inline_c_2,                                                       NULL) );
+}
+
+
+jfloat * inline_c_Foreign_JNI_105_1799b6e92e7df6d5155518d25fe2fde47055a7ff(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2) {
+return (       (*env_inline_c_0)->GetFloatArrayElements(env_inline_c_1,                                                       array_inline_c_2,                                                       NULL) );
+}
+
+
+jdouble * inline_c_Foreign_JNI_106_6b454504f66bff257e7cb14d00f7064112a8f5a2(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2) {
+return (       (*env_inline_c_0)->GetDoubleArrayElements(env_inline_c_1,                                                       array_inline_c_2,                                                       NULL) );
+}
+
+
+const jchar * inline_c_Foreign_JNI_107_ac075a3bdcb535575fe36c6f7b5eb3d66ace44a7(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jstring jstr_inline_c_2) {
+return (
+      (*env_inline_c_0)->GetStringChars(env_inline_c_1,
+                                        jstr_inline_c_2,
+                                        NULL) );
+}
+
+
+void inline_c_Foreign_JNI_108_c405fd2cc3cc77bea5093b022f17d3b2c443e2a2(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jbooleanArray array_inline_c_2, jsize start_inline_c_3, jsize len_inline_c_4, jboolean * buf_inline_c_5) {
+       (*env_inline_c_0)->SetBooleanArrayRegion(env_inline_c_1,                                                     array_inline_c_2,                                                     start_inline_c_3,                                                     len_inline_c_4,                                                     buf_inline_c_5) ;
+}
+
+
+void inline_c_Foreign_JNI_109_6aefb6e6ad5f3a169ff39bd2c3186b897bcd220f(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jbyteArray array_inline_c_2, jsize start_inline_c_3, jsize len_inline_c_4, jbyte * buf_inline_c_5) {
+       (*env_inline_c_0)->SetByteArrayRegion(env_inline_c_1,                                                     array_inline_c_2,                                                     start_inline_c_3,                                                     len_inline_c_4,                                                     buf_inline_c_5) ;
+}
+
+
+void inline_c_Foreign_JNI_110_a1e161a18fa39331bad743b7e07b0399b3104242(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jcharArray array_inline_c_2, jsize start_inline_c_3, jsize len_inline_c_4, jchar * buf_inline_c_5) {
+       (*env_inline_c_0)->SetCharArrayRegion(env_inline_c_1,                                                     array_inline_c_2,                                                     start_inline_c_3,                                                     len_inline_c_4,                                                     buf_inline_c_5) ;
+}
+
+
+void inline_c_Foreign_JNI_111_4221edb9c7b0c1666f5952fe23aad965e2f58085(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jshortArray array_inline_c_2, jsize start_inline_c_3, jsize len_inline_c_4, jshort * buf_inline_c_5) {
+       (*env_inline_c_0)->SetShortArrayRegion(env_inline_c_1,                                                     array_inline_c_2,                                                     start_inline_c_3,                                                     len_inline_c_4,                                                     buf_inline_c_5) ;
+}
+
+
+void inline_c_Foreign_JNI_112_0cf86cc89e108bd7ed150e5aad2e7d8d8f95dd7a(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jintArray array_inline_c_2, jsize start_inline_c_3, jsize len_inline_c_4, jint * buf_inline_c_5) {
+       (*env_inline_c_0)->SetIntArrayRegion(env_inline_c_1,                                                     array_inline_c_2,                                                     start_inline_c_3,                                                     len_inline_c_4,                                                     buf_inline_c_5) ;
+}
+
+
+void inline_c_Foreign_JNI_113_983cb43fd1962ef866086f1819815a8b05791888(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jlongArray array_inline_c_2, jsize start_inline_c_3, jsize len_inline_c_4, jlong * buf_inline_c_5) {
+       (*env_inline_c_0)->SetLongArrayRegion(env_inline_c_1,                                                     array_inline_c_2,                                                     start_inline_c_3,                                                     len_inline_c_4,                                                     buf_inline_c_5) ;
+}
+
+
+void inline_c_Foreign_JNI_114_26c6b32031e2561eb5fc3b047d493c3a784bbf40(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jfloatArray array_inline_c_2, jsize start_inline_c_3, jsize len_inline_c_4, jfloat * buf_inline_c_5) {
+       (*env_inline_c_0)->SetFloatArrayRegion(env_inline_c_1,                                                     array_inline_c_2,                                                     start_inline_c_3,                                                     len_inline_c_4,                                                     buf_inline_c_5) ;
+}
+
+
+void inline_c_Foreign_JNI_115_fbded71aa93cca94c495be618b75c7f610619a93(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jdoubleArray array_inline_c_2, jsize start_inline_c_3, jsize len_inline_c_4, jdouble * buf_inline_c_5) {
+       (*env_inline_c_0)->SetDoubleArrayRegion(env_inline_c_1,                                                     array_inline_c_2,                                                     start_inline_c_3,                                                     len_inline_c_4,                                                     buf_inline_c_5) ;
+}
+
+
+void inline_c_Foreign_JNI_116_5d5355f911ff38accf7df59e4f3407823efa0055(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2, jboolean * xs_inline_c_3) {
+       (*env_inline_c_0)->ReleaseBooleanArrayElements(env_inline_c_1,                                                           array_inline_c_2,                                                           xs_inline_c_3,                                                           JNI_ABORT) ;
+}
+
+
+void inline_c_Foreign_JNI_117_93701f28f0a45b8531f977f3ad28d48b747bb58c(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2, jbyte * xs_inline_c_3) {
+       (*env_inline_c_0)->ReleaseByteArrayElements(env_inline_c_1,                                                           array_inline_c_2,                                                           xs_inline_c_3,                                                           JNI_ABORT) ;
+}
+
+
+void inline_c_Foreign_JNI_118_d799968701da7f57e4eb07390e1efda373184222(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2, jchar * xs_inline_c_3) {
+       (*env_inline_c_0)->ReleaseCharArrayElements(env_inline_c_1,                                                           array_inline_c_2,                                                           xs_inline_c_3,                                                           JNI_ABORT) ;
+}
+
+
+void inline_c_Foreign_JNI_119_03e0c5a40ab324b1c42f0b70ceaeb6b1605a0ded(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2, jshort * xs_inline_c_3) {
+       (*env_inline_c_0)->ReleaseShortArrayElements(env_inline_c_1,                                                           array_inline_c_2,                                                           xs_inline_c_3,                                                           JNI_ABORT) ;
+}
+
+
+void inline_c_Foreign_JNI_120_4c2726a53e147cd6eb61733e27bfc599bc883f74(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2, jint * xs_inline_c_3) {
+       (*env_inline_c_0)->ReleaseIntArrayElements(env_inline_c_1,                                                           array_inline_c_2,                                                           xs_inline_c_3,                                                           JNI_ABORT) ;
+}
+
+
+void inline_c_Foreign_JNI_121_9e1790871e959852cb5032b3f2833c370b81bb0f(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2, jlong * xs_inline_c_3) {
+       (*env_inline_c_0)->ReleaseLongArrayElements(env_inline_c_1,                                                           array_inline_c_2,                                                           xs_inline_c_3,                                                           JNI_ABORT) ;
+}
+
+
+void inline_c_Foreign_JNI_122_44c06b418c79983d60f09ffc49851077b1feb632(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2, jfloat * xs_inline_c_3) {
+       (*env_inline_c_0)->ReleaseFloatArrayElements(env_inline_c_1,                                                           array_inline_c_2,                                                           xs_inline_c_3,                                                           JNI_ABORT) ;
+}
+
+
+void inline_c_Foreign_JNI_123_94830601ce91790b75e74abe9c9819be2e748762(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobject array_inline_c_2, jdouble * xs_inline_c_3) {
+       (*env_inline_c_0)->ReleaseDoubleArrayElements(env_inline_c_1,                                                           array_inline_c_2,                                                           xs_inline_c_3,                                                           JNI_ABORT) ;
+}
+
+
+void inline_c_Foreign_JNI_124_7a962d5f8e6114066dca42386535e4a079022eae(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jstring jstr_inline_c_2, jchar * chars_inline_c_3) {
+
+      (*env_inline_c_0)->ReleaseStringChars(env_inline_c_1,
+                                            jstr_inline_c_2,
+                                            chars_inline_c_3) ;
+}
+
+
+jobject inline_c_Foreign_JNI_125_bb66272284cce01d85b6ec31967e1a66eae7e0c5(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jarray array_inline_c_2, jsize i_inline_c_3) {
+return (
+      (*env_inline_c_0)->GetObjectArrayElement(env_inline_c_1,
+                                               array_inline_c_2,
+                                               i_inline_c_3) );
+}
+
+
+void inline_c_Foreign_JNI_126_c069b4edd080791f4858d4a7a48b8c42c99c8e4a(JNIEnv * env_inline_c_0, JNIEnv * env_inline_c_1, jobjectArray array_inline_c_2, jsize i_inline_c_3, jobject x_inline_c_4) {
+
+      (*env_inline_c_0)->SetObjectArrayElement(env_inline_c_1,
+                                               array_inline_c_2,
+                                               i_inline_c_3,
+                                               x_inline_c_4); ;
+}
+
diff --git a/src/Foreign/JNI.cpphs b/src/Foreign/JNI.cpphs
deleted file mode 100644
--- a/src/Foreign/JNI.cpphs
+++ /dev/null
@@ -1,986 +0,0 @@
--- | Low-level bindings to the Java Native Interface (JNI).
---
--- Read the
--- <https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html JNI spec>
--- for authoritative documentation as to what each of the functions in
--- this module does. The names of the bindings in this module were chosen to
--- match the names of the functions in the JNI spec.
---
--- All bindings in this module access the JNI via a thread-local variable of
--- type @JNIEnv *@. If the current OS thread has not yet been "attached" to the
--- JVM, it is attached implicitly upon the first call to one of these bindings
--- in the current thread.
---
--- The 'String' type in this module is the type of JNI strings. See
--- "Foreign.JNI.String".
-
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE ExplicitNamespaces #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedLabels #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE QuasiQuotes #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TemplateHaskell #-}
-{-# LANGUAGE ViewPatterns #-}
-
--- XXX pgmP directive should be redundant. But necessary to workaround
--- https://github.com/haskell/cabal/issues/4278.
-{-# OPTIONS_GHC -fno-warn-name-shadowing -pgmPcpphs -optP--cpp #-}
-
--- XXX This file uses cpphs for preprocessing instead of the system's native
--- CPP, because the OS X has subtly different whitespace behaviour in the
--- presence of concatenation.
-
-module Foreign.JNI
-  ( -- * JNI functions
-    -- ** VM management
-    withJVM
-  , newJVM
-  , destroyJVM
-    -- ** Class loading
-  , defineClass
-  , JNINativeMethod(..)
-  , registerNatives
-    -- ** String wrappers
-  , ReferenceTypeName
-  , MethodSignature
-  , Signature
-    -- ** Exceptions
-  , JVMException(..)
-  , throw
-  , throwNew
-    -- ** Query functions
-  , findClass
-  , getFieldID
-  , getStaticFieldID
-  , getMethodID
-  , getStaticMethodID
-  , getObjectClass
-    -- ** Reference manipulation
-  , newGlobalRef
-  , deleteGlobalRef
-  , newGlobalRefNonFinalized
-  , deleteGlobalRefNonFinalized
-  , newLocalRef
-  , deleteLocalRef
-  , pushLocalFrame
-  , popLocalFrame
-    -- ** Field accessor functions
-    -- *** Get fields
-  , getObjectField
-  , getBooleanField
-  , getIntField
-  , getLongField
-  , getCharField
-  , getShortField
-  , getByteField
-  , getDoubleField
-  , getFloatField
-    -- *** Get static fields
-  , getStaticObjectField
-  , getStaticBooleanField
-  , getStaticIntField
-  , getStaticLongField
-  , getStaticCharField
-  , getStaticShortField
-  , getStaticByteField
-  , getStaticDoubleField
-  , getStaticFloatField
-    -- *** Set fields
-  , setObjectField
-  , setBooleanField
-  , setIntField
-  , setLongField
-  , setCharField
-  , setShortField
-  , setByteField
-  , setDoubleField
-  , setFloatField
-    -- *** Set static fields
-  , setStaticObjectField
-  , setStaticBooleanField
-  , setStaticIntField
-  , setStaticLongField
-  , setStaticCharField
-  , setStaticShortField
-  , setStaticByteField
-  , setStaticDoubleField
-  , setStaticFloatField
-    -- ** Method invocation
-  , callObjectMethod
-  , callBooleanMethod
-  , callIntMethod
-  , callLongMethod
-  , callCharMethod
-  , callShortMethod
-  , callByteMethod
-  , callDoubleMethod
-  , callFloatMethod
-  , callVoidMethod
-  , callStaticObjectMethod
-  , callStaticVoidMethod
-  , callStaticBooleanMethod
-  , callStaticIntMethod
-  , callStaticLongMethod
-  , callStaticCharMethod
-  , callStaticShortMethod
-  , callStaticByteMethod
-  , callStaticDoubleMethod
-  , callStaticFloatMethod
-    -- ** Object construction
-  , newObject
-  , newString
-  , newObjectArray
-  , newBooleanArray
-  , newByteArray
-  , newCharArray
-  , newShortArray
-  , newIntArray
-  , newLongArray
-  , newFloatArray
-  , newDoubleArray
-    -- ** Array manipulation
-  , getArrayLength
-  , getStringLength
-  , ArrayCopyFailed(..)
-  , NullPointerException(..)
-  , getBooleanArrayElements
-  , getByteArrayElements
-  , getCharArrayElements
-  , getShortArrayElements
-  , getIntArrayElements
-  , getLongArrayElements
-  , getFloatArrayElements
-  , getDoubleArrayElements
-  , getStringChars
-  , setBooleanArrayRegion
-  , setByteArrayRegion
-  , setCharArrayRegion
-  , setShortArrayRegion
-  , setIntArrayRegion
-  , setLongArrayRegion
-  , setFloatArrayRegion
-  , setDoubleArrayRegion
-  , releaseBooleanArrayElements
-  , releaseByteArrayElements
-  , releaseCharArrayElements
-  , releaseShortArrayElements
-  , releaseIntArrayElements
-  , releaseLongArrayElements
-  , releaseFloatArrayElements
-  , releaseDoubleArrayElements
-  , releaseStringChars
-  , getObjectArrayElement
-  , setObjectArrayElement
-    -- * Thread management
-  , attachCurrentThreadAsDaemon
-  , detachCurrentThread
-  , runInAttachedThread
-  ) where
-
-import Control.Concurrent (isCurrentThreadBound, rtsSupportsBoundThreads)
-import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar)
-import Control.Exception (Exception, bracket, bracket_, catch, finally, throwIO)
-import Control.Monad (join, unless, void, when)
-import Data.Choice
-import Data.Coerce
-import Data.Int
-import Data.IORef (IORef, newIORef, atomicModifyIORef)
-import Data.Word
-import Data.ByteString (ByteString)
-import qualified Data.ByteString as BS
-import Data.Monoid ((<>))
-import Data.Typeable (Typeable)
-import Foreign.C (CChar)
-import Foreign.ForeignPtr
-  ( finalizeForeignPtr
-  , newForeignPtr_
-  , withForeignPtr
-  )
-import Foreign.JNI.Internal
-import Foreign.JNI.NativeMethod
-import Foreign.JNI.Types
-import qualified Foreign.JNI.String as JNI
-import Foreign.Marshal.Alloc (alloca)
-import Foreign.Marshal.Array
-import Foreign.Ptr (Ptr, nullPtr)
-import Foreign.Storable (peek)
-import GHC.ForeignPtr (newConcForeignPtr)
-import GHC.Stack (HasCallStack, callStack, getCallStack, prettySrcLoc)
-import qualified Language.C.Inline as C
-import qualified Language.C.Inline.Unsafe as CU
-import System.IO (fixIO)
-import System.IO.Unsafe (unsafePerformIO)
-import Prelude hiding (String)
-import qualified Prelude
-
-C.context (C.baseCtx <> C.bsCtx <> jniCtx)
-
-C.include "<jni.h>"
-C.include "<stdio.h>"
-C.include "<errno.h>"
-C.include "<stdlib.h>"
-
--- A thread-local variable to cache the JNI environment. Accessing this variable
--- is faster than calling @jvm->GetEnv()@.
-$(C.verbatim "static __thread JNIEnv* jniEnv; ")
-
--- | A JNI call may cause a (Java) exception to be raised. This module raises it
--- as a Haskell exception wrapping the Java exception.
-newtype JVMException = JVMException JThrowable
-  deriving (Show, Typeable)
-
-instance Exception JVMException
-
--- | Thrown when @Get<PrimitiveType>ArrayElements@ returns a null pointer,
--- because it wanted to copy the array contents but couldn't. In this case the
--- JVM doesn't throw OutOfMemory according to the JNI spec.
-data ArrayCopyFailed = ArrayCopyFailed
-  deriving (Exception, Show, Typeable)
-
--- | A null reference is found where a non-null reference was expected.
-data NullPointerException = NullPointerException
-  deriving (Exception, Show, Typeable)
-
--- | A JNI call is made from a thread not attached to the JVM.
-data ThreadNotAttached = ThreadNotAttached
-  deriving (Exception, Show, Typeable)
-
--- | A JNI call is made from an unbound thread.
-data ThreadNotBound = ThreadNotBound
-  deriving (Exception, Show, Typeable)
-
--- | Thrown when an JNI call is made from an unbound thread.
-data JNIError = JNIError Prelude.String Int32
-  deriving (Show, Typeable)
-
-instance Exception JNIError
-
--- | Map Java exceptions to Haskell exceptions.
-throwIfException :: Ptr JNIEnv -> IO a -> IO a
-throwIfException env m = m `finally` do
-    excptr <- [CU.exp| jthrowable { (*$(JNIEnv *env))->ExceptionOccurred($(JNIEnv *env)) } |]
-    unless (excptr == nullPtr) $ do
-      [CU.exp| void { (*$(JNIEnv *env))->ExceptionDescribe($(JNIEnv *env)) } |]
-      [CU.exp| void { (*$(JNIEnv *env))->ExceptionClear($(JNIEnv *env)) } |]
-      throwIO . JVMException =<< newGlobalRef =<< objectFromPtr excptr
-
--- | Check whether a pointer is null.
-throwIfNull :: IO (Ptr a) -> IO (Ptr a)
-throwIfNull m = do
-    ptr <- m
-    if ptr == nullPtr
-    then throwIO ArrayCopyFailed
-    else return ptr
-
--- | Throws an error if the given reference is null, otherwise performs
--- the given io action.
-throwIfJNull :: J ty -> IO a -> IO a
-throwIfJNull j io =
-    if j == jnull
-    then throwIO NullPointerException
-    else io
-
--- | A read-write lock
---
--- Concurrent readers are allowed, but only one writer is supported.
-newtype RWLock =
-    RWLock (IORef (Int, RWWantedState))
-    -- ^ A count of the held read locks and the wanted state
-
--- | The wanted state of the RW
-data RWWantedState
-    = Reading            -- ^ There are no writers
-    | Writing (MVar ())
-       -- ^ A writer wants to write, grant no more read locks. The MVar is used
-       -- to notify the writer when the currently held read locks are released.
-
--- | Creates a new read-write lock.
-newRWLock :: IO RWLock
-newRWLock = RWLock <$> newIORef (0, Reading)
-
--- | Tries to acquire a read lock. If this call returns `Do #read`, no writer
--- will be granted a lock before the read lock is released.
-tryAcquireReadLock :: RWLock -> IO (Choice "read")
-tryAcquireReadLock (RWLock ref) = do
-    atomicModifyIORef ref $ \case
-      (!readers,  Reading) -> ((readers + 1, Reading),    Do #read)
-      st                   -> (                       st, Don't #read)
-
--- | Releases a read lock.
-releaseReadLock :: RWLock -> IO ()
-releaseReadLock (RWLock ref) = do
-    st <- atomicModifyIORef ref $
-            \st@(readers, aim) -> ((readers - 1, aim), st)
-    case st of
-      -- Notify the writer if I'm the last reader.
-      (1, Writing mv) -> putMVar mv ()
-      _               -> return ()
-
--- | Waits until the current read locks are released and grants a write lock.
-acquireWriteLock :: RWLock -> IO ()
-acquireWriteLock (RWLock ref) = do
-    mv <- newEmptyMVar
-    join $ atomicModifyIORef ref $ \(readers, _) ->
-      ((readers, Writing mv), when (readers > 0) (takeMVar mv))
-
--- | This lock is used to avoid the JVM from dying before any finalizers
--- deleting global references are finished.
---
--- Finalizers try to acquire read locks.
---
--- The JVM acquires a write lock before shutdown. Thence, finalizers fail to
--- acquire read locks and behave as noops.
-globalJVMLock :: RWLock
-globalJVMLock = unsafePerformIO newRWLock
-{-# NOINLINE globalJVMLock #-}
-
-throwIfNotOK_ :: HasCallStack => IO Int32 -> IO ()
-throwIfNotOK_ m = m >>= \case
-  rc
-    | rc == [CU.pure| jint { JNI_OK } |] -> return ()
-    | rc == [CU.pure| jint { JNI_EDETACHED } |] -> throwIO ThreadNotAttached
-    | otherwise -> throwIO $ JNIError (prettySrcLoc loc) rc
-  where
-    (_, loc):_ = getCallStack callStack
-
-attachCurrentThreadAsDaemon :: IO ()
-attachCurrentThreadAsDaemon = do
-    throwIfNotOK_
-      [CU.exp| jint {
-        (*$(JavaVM* jvm))->AttachCurrentThreadAsDaemon($(JavaVM* jvm), &jniEnv, NULL)
-      } |]
-
-detachCurrentThread :: IO ()
-detachCurrentThread =
-    throwIfNotOK_
-    [CU.block| jint {
-      int rc = (*$(JavaVM* jvm))->DetachCurrentThread($(JavaVM* jvm));
-      if (rc == JNI_OK)
-        jniEnv = NULL;
-      return rc;
-    } |]
-
--- | Attaches the calling thread to the JVM, runs the given IO action and
--- then detaches the thread.
---
--- If the thread is already attached no attaching and detaching is performed.
-runInAttachedThread :: IO a -> IO a
-runInAttachedThread io = do
-    attached <-
-      catch (getJNIEnv >> return True) (\ThreadNotBound -> return False)
-    if attached
-    then io
-    else bracket_
-          attachCurrentThreadAsDaemon
-          detachCurrentThread
-          io
-
--- | The current JVM
---
--- Assumes there's at most one JVM. The current JNI spec (2016) says only
--- one JVM per process is supported anyways.
-{-# NOINLINE jvm #-}
-jvm :: Ptr JVM
-jvm = unsafePerformIO $ alloca $ \pjvm -> alloca $ \pnum_jvms -> do
-    throwIfNotOK_
-      [CU.exp| jint {
-        JNI_GetCreatedJavaVMs($(JavaVM** pjvm), 1, $(jsize* pnum_jvms))
-      }|]
-    num_jvms <- peek pnum_jvms
-    when (num_jvms == 0) $
-      fail "JNI_GetCreatedJavaVMs: No JVM has been initialized yet."
-    when (num_jvms > 1) $
-      fail "JNI_GetCreatedJavaVMs: There are multiple JVMs but only one is supported."
-    peek pjvm
-
--- | Yields the JNIEnv of the calling thread.
---
--- Yields @Nothing@ if the calling thread is not attached to the JVM.
-getJNIEnv :: IO (Ptr JNIEnv)
-getJNIEnv = [CU.exp| JNIEnv* { jniEnv } |] >>= \case
-    env | env == nullPtr -> do
-      throwIfNotOK_
-        [CU.exp| jint {
-          (*$(JavaVM* jvm))->GetEnv($(JavaVM* jvm), (void**)&jniEnv, JNI_VERSION_1_6)
-        }|]
-      [CU.exp| JNIEnv* { jniEnv } |]
-    env -> return env
-
--- | Run an action against the appropriate 'JNIEnv'.
---
--- Each OS thread has its own 'JNIEnv', which this function gives access to.
-withJNIEnv :: (Ptr JNIEnv -> IO a) -> IO a
-withJNIEnv f = getJNIEnv >>= f
-
-useAsCStrings :: [ByteString] -> ([Ptr CChar] -> IO a) -> IO a
-useAsCStrings strs m =
-  foldr (\str k cstrs -> BS.useAsCString str $ \cstr -> k (cstr:cstrs)) m strs []
-
--- | Create a new JVM, with the given arguments. /Can only be called once/. Best
--- practice: use 'withJVM' instead. Only useful for GHCi.
-newJVM :: [ByteString] -> IO JVM
-newJVM options = JVM_ <$> do
-    useAsCStrings options $ \cstrs -> do
-      withArray cstrs $ \(coptions :: Ptr (Ptr CChar)) -> do
-        let n = fromIntegral (length cstrs) :: C.CInt
-        checkBoundness
-        [C.block| JavaVM * {
-          JavaVM *jvm;
-          JavaVMInitArgs vm_args;
-          JavaVMOption *options = malloc(sizeof(JavaVMOption) * $(int n));
-          for(int i = 0; i < $(int n); i++)
-                  options[i].optionString = $(char **coptions)[i];
-          vm_args.version = JNI_VERSION_1_6;
-          vm_args.nOptions = $(int n);
-          vm_args.options = options;
-          vm_args.ignoreUnrecognized = 0;
-          JNI_CreateJavaVM(&jvm, (void**)&jniEnv, &vm_args);
-          free(options);
-          return jvm; } |]
-
-  where
-    checkBoundness :: IO ()
-    checkBoundness = when rtsSupportsBoundThreads $ do
-      bound <- isCurrentThreadBound
-      unless bound (throwIO ThreadNotBound)
-
--- | Deallocate a 'JVM' created using 'newJVM'.
-destroyJVM :: JVM -> IO ()
-destroyJVM (JVM_ jvm) = do
-    acquireWriteLock globalJVMLock
-    [C.block| void {
-        (*$(JavaVM *jvm))->DestroyJavaVM($(JavaVM *jvm));
-        jniEnv = NULL;
-    } |]
-
--- | Create a new JVM, with the given arguments. Destroy it once the given
--- action completes. /Can only be called once/. Best practice: use it to wrap
--- your @main@ function.
-withJVM :: [ByteString] -> IO a -> IO a
-withJVM options action = bracket (newJVM options) destroyJVM (const action)
-
-defineClass
-  :: Coercible o (J ('Class "java.lang.ClassLoader"))
-  => ReferenceTypeName -- ^ Class name
-  -> o -- ^ Loader
-  -> ByteString -- ^ Bytecode buffer
-  -> IO JClass
-defineClass (coerce -> name) (coerce -> upcast -> loader) buf = withJNIEnv $ \env ->
-    throwIfException env $
-    JNI.withString name $ \namep ->
-    objectFromPtr =<<
-    [CU.exp| jclass {
-      (*$(JNIEnv *env))->DefineClass($(JNIEnv *env),
-                                     $(char *namep),
-                                     $fptr-ptr:(jobject loader),
-                                     $bs-ptr:buf,
-                                     $bs-len:buf) } |]
-registerNatives
-  :: JClass
-  -> [JNINativeMethod]
-  -> IO ()
-registerNatives cls methods = withJNIEnv $ \env ->
-    throwIfException env $
-    withArray methods $ \cmethods -> do
-      let numMethods = fromIntegral $ length methods
-      _ <- [CU.exp| jint {
-             (*$(JNIEnv *env))->RegisterNatives($(JNIEnv *env),
-                                                $fptr-ptr:(jclass cls),
-                                                $(JNINativeMethod *cmethods),
-                                                $(int numMethods)) } |]
-      return ()
-
-throw :: Coercible o (J a) => o -> IO ()
-throw (coerce -> upcast -> obj) = withJNIEnv $ \env -> void $ do
-    [CU.exp| jint {
-       (*$(JNIEnv *env))->Throw($(JNIEnv *env),
-                                $fptr-ptr:(jobject obj)) } |]
-
-throwNew :: JClass -> JNI.String -> IO ()
-throwNew cls msg = throwIfJNull cls $ withJNIEnv $ \env ->
-    JNI.withString msg $ \msgp -> void $ do
-    [CU.exp| jint {
-       (*$(JNIEnv *env))->ThrowNew($(JNIEnv *env),
-                                   $fptr-ptr:(jclass cls),
-                                   $(char *msgp)) } |]
-
-findClass
-  :: ReferenceTypeName -- ^ Class name
-  -> IO JClass
-findClass (coerce -> name) = withJNIEnv $ \env ->
-    throwIfException env $
-    JNI.withString name $ \namep ->
-    objectFromPtr =<<
-    [CU.exp| jclass { (*$(JNIEnv *env))->FindClass($(JNIEnv *env), $(char *namep)) } |]
-
-newObject :: JClass -> MethodSignature -> [JValue] -> IO JObject
-newObject cls (coerce -> sig) args = throwIfJNull cls $ withJNIEnv $ \env ->
-    throwIfException env $
-    withJValues args $ \cargs -> do
-      constr <- getMethodID cls "<init>" sig
-      objectFromPtr =<< [CU.exp| jobject {
-        (*$(JNIEnv *env))->NewObjectA($(JNIEnv *env),
-                                      $fptr-ptr:(jclass cls),
-                                      $(jmethodID constr),
-                                      $(jvalue *cargs)) } |]
-
-getFieldID
-  :: JClass -- ^ A class object as returned by 'findClass'
-  -> JNI.String -- ^ Field name
-  -> Signature -- ^ JNI signature
-  -> IO JFieldID
-getFieldID cls fieldname (coerce -> sig) = throwIfJNull cls $
-    withJNIEnv $ \env ->
-    throwIfException env $
-    JNI.withString fieldname $ \fieldnamep ->
-    JNI.withString sig $ \sigp ->
-    [CU.exp| jfieldID {
-      (*$(JNIEnv *env))->GetFieldID($(JNIEnv *env),
-                                    $fptr-ptr:(jclass cls),
-                                    $(char *fieldnamep),
-                                    $(char *sigp)) } |]
-
-getStaticFieldID
-  :: JClass -- ^ A class object as returned by 'findClass'
-  -> JNI.String -- ^ Field name
-  -> Signature -- ^ JNI signature
-  -> IO JFieldID
-getStaticFieldID cls fieldname (coerce -> sig) = throwIfJNull cls $
-    withJNIEnv $ \env ->
-    throwIfException env $
-    JNI.withString fieldname $ \fieldnamep ->
-    JNI.withString sig $ \sigp ->
-    [CU.exp| jfieldID {
-      (*$(JNIEnv *env))->GetStaticFieldID($(JNIEnv *env),
-                                          $fptr-ptr:(jclass cls),
-                                          $(char *fieldnamep),
-                                          $(char *sigp)) } |]
-
-#define GET_FIELD(name, hs_rettype, c_rettype) \
-get/**/name/**/Field :: Coercible o (J a) => o -> JFieldID -> IO hs_rettype; \
-get/**/name/**/Field (coerce -> upcast -> obj) field = withJNIEnv $ \env -> \
-    throwIfException env $ \
-    [CU.exp| c_rettype { \
-      (*$(JNIEnv *env))->Get/**/name/**/Field($(JNIEnv *env), \
-                                              $fptr-ptr:(jobject obj), \
-                                              $(jfieldID field)) } |]
-
-getObjectField :: Coercible o (J a) => o -> JFieldID -> IO JObject
-getObjectField x y =
-    let GET_FIELD(Object, (Ptr JObject), jobject)
-    in objectFromPtr =<< getObjectField x y
-GET_FIELD(Boolean, Word8, jboolean)
-GET_FIELD(Byte, CChar, jbyte)
-GET_FIELD(Char, Word16, jchar)
-GET_FIELD(Short, Int16, jshort)
-GET_FIELD(Int, Int32, jint)
-GET_FIELD(Long, Int64, jlong)
-GET_FIELD(Float, Float, jfloat)
-GET_FIELD(Double, Double, jdouble)
-
-#define GET_STATIC_FIELD(name, hs_rettype, c_rettype) \
-getStatic/**/name/**/Field :: JClass -> JFieldID -> IO hs_rettype; \
-getStatic/**/name/**/Field klass field = throwIfJNull klass $ \
-    withJNIEnv $ \env -> \
-    throwIfException env $ \
-    [CU.exp| c_rettype { \
-      (*$(JNIEnv *env))->GetStatic/**/name/**/Field($(JNIEnv *env), \
-                                                    $fptr-ptr:(jclass klass), \
-                                                    $(jfieldID field)) } |]
-
-getStaticObjectField :: JClass -> JFieldID -> IO JObject
-getStaticObjectField x y =
-    let GET_STATIC_FIELD(Object, (Ptr JObject), jobject)
-    in objectFromPtr =<< getStaticObjectField x y
-GET_STATIC_FIELD(Boolean, Word8, jboolean)
-GET_STATIC_FIELD(Byte, CChar, jbyte)
-GET_STATIC_FIELD(Char, Word16, jchar)
-GET_STATIC_FIELD(Short, Int16, jshort)
-GET_STATIC_FIELD(Int, Int32, jint)
-GET_STATIC_FIELD(Long, Int64, jlong)
-GET_STATIC_FIELD(Float, Float, jfloat)
-GET_STATIC_FIELD(Double, Double, jdouble)
-
-#define SET_FIELD(name, hs_fieldtype, c_fieldtype) \
-set/**/name/**/Field :: Coercible o (J a) => o -> JFieldID -> hs_fieldtype -> IO (); \
-set/**/name/**/Field (coerce -> upcast -> obj) field x = \
-    withJNIEnv $ \env -> \
-    throwIfException env $ \
-    [CU.block| void { \
-      (*$(JNIEnv *env))->Set/**/name/**/Field($(JNIEnv *env), \
-                                              $fptr-ptr:(jobject obj), \
-                                              $(jfieldID field), \
-                                              $(c_fieldtype x)); } |]
-
-setObjectField :: Coercible o (J a) => o -> JFieldID -> JObject -> IO ()
-setObjectField x y z =
-    let SET_FIELD(Object, (Ptr JObject), jobject)
-    in withForeignPtr (coerce z) (setObjectField x y)
-SET_FIELD(Boolean, Word8, jboolean)
-SET_FIELD(Byte, CChar, jbyte)
-SET_FIELD(Char, Word16, jchar)
-SET_FIELD(Short, Int16, jshort)
-SET_FIELD(Int, Int32, jint)
-SET_FIELD(Long, Int64, jlong)
-SET_FIELD(Float, Float, jfloat)
-SET_FIELD(Double, Double, jdouble)
-
-#define SET_STATIC_FIELD(name, hs_fieldtype, c_fieldtype) \
-setStatic/**/name/**/Field :: JClass -> JFieldID -> hs_fieldtype -> IO (); \
-setStatic/**/name/**/Field klass field x = throwIfJNull klass $ \
-    withJNIEnv $ \env -> \
-    throwIfException env $ \
-    [CU.block| void { \
-      (*$(JNIEnv *env))->SetStatic/**/name/**/Field($(JNIEnv *env), \
-                                                    $fptr-ptr:(jclass klass), \
-                                                    $(jfieldID field), \
-                                                    $(c_fieldtype x)); } |]
-
-setStaticObjectField :: JClass -> JFieldID -> JObject -> IO ()
-setStaticObjectField x y z =
-    let SET_STATIC_FIELD(Object, (Ptr JObject), jobject)
-    in withForeignPtr (coerce z) (setStaticObjectField x y)
-SET_STATIC_FIELD(Boolean, Word8, jboolean)
-SET_STATIC_FIELD(Byte, CChar, jbyte)
-SET_STATIC_FIELD(Char, Word16, jchar)
-SET_STATIC_FIELD(Short, Int16, jshort)
-SET_STATIC_FIELD(Int, Int32, jint)
-SET_STATIC_FIELD(Long, Int64, jlong)
-SET_STATIC_FIELD(Float, Float, jfloat)
-SET_STATIC_FIELD(Double, Double, jdouble)
-
-getMethodID
-  :: JClass -- ^ A class object as returned by 'findClass'
-  -> JNI.String -- ^ Field name
-  -> MethodSignature -- ^ JNI signature
-  -> IO JMethodID
-getMethodID cls methodname (coerce -> sig) = throwIfJNull cls $
-    withJNIEnv $ \env ->
-    throwIfException env $
-    JNI.withString methodname $ \methodnamep ->
-    JNI.withString sig $ \sigp ->
-    [CU.exp| jmethodID {
-      (*$(JNIEnv *env))->GetMethodID($(JNIEnv *env),
-                                     $fptr-ptr:(jclass cls),
-                                     $(char *methodnamep),
-                                     $(char *sigp)) } |]
-
-getStaticMethodID
-  :: JClass -- ^ A class object as returned by 'findClass'
-  -> JNI.String -- ^ Field name
-  -> MethodSignature -- ^ JNI signature
-  -> IO JMethodID
-getStaticMethodID cls methodname (coerce -> sig) = throwIfJNull cls $
-    withJNIEnv $ \env ->
-    throwIfException env $
-    JNI.withString methodname $ \methodnamep ->
-    JNI.withString sig $ \sigp ->
-    [CU.exp| jmethodID {
-      (*$(JNIEnv *env))->GetStaticMethodID($(JNIEnv *env),
-                                           $fptr-ptr:(jclass cls),
-                                           $(char *methodnamep),
-                                           $(char *sigp)) } |]
-
-getObjectClass :: Coercible o (J ty) => o -> IO JClass
-getObjectClass (coerce -> upcast -> obj) = throwIfJNull obj $
-    withJNIEnv $ \env ->
-    objectFromPtr =<<
-    [CU.exp| jclass {
-      (*$(JNIEnv *env))->GetObjectClass($(JNIEnv *env),
-                                        $fptr-ptr:(jobject obj)) } |]
-
--- | Creates a global reference to the object referred to by
--- the given reference.
---
--- Arranges for a finalizer to call 'deleteGlobalRef' when the
--- global reference is no longer reachable on the Haskell side.
-newGlobalRef :: Coercible o (J ty) => o -> IO o
-newGlobalRef (coerce -> upcast -> obj) = withJNIEnv $ \env -> do
-    gobj <-
-      [CU.exp| jobject {
-        (*$(JNIEnv *env))->NewGlobalRef($(JNIEnv *env),
-                                        $fptr-ptr:(jobject obj)) } |]
-    fixIO $ \j ->
-      coerce <$> J <$> newConcForeignPtr gobj (deleteGlobalRefNonFinalized j)
-
-deleteGlobalRef :: Coercible o (J ty) => o -> IO ()
-deleteGlobalRef (coerce -> J p) = finalizeForeignPtr p
-
--- | Like 'newGlobalRef' but it doesn't attach a finalizer to destroy
--- the reference when it is not longer reachable. Use
--- 'deleteGlobalRefNonFinalized' to destroy this reference.
-newGlobalRefNonFinalized :: Coercible o (J ty) => o -> IO o
-newGlobalRefNonFinalized (coerce -> upcast -> obj) = withJNIEnv $ \env -> do
-    gobj <-
-      [CU.exp| jobject {
-        (*$(JNIEnv *env))->NewGlobalRef($(JNIEnv *env),
-                                        $fptr-ptr:(jobject obj)) } |]
-    coerce <$> J <$> newForeignPtr_ gobj
-
--- | Like 'deleteGlobalRef' but it can be used only on references created with
--- 'newGlobalRefNonFinalized'.
-deleteGlobalRefNonFinalized :: Coercible o (J ty) => o -> IO ()
-deleteGlobalRefNonFinalized (coerce -> upcast -> obj) = do
-    bracket (tryAcquireReadLock globalJVMLock)
-            (\doRead -> when (toBool doRead) $ releaseReadLock globalJVMLock)
-            $ \doRead ->
-      when (toBool doRead) $ withJNIEnv $ \env -> do
-        [CU.block| void { (*$(JNIEnv *env))->DeleteGlobalRef($(JNIEnv *env)
-                                                            ,$fptr-ptr:(jobject obj));
-                        } |]
-
--- NB: Cannot add a finalizer to local references because it may
--- run in a thread where the reference is not valid.
-newLocalRef :: Coercible o (J ty) => o -> IO (J ty)
-newLocalRef (coerce -> upcast -> obj) = withJNIEnv $ \env ->
-    unsafeCast <$> (objectFromPtr =<<)
-    [CU.exp| jobject {
-      (*$(JNIEnv *env))->NewLocalRef($(JNIEnv *env),
-                                     $fptr-ptr:(jobject obj)) } |]
-
-deleteLocalRef :: Coercible o (J ty) => o -> IO ()
-deleteLocalRef (coerce -> upcast -> obj) = withJNIEnv $ \env ->
-    [CU.exp| void {
-      (*$(JNIEnv *env))->DeleteLocalRef($(JNIEnv *env),
-                                        $fptr-ptr:(jobject obj)) } |]
-
-pushLocalFrame :: Int32 -> IO ()
-pushLocalFrame (coerce -> capacity) = withJNIEnv $ \env ->
-    -- We ignore the output as it is always 0 on success and throws an
-    -- exception otherwise.
-    throwIfException env $ void $
-    [CU.block| jint {
-      (*$(JNIEnv *env))->PushLocalFrame($(JNIEnv *env),
-                                        $(jint capacity)); } |]
-
-popLocalFrame :: Coercible o (J ty) => o -> IO o
-popLocalFrame (coerce -> upcast -> obj) = withJNIEnv $ \env ->
-    coerce <$> (objectFromPtr =<<)
-    [CU.exp| jobject {
-      (*$(JNIEnv *env))->PopLocalFrame($(JNIEnv *env),
-                                       $fptr-ptr:(jobject obj)) } |]
-
--- Modern CPP does have ## for concatenating strings, but we use the hacky /**/
--- comment syntax for string concatenation. This is because GHC passes
--- the -traditional flag to the preprocessor by default, which turns off several
--- modern CPP features.
-
-#define CALL_METHOD(name, hs_rettype, c_rettype) \
-call/**/name/**/Method :: Coercible o (J a) => o -> JMethodID -> [JValue] -> IO hs_rettype; \
-call/**/name/**/Method (coerce -> upcast -> obj) method args = withJNIEnv $ \env -> \
-    throwIfException env $ \
-    withJValues args $ \cargs -> \
-    [C.exp| c_rettype { \
-      (*$(JNIEnv *env))->Call/**/name/**/MethodA($(JNIEnv *env), \
-                                                 $fptr-ptr:(jobject obj), \
-                                                 $(jmethodID method), \
-                                                 $(jvalue *cargs)) } |]
-
-CALL_METHOD(Void, (), void)
-callObjectMethod :: Coercible o (J a) => o -> JMethodID -> [JValue] -> IO JObject
-callObjectMethod x y z =
-    let CALL_METHOD(Object, (Ptr JObject), jobject)
-    in objectFromPtr =<< callObjectMethod x y z
-callBooleanMethod :: Coercible o (J a) => o -> JMethodID -> [JValue] -> IO Bool
-callBooleanMethod x y z =
-    let CALL_METHOD(Boolean, Word8, jboolean)
-    in toEnum . fromIntegral <$> callBooleanMethod x y z
-CALL_METHOD(Byte, CChar, jbyte)
-CALL_METHOD(Char, Word16, jchar)
-CALL_METHOD(Short, Int16, jshort)
-CALL_METHOD(Int, Int32, jint)
-CALL_METHOD(Long, Int64, jlong)
-CALL_METHOD(Float, Float, jfloat)
-CALL_METHOD(Double, Double, jdouble)
-
-#define CALL_STATIC_METHOD(name, hs_rettype, c_rettype) \
-callStatic/**/name/**/Method :: JClass -> JMethodID -> [JValue] -> IO hs_rettype; \
-callStatic/**/name/**/Method cls method args = throwIfJNull cls $ \
-    withJNIEnv $ \env -> \
-    throwIfException env $ \
-    withJValues args $ \cargs -> \
-    [C.exp| c_rettype { \
-      (*$(JNIEnv *env))->CallStatic/**/name/**/MethodA($(JNIEnv *env), \
-                                                       $fptr-ptr:(jclass cls), \
-                                                       $(jmethodID method), \
-                                                       $(jvalue *cargs)) } |]
-
-CALL_STATIC_METHOD(Void, (), void)
-callStaticObjectMethod :: JClass -> JMethodID -> [JValue] -> IO JObject
-callStaticObjectMethod x y z =
-    let CALL_STATIC_METHOD(Object, (Ptr JObject), jobject)
-    in objectFromPtr =<< callStaticObjectMethod x y z
-callStaticBooleanMethod :: JClass -> JMethodID -> [JValue] -> IO Bool
-callStaticBooleanMethod x y z =
-    let CALL_STATIC_METHOD(Boolean, Word8, jboolean)
-    in toEnum . fromIntegral <$> callStaticBooleanMethod x y z
-CALL_STATIC_METHOD(Byte, CChar, jbyte)
-CALL_STATIC_METHOD(Char, Word16, jchar)
-CALL_STATIC_METHOD(Short, Int16, jshort)
-CALL_STATIC_METHOD(Int, Int32, jint)
-CALL_STATIC_METHOD(Long, Int64, jlong)
-CALL_STATIC_METHOD(Float, Float, jfloat)
-CALL_STATIC_METHOD(Double, Double, jdouble)
-
-newObjectArray :: Int32 -> JClass -> IO JObjectArray
-newObjectArray sz cls = throwIfJNull cls $ withJNIEnv $ \env ->
-    throwIfException env $
-    objectFromPtr =<<
-    [CU.exp| jobjectArray {
-      (*$(JNIEnv *env))->NewObjectArray($(JNIEnv *env),
-                                        $(jsize sz),
-                                        $fptr-ptr:(jclass cls),
-                                        NULL) } |]
-
-#define NEW_ARRAY(name, c_rettype) \
-new/**/name/**/Array :: Int32 -> IO J/**/name/**/Array; \
-new/**/name/**/Array sz = withJNIEnv $ \env -> \
-    throwIfException env $ \
-    objectFromPtr =<< \
-    [CU.exp| c_rettype/**/Array { \
-      (*$(JNIEnv *env))->New/**/name/**/Array($(JNIEnv *env), \
-                                              $(jsize sz)) } |]
-
-NEW_ARRAY(Boolean, jboolean)
-NEW_ARRAY(Byte, jbyte)
-NEW_ARRAY(Char, jchar)
-NEW_ARRAY(Short, jshort)
-NEW_ARRAY(Int, jint)
-NEW_ARRAY(Long, jlong)
-NEW_ARRAY(Float, jfloat)
-NEW_ARRAY(Double, jdouble)
-
-newString :: Ptr Word16 -> Int32 -> IO JString
-newString ptr len = withJNIEnv $ \env ->
-    throwIfException env $
-    objectFromPtr =<<
-    [CU.exp| jstring {
-      (*$(JNIEnv *env))->NewString($(JNIEnv *env),
-                                   $(jchar *ptr),
-                                   $(jsize len)) } |]
-
-getArrayLength :: Coercible o (JArray a) => o -> IO Int32
-getArrayLength (coerce -> upcast -> array) = throwIfJNull array $
-    withJNIEnv $ \env ->
-    [C.exp| jsize {
-      (*$(JNIEnv *env))->GetArrayLength($(JNIEnv *env),
-                                        $fptr-ptr:(jarray array)) } |]
-
-getStringLength :: JString -> IO Int32
-getStringLength jstr = throwIfJNull jstr $ withJNIEnv $ \env ->
-    [CU.exp| jsize {
-      (*$(JNIEnv *env))->GetStringLength($(JNIEnv *env),
-                                         $fptr-ptr:(jstring jstr)) } |]
-
-#define GET_ARRAY_ELEMENTS(name, hs_rettype, c_rettype) \
-get/**/name/**/ArrayElements :: J/**/name/**/Array -> IO (Ptr hs_rettype); \
-get/**/name/**/ArrayElements (upcast -> array) = throwIfJNull array $ \
-    withJNIEnv $ \env -> \
-    throwIfNull $ \
-    [CU.exp| c_rettype* { \
-      (*$(JNIEnv *env))->Get/**/name/**/ArrayElements($(JNIEnv *env), \
-                                                      $fptr-ptr:(jobject array), \
-                                                      NULL) } |]
-
-GET_ARRAY_ELEMENTS(Boolean, Word8, jboolean)
-GET_ARRAY_ELEMENTS(Byte, CChar, jbyte)
-GET_ARRAY_ELEMENTS(Char, Word16, jchar)
-GET_ARRAY_ELEMENTS(Short, Int16, jshort)
-GET_ARRAY_ELEMENTS(Int, Int32, jint)
-GET_ARRAY_ELEMENTS(Long, Int64, jlong)
-GET_ARRAY_ELEMENTS(Float, Float, jfloat)
-GET_ARRAY_ELEMENTS(Double, Double, jdouble)
-
-getStringChars :: JString -> IO (Ptr Word16)
-getStringChars jstr = throwIfJNull jstr $ withJNIEnv $ \env ->
-    throwIfNull $
-    [CU.exp| const jchar* {
-      (*$(JNIEnv *env))->GetStringChars($(JNIEnv *env),
-                                        $fptr-ptr:(jstring jstr),
-                                        NULL) } |]
-
-#define SET_ARRAY_REGION(name, hs_argtype, c_argtype) \
-set/**/name/**/ArrayRegion :: J/**/name/**/Array -> Int32 -> Int32 -> Ptr hs_argtype -> IO (); \
-set/**/name/**/ArrayRegion array start len buf = throwIfJNull array $ \
-    withJNIEnv $ \env -> \
-    throwIfException env $ \
-    [CU.exp| void { \
-      (*$(JNIEnv *env))->Set/**/name/**/ArrayRegion($(JNIEnv *env), \
-                                                    $fptr-ptr:(c_argtype/**/Array array), \
-                                                    $(jsize start), \
-                                                    $(jsize len), \
-                                                    $(c_argtype *buf)) } |]
-
-SET_ARRAY_REGION(Boolean, Word8, jboolean)
-SET_ARRAY_REGION(Byte, CChar, jbyte)
-SET_ARRAY_REGION(Char, Word16, jchar)
-SET_ARRAY_REGION(Short, Int16, jshort)
-SET_ARRAY_REGION(Int, Int32, jint)
-SET_ARRAY_REGION(Long, Int64, jlong)
-SET_ARRAY_REGION(Float, Float, jfloat)
-SET_ARRAY_REGION(Double, Double, jdouble)
-
-#define RELEASE_ARRAY_ELEMENTS(name, hs_argtype, c_argtype) \
-release/**/name/**/ArrayElements :: J/**/name/**/Array -> Ptr hs_argtype -> IO (); \
-release/**/name/**/ArrayElements (upcast -> array) xs = throwIfJNull array $ \
-    withJNIEnv $ \env -> \
-    [CU.exp| void { \
-      (*$(JNIEnv *env))->Release/**/name/**/ArrayElements($(JNIEnv *env), \
-                                                          $fptr-ptr:(jobject array), \
-                                                          $(c_argtype *xs), \
-                                                          JNI_ABORT) } |]
-
-RELEASE_ARRAY_ELEMENTS(Boolean, Word8, jboolean)
-RELEASE_ARRAY_ELEMENTS(Byte, CChar, jbyte)
-RELEASE_ARRAY_ELEMENTS(Char, Word16, jchar)
-RELEASE_ARRAY_ELEMENTS(Short, Int16, jshort)
-RELEASE_ARRAY_ELEMENTS(Int, Int32, jint)
-RELEASE_ARRAY_ELEMENTS(Long, Int64, jlong)
-RELEASE_ARRAY_ELEMENTS(Float, Float, jfloat)
-RELEASE_ARRAY_ELEMENTS(Double, Double, jdouble)
-
-releaseStringChars :: JString -> Ptr Word16 -> IO ()
-releaseStringChars jstr chars = throwIfJNull jstr $ withJNIEnv $ \env ->
-    [CU.exp| void {
-      (*$(JNIEnv *env))->ReleaseStringChars($(JNIEnv *env),
-                                            $fptr-ptr:(jstring jstr),
-                                            $(jchar *chars)) } |]
-
-getObjectArrayElement
-  :: forall a o.
-     (IsReferenceType a, Coercible o (J a))
-  => JArray a
-  -> Int32
-  -> IO o
-getObjectArrayElement (arrayUpcast -> array) i = throwIfJNull array $
-    withJNIEnv $ \env ->
-    ( (coerce :: J a -> o)
-    . (unsafeCast :: JObject -> J a)
-    ) <$> (objectFromPtr =<<)
-    [C.exp| jobject {
-      (*$(JNIEnv *env))->GetObjectArrayElement($(JNIEnv *env),
-                                               $fptr-ptr:(jarray array),
-                                               $(jsize i)) } |]
-
-setObjectArrayElement
-  :: forall a o.
-     (IsReferenceType a, Coercible o (J a))
-  => JArray a
-  -> Int32
-  -> o
-  -> IO ()
-setObjectArrayElement (arrayUpcast -> array)
-                      i
-                      ((coerce :: o -> J a) -> upcast -> x) =
-    throwIfJNull array $
-    withJNIEnv $ \env ->
-    [C.exp| void {
-      (*$(JNIEnv *env))->SetObjectArrayElement($(JNIEnv *env),
-                                               $fptr-ptr:(jobjectArray array),
-                                               $(jsize i),
-                                               $fptr-ptr:(jobject x)); } |]
diff --git a/src/Foreign/JNI.hs b/src/Foreign/JNI.hs
new file mode 100644
--- /dev/null
+++ b/src/Foreign/JNI.hs
@@ -0,0 +1,987 @@
+-- | Low-level bindings to the Java Native Interface (JNI).
+--
+-- Read the
+-- <https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html JNI spec>
+-- for authoritative documentation as to what each of the functions in
+-- this module does. The names of the bindings in this module were chosen to
+-- match the names of the functions in the JNI spec.
+--
+-- All bindings in this module access the JNI via a thread-local variable of
+-- type @JNIEnv *@. If the current OS thread has not yet been "attached" to the
+-- JVM, it is attached implicitly upon the first call to one of these bindings
+-- in the current thread.
+--
+-- The 'String' type in this module is the type of JNI strings. See
+-- "Foreign.JNI.String".
+
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE ExplicitNamespaces #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedLabels #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE ViewPatterns #-}
+
+-- XXX pgmP directive should be redundant. But necessary to workaround
+-- https://github.com/haskell/cabal/issues/4278.
+{-# OPTIONS_GHC -fno-warn-name-shadowing -pgmPcpphs -optP--cpp #-}
+
+-- XXX This file uses cpphs for preprocessing instead of the system's native
+-- CPP, because the OS X has subtly different whitespace behaviour in the
+-- presence of concatenation.
+
+module Foreign.JNI
+  ( -- * JNI functions
+    -- ** VM management
+    withJVM
+  , newJVM
+  , destroyJVM
+    -- ** Class loading
+  , defineClass
+  , JNINativeMethod(..)
+  , registerNatives
+    -- ** String wrappers
+  , ReferenceTypeName
+  , MethodSignature
+  , Signature
+    -- ** Exceptions
+  , JVMException(..)
+  , throw
+  , throwNew
+    -- ** Query functions
+  , findClass
+  , getFieldID
+  , getStaticFieldID
+  , getMethodID
+  , getStaticMethodID
+  , getObjectClass
+    -- ** Reference manipulation
+  , newGlobalRef
+  , deleteGlobalRef
+  , newGlobalRefNonFinalized
+  , deleteGlobalRefNonFinalized
+  , newLocalRef
+  , deleteLocalRef
+  , pushLocalFrame
+  , popLocalFrame
+    -- ** Field accessor functions
+    -- *** Get fields
+  , getObjectField
+  , getBooleanField
+  , getIntField
+  , getLongField
+  , getCharField
+  , getShortField
+  , getByteField
+  , getDoubleField
+  , getFloatField
+    -- *** Get static fields
+  , getStaticObjectField
+  , getStaticBooleanField
+  , getStaticIntField
+  , getStaticLongField
+  , getStaticCharField
+  , getStaticShortField
+  , getStaticByteField
+  , getStaticDoubleField
+  , getStaticFloatField
+    -- *** Set fields
+  , setObjectField
+  , setBooleanField
+  , setIntField
+  , setLongField
+  , setCharField
+  , setShortField
+  , setByteField
+  , setDoubleField
+  , setFloatField
+    -- *** Set static fields
+  , setStaticObjectField
+  , setStaticBooleanField
+  , setStaticIntField
+  , setStaticLongField
+  , setStaticCharField
+  , setStaticShortField
+  , setStaticByteField
+  , setStaticDoubleField
+  , setStaticFloatField
+    -- ** Method invocation
+  , callObjectMethod
+  , callBooleanMethod
+  , callIntMethod
+  , callLongMethod
+  , callCharMethod
+  , callShortMethod
+  , callByteMethod
+  , callDoubleMethod
+  , callFloatMethod
+  , callVoidMethod
+  , callStaticObjectMethod
+  , callStaticVoidMethod
+  , callStaticBooleanMethod
+  , callStaticIntMethod
+  , callStaticLongMethod
+  , callStaticCharMethod
+  , callStaticShortMethod
+  , callStaticByteMethod
+  , callStaticDoubleMethod
+  , callStaticFloatMethod
+    -- ** Object construction
+  , newObject
+  , newString
+  , newObjectArray
+  , newBooleanArray
+  , newByteArray
+  , newCharArray
+  , newShortArray
+  , newIntArray
+  , newLongArray
+  , newFloatArray
+  , newDoubleArray
+    -- ** Array manipulation
+  , getArrayLength
+  , getStringLength
+  , ArrayCopyFailed(..)
+  , NullPointerException(..)
+  , getBooleanArrayElements
+  , getByteArrayElements
+  , getCharArrayElements
+  , getShortArrayElements
+  , getIntArrayElements
+  , getLongArrayElements
+  , getFloatArrayElements
+  , getDoubleArrayElements
+  , getStringChars
+  , setBooleanArrayRegion
+  , setByteArrayRegion
+  , setCharArrayRegion
+  , setShortArrayRegion
+  , setIntArrayRegion
+  , setLongArrayRegion
+  , setFloatArrayRegion
+  , setDoubleArrayRegion
+  , releaseBooleanArrayElements
+  , releaseByteArrayElements
+  , releaseCharArrayElements
+  , releaseShortArrayElements
+  , releaseIntArrayElements
+  , releaseLongArrayElements
+  , releaseFloatArrayElements
+  , releaseDoubleArrayElements
+  , releaseStringChars
+  , getObjectArrayElement
+  , setObjectArrayElement
+    -- * Thread management
+  , attachCurrentThreadAsDaemon
+  , detachCurrentThread
+  , runInAttachedThread
+  ) where
+
+import Control.Concurrent (isCurrentThreadBound, rtsSupportsBoundThreads)
+import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, takeMVar)
+import Control.Exception (Exception, bracket, bracket_, catch, finally, throwIO)
+import Control.Monad (join, unless, void, when)
+import Data.Choice
+import Data.Coerce
+import Data.Int
+import Data.IORef (IORef, newIORef, atomicModifyIORef)
+import Data.Word
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import Data.Monoid ((<>))
+import Data.Typeable (Typeable)
+import Foreign.C (CChar)
+import Foreign.ForeignPtr
+  ( finalizeForeignPtr
+  , newForeignPtr_
+  , withForeignPtr
+  )
+import Foreign.JNI.Internal
+import Foreign.JNI.NativeMethod
+import Foreign.JNI.Types
+import qualified Foreign.JNI.String as JNI
+import Foreign.Marshal.Alloc (alloca)
+import Foreign.Marshal.Array
+import Foreign.Ptr (Ptr, nullPtr)
+import Foreign.Storable (peek)
+import GHC.ForeignPtr (newConcForeignPtr)
+import GHC.Stack (HasCallStack, callStack, getCallStack, prettySrcLoc)
+import qualified Language.C.Inline as C
+import qualified Language.C.Inline.Unsafe as CU
+import System.IO (fixIO)
+import System.IO.Unsafe (unsafePerformIO)
+import Prelude hiding (String)
+import qualified Prelude
+
+C.context (C.baseCtx <> C.bsCtx <> jniCtx)
+
+C.include "<jni.h>"
+C.include "<stdio.h>"
+C.include "<errno.h>"
+C.include "<stdlib.h>"
+
+-- A thread-local variable to cache the JNI environment. Accessing this variable
+-- is faster than calling @jvm->GetEnv()@.
+$(C.verbatim "static __thread JNIEnv* jniEnv; ")
+
+-- | A JNI call may cause a (Java) exception to be raised. This module raises it
+-- as a Haskell exception wrapping the Java exception.
+newtype JVMException = JVMException JThrowable
+  deriving (Show, Typeable)
+
+instance Exception JVMException
+
+-- | Thrown when @Get<PrimitiveType>ArrayElements@ returns a null pointer,
+-- because it wanted to copy the array contents but couldn't. In this case the
+-- JVM doesn't throw OutOfMemory according to the JNI spec.
+data ArrayCopyFailed = ArrayCopyFailed
+  deriving (Exception, Show, Typeable)
+
+-- | A null reference is found where a non-null reference was expected.
+data NullPointerException = NullPointerException
+  deriving (Exception, Show, Typeable)
+
+-- | A JNI call is made from a thread not attached to the JVM.
+data ThreadNotAttached = ThreadNotAttached
+  deriving (Exception, Show, Typeable)
+
+-- | A JNI call is made from an unbound thread.
+data ThreadNotBound = ThreadNotBound
+  deriving (Exception, Show, Typeable)
+
+-- | Thrown when an JNI call is made from an unbound thread.
+data JNIError = JNIError Prelude.String Int32
+  deriving (Show, Typeable)
+
+instance Exception JNIError
+
+-- | Map Java exceptions to Haskell exceptions.
+throwIfException :: Ptr JNIEnv -> IO a -> IO a
+throwIfException env m = m `finally` do
+    excptr <- [CU.exp| jthrowable { (*$(JNIEnv *env))->ExceptionOccurred($(JNIEnv *env)) } |]
+    unless (excptr == nullPtr) $ do
+      [CU.exp| void { (*$(JNIEnv *env))->ExceptionDescribe($(JNIEnv *env)) } |]
+      [CU.exp| void { (*$(JNIEnv *env))->ExceptionClear($(JNIEnv *env)) } |]
+      throwIO . JVMException =<< newGlobalRef =<< objectFromPtr excptr
+
+-- | Check whether a pointer is null.
+throwIfNull :: IO (Ptr a) -> IO (Ptr a)
+throwIfNull m = do
+    ptr <- m
+    if ptr == nullPtr
+    then throwIO ArrayCopyFailed
+    else return ptr
+
+-- | Throws an error if the given reference is null, otherwise performs
+-- the given io action.
+throwIfJNull :: J ty -> IO a -> IO a
+throwIfJNull j io =
+    if j == jnull
+    then throwIO NullPointerException
+    else io
+
+-- | A read-write lock
+--
+-- Concurrent readers are allowed, but only one writer is supported.
+newtype RWLock =
+    RWLock (IORef (Int, RWWantedState))
+    -- ^ A count of the held read locks and the wanted state
+
+-- | The wanted state of the RW
+data RWWantedState
+    = Reading            -- ^ There are no writers
+    | Writing (MVar ())
+       -- ^ A writer wants to write, grant no more read locks. The MVar is used
+       -- to notify the writer when the currently held read locks are released.
+
+-- | Creates a new read-write lock.
+newRWLock :: IO RWLock
+newRWLock = RWLock <$> newIORef (0, Reading)
+
+-- | Tries to acquire a read lock. If this call returns `Do #read`, no writer
+-- will be granted a lock before the read lock is released.
+tryAcquireReadLock :: RWLock -> IO (Choice "read")
+tryAcquireReadLock (RWLock ref) = do
+    atomicModifyIORef ref $ \case
+      (!readers,  Reading) -> ((readers + 1, Reading),    Do #read)
+      st                   -> (                       st, Don't #read)
+
+-- | Releases a read lock.
+releaseReadLock :: RWLock -> IO ()
+releaseReadLock (RWLock ref) = do
+    st <- atomicModifyIORef ref $
+            \st@(readers, aim) -> ((readers - 1, aim), st)
+    case st of
+      -- Notify the writer if I'm the last reader.
+      (1, Writing mv) -> putMVar mv ()
+      _               -> return ()
+
+-- | Waits until the current read locks are released and grants a write lock.
+acquireWriteLock :: RWLock -> IO ()
+acquireWriteLock (RWLock ref) = do
+    mv <- newEmptyMVar
+    join $ atomicModifyIORef ref $ \(readers, _) ->
+      ((readers, Writing mv), when (readers > 0) (takeMVar mv))
+
+-- | This lock is used to avoid the JVM from dying before any finalizers
+-- deleting global references are finished.
+--
+-- Finalizers try to acquire read locks.
+--
+-- The JVM acquires a write lock before shutdown. Thence, finalizers fail to
+-- acquire read locks and behave as noops.
+globalJVMLock :: RWLock
+globalJVMLock = unsafePerformIO newRWLock
+{-# NOINLINE globalJVMLock #-}
+
+throwIfNotOK_ :: HasCallStack => IO Int32 -> IO ()
+throwIfNotOK_ m = m >>= \case
+  rc
+    | rc == [CU.pure| jint { JNI_OK } |] -> return ()
+    | rc == [CU.pure| jint { JNI_EDETACHED } |] -> throwIO ThreadNotAttached
+    | otherwise -> throwIO $ JNIError (prettySrcLoc loc) rc
+  where
+    (_, loc):_ = getCallStack callStack
+
+attachCurrentThreadAsDaemon :: IO ()
+attachCurrentThreadAsDaemon = do
+    throwIfNotOK_
+      [CU.exp| jint {
+        (*$(JavaVM* jvm))->AttachCurrentThreadAsDaemon($(JavaVM* jvm), &jniEnv, NULL)
+      } |]
+
+detachCurrentThread :: IO ()
+detachCurrentThread =
+    throwIfNotOK_
+    [CU.block| jint {
+      int rc = (*$(JavaVM* jvm))->DetachCurrentThread($(JavaVM* jvm));
+      if (rc == JNI_OK)
+        jniEnv = NULL;
+      return rc;
+    } |]
+
+-- | Attaches the calling thread to the JVM, runs the given IO action and
+-- then detaches the thread.
+--
+-- If the thread is already attached no attaching and detaching is performed.
+runInAttachedThread :: IO a -> IO a
+runInAttachedThread io = do
+    attached <-
+      catch (getJNIEnv >> return True) (\ThreadNotBound -> return False)
+    if attached
+    then io
+    else bracket_
+          attachCurrentThreadAsDaemon
+          detachCurrentThread
+          io
+
+-- | The current JVM
+--
+-- Assumes there's at most one JVM. The current JNI spec (2016) says only
+-- one JVM per process is supported anyways.
+{-# NOINLINE jvm #-}
+jvm :: Ptr JVM
+jvm = unsafePerformIO $ alloca $ \pjvm -> alloca $ \pnum_jvms -> do
+    throwIfNotOK_
+      [CU.exp| jint {
+        JNI_GetCreatedJavaVMs($(JavaVM** pjvm), 1, $(jsize* pnum_jvms))
+      }|]
+    num_jvms <- peek pnum_jvms
+    when (num_jvms == 0) $
+      fail "JNI_GetCreatedJavaVMs: No JVM has been initialized yet."
+    when (num_jvms > 1) $
+      fail "JNI_GetCreatedJavaVMs: There are multiple JVMs but only one is supported."
+    peek pjvm
+
+-- | Yields the JNIEnv of the calling thread.
+--
+-- Yields @Nothing@ if the calling thread is not attached to the JVM.
+getJNIEnv :: IO (Ptr JNIEnv)
+getJNIEnv = [CU.exp| JNIEnv* { jniEnv } |] >>= \case
+    env | env == nullPtr -> do
+      throwIfNotOK_
+        [CU.exp| jint {
+          (*$(JavaVM* jvm))->GetEnv($(JavaVM* jvm), (void**)&jniEnv, JNI_VERSION_1_6)
+        }|]
+      [CU.exp| JNIEnv* { jniEnv } |]
+    env -> return env
+
+-- | Run an action against the appropriate 'JNIEnv'.
+--
+-- Each OS thread has its own 'JNIEnv', which this function gives access to.
+withJNIEnv :: (Ptr JNIEnv -> IO a) -> IO a
+withJNIEnv f = getJNIEnv >>= f
+
+useAsCStrings :: [ByteString] -> ([Ptr CChar] -> IO a) -> IO a
+useAsCStrings strs m =
+  foldr (\str k cstrs -> BS.useAsCString str $ \cstr -> k (cstr:cstrs)) m strs []
+
+-- | Create a new JVM, with the given arguments. /Can only be called once/. Best
+-- practice: use 'withJVM' instead. Only useful for GHCi.
+newJVM :: [ByteString] -> IO JVM
+newJVM options = JVM_ <$> do
+    useAsCStrings options $ \cstrs -> do
+      withArray cstrs $ \(coptions :: Ptr (Ptr CChar)) -> do
+        let n = fromIntegral (length cstrs) :: C.CInt
+        checkBoundness
+        [C.block| JavaVM * {
+          JavaVM *jvm;
+          JavaVMInitArgs vm_args;
+          JavaVMOption *options = malloc(sizeof(JavaVMOption) * $(int n));
+          for(int i = 0; i < $(int n); i++)
+                  options[i].optionString = $(char **coptions)[i];
+          vm_args.version = JNI_VERSION_1_6;
+          vm_args.nOptions = $(int n);
+          vm_args.options = options;
+          vm_args.ignoreUnrecognized = 0;
+          JNI_CreateJavaVM(&jvm, (void**)&jniEnv, &vm_args);
+          free(options);
+          return jvm; } |]
+
+  where
+    checkBoundness :: IO ()
+    checkBoundness = when rtsSupportsBoundThreads $ do
+      bound <- isCurrentThreadBound
+      unless bound (throwIO ThreadNotBound)
+
+-- | Deallocate a 'JVM' created using 'newJVM'.
+destroyJVM :: JVM -> IO ()
+destroyJVM (JVM_ jvm) = do
+    acquireWriteLock globalJVMLock
+    [C.block| void {
+        (*$(JavaVM *jvm))->DestroyJavaVM($(JavaVM *jvm));
+        jniEnv = NULL;
+    } |]
+
+-- | Create a new JVM, with the given arguments. Destroy it once the given
+-- action completes. /Can only be called once/. Best practice: use it to wrap
+-- your @main@ function.
+withJVM :: [ByteString] -> IO a -> IO a
+withJVM options action = bracket (newJVM options) destroyJVM (const action)
+
+defineClass
+  :: Coercible o (J ('Class "java.lang.ClassLoader"))
+  => ReferenceTypeName -- ^ Class name
+  -> o -- ^ Loader
+  -> ByteString -- ^ Bytecode buffer
+  -> IO JClass
+defineClass (coerce -> name) (coerce -> upcast -> loader) buf = withJNIEnv $ \env ->
+    throwIfException env $
+    JNI.withString name $ \namep ->
+    objectFromPtr =<<
+    [CU.exp| jclass {
+      (*$(JNIEnv *env))->DefineClass($(JNIEnv *env),
+                                     $(char *namep),
+                                     $fptr-ptr:(jobject loader),
+                                     $bs-ptr:buf,
+                                     $bs-len:buf) } |]
+registerNatives
+  :: JClass
+  -> [JNINativeMethod]
+  -> IO ()
+registerNatives cls methods = withJNIEnv $ \env ->
+    throwIfException env $
+    withArray methods $ \cmethods -> do
+      let numMethods = fromIntegral $ length methods
+      _ <- [CU.exp| jint {
+             (*$(JNIEnv *env))->RegisterNatives($(JNIEnv *env),
+                                                $fptr-ptr:(jclass cls),
+                                                $(JNINativeMethod *cmethods),
+                                                $(int numMethods)) } |]
+      return ()
+
+throw :: Coercible o (J a) => o -> IO ()
+throw (coerce -> upcast -> obj) = withJNIEnv $ \env -> void $ do
+    [CU.exp| jint {
+       (*$(JNIEnv *env))->Throw($(JNIEnv *env),
+                                $fptr-ptr:(jobject obj)) } |]
+
+throwNew :: JClass -> JNI.String -> IO ()
+throwNew cls msg = throwIfJNull cls $ withJNIEnv $ \env ->
+    JNI.withString msg $ \msgp -> void $ do
+    [CU.exp| jint {
+       (*$(JNIEnv *env))->ThrowNew($(JNIEnv *env),
+                                   $fptr-ptr:(jclass cls),
+                                   $(char *msgp)) } |]
+
+findClass
+  :: ReferenceTypeName -- ^ Class name
+  -> IO JClass
+findClass (coerce -> name) = withJNIEnv $ \env ->
+    throwIfException env $
+    JNI.withString name $ \namep ->
+    objectFromPtr =<<
+    [CU.exp| jclass { (*$(JNIEnv *env))->FindClass($(JNIEnv *env), $(char *namep)) } |]
+
+newObject :: JClass -> MethodSignature -> [JValue] -> IO JObject
+newObject cls (coerce -> sig) args = throwIfJNull cls $ withJNIEnv $ \env ->
+    throwIfException env $
+    withJValues args $ \cargs -> do
+      constr <- getMethodID cls "<init>" sig
+      objectFromPtr =<< [CU.exp| jobject {
+        (*$(JNIEnv *env))->NewObjectA($(JNIEnv *env),
+                                      $fptr-ptr:(jclass cls),
+                                      $(jmethodID constr),
+                                      $(jvalue *cargs)) } |]
+
+getFieldID
+  :: JClass -- ^ A class object as returned by 'findClass'
+  -> JNI.String -- ^ Field name
+  -> Signature -- ^ JNI signature
+  -> IO JFieldID
+getFieldID cls fieldname (coerce -> sig) = throwIfJNull cls $
+    withJNIEnv $ \env ->
+    throwIfException env $
+    JNI.withString fieldname $ \fieldnamep ->
+    JNI.withString sig $ \sigp ->
+    [CU.exp| jfieldID {
+      (*$(JNIEnv *env))->GetFieldID($(JNIEnv *env),
+                                    $fptr-ptr:(jclass cls),
+                                    $(char *fieldnamep),
+                                    $(char *sigp)) } |]
+
+getStaticFieldID
+  :: JClass -- ^ A class object as returned by 'findClass'
+  -> JNI.String -- ^ Field name
+  -> Signature -- ^ JNI signature
+  -> IO JFieldID
+getStaticFieldID cls fieldname (coerce -> sig) = throwIfJNull cls $
+    withJNIEnv $ \env ->
+    throwIfException env $
+    JNI.withString fieldname $ \fieldnamep ->
+    JNI.withString sig $ \sigp ->
+    [CU.exp| jfieldID {
+      (*$(JNIEnv *env))->GetStaticFieldID($(JNIEnv *env),
+                                          $fptr-ptr:(jclass cls),
+                                          $(char *fieldnamep),
+                                          $(char *sigp)) } |]
+
+#define GET_FIELD(name, hs_rettype, c_rettype) \
+get/**/name/**/Field :: Coercible o (J a) => o -> JFieldID -> IO hs_rettype; \
+get/**/name/**/Field (coerce -> upcast -> obj) field = withJNIEnv $ \env -> \
+    throwIfException env $ \
+    [CU.exp| c_rettype { \
+      (*$(JNIEnv *env))->Get/**/name/**/Field($(JNIEnv *env), \
+                                              $fptr-ptr:(jobject obj), \
+                                              $(jfieldID field)) } |]
+
+getObjectField :: Coercible o (J a) => o -> JFieldID -> IO JObject
+getObjectField x y =
+    let GET_FIELD(Object, (Ptr JObject), jobject)
+    in objectFromPtr =<< getObjectField x y
+GET_FIELD(Boolean, Word8, jboolean)
+GET_FIELD(Byte, CChar, jbyte)
+GET_FIELD(Char, Word16, jchar)
+GET_FIELD(Short, Int16, jshort)
+GET_FIELD(Int, Int32, jint)
+GET_FIELD(Long, Int64, jlong)
+GET_FIELD(Float, Float, jfloat)
+GET_FIELD(Double, Double, jdouble)
+
+#define GET_STATIC_FIELD(name, hs_rettype, c_rettype) \
+getStatic/**/name/**/Field :: JClass -> JFieldID -> IO hs_rettype; \
+getStatic/**/name/**/Field klass field = throwIfJNull klass $ \
+    withJNIEnv $ \env -> \
+    throwIfException env $ \
+    [CU.exp| c_rettype { \
+      (*$(JNIEnv *env))->GetStatic/**/name/**/Field($(JNIEnv *env), \
+                                                    $fptr-ptr:(jclass klass), \
+                                                    $(jfieldID field)) } |]
+
+getStaticObjectField :: JClass -> JFieldID -> IO JObject
+getStaticObjectField x y =
+    let GET_STATIC_FIELD(Object, (Ptr JObject), jobject)
+    in objectFromPtr =<< getStaticObjectField x y
+GET_STATIC_FIELD(Boolean, Word8, jboolean)
+GET_STATIC_FIELD(Byte, CChar, jbyte)
+GET_STATIC_FIELD(Char, Word16, jchar)
+GET_STATIC_FIELD(Short, Int16, jshort)
+GET_STATIC_FIELD(Int, Int32, jint)
+GET_STATIC_FIELD(Long, Int64, jlong)
+GET_STATIC_FIELD(Float, Float, jfloat)
+GET_STATIC_FIELD(Double, Double, jdouble)
+
+#define SET_FIELD(name, hs_fieldtype, c_fieldtype) \
+set/**/name/**/Field :: Coercible o (J a) => o -> JFieldID -> hs_fieldtype -> IO (); \
+set/**/name/**/Field (coerce -> upcast -> obj) field x = \
+    withJNIEnv $ \env -> \
+    throwIfException env $ \
+    [CU.block| void { \
+      (*$(JNIEnv *env))->Set/**/name/**/Field($(JNIEnv *env), \
+                                              $fptr-ptr:(jobject obj), \
+                                              $(jfieldID field), \
+                                              $(c_fieldtype x)); } |]
+
+setObjectField :: Coercible o (J a) => o -> JFieldID -> JObject -> IO ()
+setObjectField x y z =
+    let SET_FIELD(Object, (Ptr JObject), jobject)
+    in withForeignPtr (coerce z) (setObjectField x y)
+SET_FIELD(Boolean, Word8, jboolean)
+SET_FIELD(Byte, CChar, jbyte)
+SET_FIELD(Char, Word16, jchar)
+SET_FIELD(Short, Int16, jshort)
+SET_FIELD(Int, Int32, jint)
+SET_FIELD(Long, Int64, jlong)
+SET_FIELD(Float, Float, jfloat)
+SET_FIELD(Double, Double, jdouble)
+
+#define SET_STATIC_FIELD(name, hs_fieldtype, c_fieldtype) \
+setStatic/**/name/**/Field :: JClass -> JFieldID -> hs_fieldtype -> IO (); \
+setStatic/**/name/**/Field klass field x = throwIfJNull klass $ \
+    withJNIEnv $ \env -> \
+    throwIfException env $ \
+    [CU.block| void { \
+      (*$(JNIEnv *env))->SetStatic/**/name/**/Field($(JNIEnv *env), \
+                                                    $fptr-ptr:(jclass klass), \
+                                                    $(jfieldID field), \
+                                                    $(c_fieldtype x)); } |]
+
+setStaticObjectField :: JClass -> JFieldID -> JObject -> IO ()
+setStaticObjectField x y z =
+    let SET_STATIC_FIELD(Object, (Ptr JObject), jobject)
+    in withForeignPtr (coerce z) (setStaticObjectField x y)
+SET_STATIC_FIELD(Boolean, Word8, jboolean)
+SET_STATIC_FIELD(Byte, CChar, jbyte)
+SET_STATIC_FIELD(Char, Word16, jchar)
+SET_STATIC_FIELD(Short, Int16, jshort)
+SET_STATIC_FIELD(Int, Int32, jint)
+SET_STATIC_FIELD(Long, Int64, jlong)
+SET_STATIC_FIELD(Float, Float, jfloat)
+SET_STATIC_FIELD(Double, Double, jdouble)
+
+getMethodID
+  :: JClass -- ^ A class object as returned by 'findClass'
+  -> JNI.String -- ^ Field name
+  -> MethodSignature -- ^ JNI signature
+  -> IO JMethodID
+getMethodID cls methodname (coerce -> sig) = throwIfJNull cls $
+    withJNIEnv $ \env ->
+    throwIfException env $
+    JNI.withString methodname $ \methodnamep ->
+    JNI.withString sig $ \sigp ->
+    [CU.exp| jmethodID {
+      (*$(JNIEnv *env))->GetMethodID($(JNIEnv *env),
+                                     $fptr-ptr:(jclass cls),
+                                     $(char *methodnamep),
+                                     $(char *sigp)) } |]
+
+getStaticMethodID
+  :: JClass -- ^ A class object as returned by 'findClass'
+  -> JNI.String -- ^ Field name
+  -> MethodSignature -- ^ JNI signature
+  -> IO JMethodID
+getStaticMethodID cls methodname (coerce -> sig) = throwIfJNull cls $
+    withJNIEnv $ \env ->
+    throwIfException env $
+    JNI.withString methodname $ \methodnamep ->
+    JNI.withString sig $ \sigp ->
+    [CU.exp| jmethodID {
+      (*$(JNIEnv *env))->GetStaticMethodID($(JNIEnv *env),
+                                           $fptr-ptr:(jclass cls),
+                                           $(char *methodnamep),
+                                           $(char *sigp)) } |]
+
+getObjectClass :: Coercible o (J ty) => o -> IO JClass
+getObjectClass (coerce -> upcast -> obj) = throwIfJNull obj $
+    withJNIEnv $ \env ->
+    objectFromPtr =<<
+    [CU.exp| jclass {
+      (*$(JNIEnv *env))->GetObjectClass($(JNIEnv *env),
+                                        $fptr-ptr:(jobject obj)) } |]
+
+-- | Creates a global reference to the object referred to by
+-- the given reference.
+--
+-- Arranges for a finalizer to call 'deleteGlobalRef' when the
+-- global reference is no longer reachable on the Haskell side.
+newGlobalRef :: Coercible o (J ty) => o -> IO o
+newGlobalRef (coerce -> upcast -> obj) = withJNIEnv $ \env -> do
+    gobj <-
+      [CU.exp| jobject {
+        (*$(JNIEnv *env))->NewGlobalRef($(JNIEnv *env),
+                                        $fptr-ptr:(jobject obj)) } |]
+    fixIO $ \j ->
+      coerce <$> J <$> newConcForeignPtr gobj (deleteGlobalRefNonFinalized j)
+
+deleteGlobalRef :: Coercible o (J ty) => o -> IO ()
+deleteGlobalRef (coerce -> J p) = finalizeForeignPtr p
+
+-- | Like 'newGlobalRef' but it doesn't attach a finalizer to destroy
+-- the reference when it is not longer reachable. Use
+-- 'deleteGlobalRefNonFinalized' to destroy this reference.
+newGlobalRefNonFinalized :: Coercible o (J ty) => o -> IO o
+newGlobalRefNonFinalized (coerce -> upcast -> obj) = withJNIEnv $ \env -> do
+    gobj <-
+      [CU.exp| jobject {
+        (*$(JNIEnv *env))->NewGlobalRef($(JNIEnv *env),
+                                        $fptr-ptr:(jobject obj)) } |]
+    coerce <$> J <$> newForeignPtr_ gobj
+
+-- | Like 'deleteGlobalRef' but it can be used only on references created with
+-- 'newGlobalRefNonFinalized'.
+deleteGlobalRefNonFinalized :: Coercible o (J ty) => o -> IO ()
+deleteGlobalRefNonFinalized (coerce -> upcast -> obj) = do
+    bracket (tryAcquireReadLock globalJVMLock)
+            (\doRead -> when (toBool doRead) $ releaseReadLock globalJVMLock)
+            $ \doRead ->
+      when (toBool doRead) $ withJNIEnv $ \env -> do
+        [CU.block| void { (*$(JNIEnv *env))->DeleteGlobalRef($(JNIEnv *env)
+                                                            ,$fptr-ptr:(jobject obj));
+                        } |]
+
+-- NB: Cannot add a finalizer to local references because it may
+-- run in a thread where the reference is not valid.
+newLocalRef :: Coercible o (J ty) => o -> IO o
+newLocalRef (coerce -> upcast -> obj) = withJNIEnv $ \env ->
+    coerce <$> (objectFromPtr =<<)
+    [CU.exp| jobject {
+      (*$(JNIEnv *env))->NewLocalRef($(JNIEnv *env),
+                                     $fptr-ptr:(jobject obj)) } |]
+
+deleteLocalRef :: Coercible o (J ty) => o -> IO ()
+deleteLocalRef (coerce -> upcast -> obj) = withJNIEnv $ \env ->
+    [CU.exp| void {
+      (*$(JNIEnv *env))->DeleteLocalRef($(JNIEnv *env),
+                                        $fptr-ptr:(jobject obj)) } |]
+
+pushLocalFrame :: Int32 -> IO ()
+pushLocalFrame (coerce -> capacity) = withJNIEnv $ \env ->
+    -- We ignore the output as it is always 0 on success and throws an
+    -- exception otherwise.
+    throwIfException env $ void $
+    [CU.block| jint {
+      (*$(JNIEnv *env))->PushLocalFrame($(JNIEnv *env),
+                                        $(jint capacity)); } |]
+
+popLocalFrame :: Coercible o (J ty) => o -> IO o
+popLocalFrame (coerce -> upcast -> obj) = withJNIEnv $ \env ->
+    coerce <$> (objectFromPtr =<<)
+    [CU.exp| jobject {
+      (*$(JNIEnv *env))->PopLocalFrame($(JNIEnv *env),
+                                       $fptr-ptr:(jobject obj)) } |]
+
+-- Modern CPP does have ## for concatenating strings, but we use the hacky /**/
+-- comment syntax for string concatenation. This is because GHC passes
+-- the -traditional flag to the preprocessor by default, which turns off several
+-- modern CPP features.
+
+#define CALL_METHOD(name, hs_rettype, c_rettype) \
+call/**/name/**/Method :: Coercible o (J a) => o -> JMethodID -> [JValue] -> IO hs_rettype; \
+call/**/name/**/Method (coerce -> upcast -> obj) method args = withJNIEnv $ \env -> \
+    throwIfException env $ \
+    withJValues args $ \cargs -> \
+    [C.exp| c_rettype { \
+      (*$(JNIEnv *env))->Call/**/name/**/MethodA($(JNIEnv *env), \
+                                                 $fptr-ptr:(jobject obj), \
+                                                 $(jmethodID method), \
+                                                 $(jvalue *cargs)) } |]
+
+CALL_METHOD(Void, (), void)
+callObjectMethod :: Coercible o (J a) => o -> JMethodID -> [JValue] -> IO JObject
+callObjectMethod x y z =
+    let CALL_METHOD(Object, (Ptr JObject), jobject)
+    in objectFromPtr =<< callObjectMethod x y z
+callBooleanMethod :: Coercible o (J a) => o -> JMethodID -> [JValue] -> IO Bool
+callBooleanMethod x y z =
+    let CALL_METHOD(Boolean, Word8, jboolean)
+    in toEnum . fromIntegral <$> callBooleanMethod x y z
+CALL_METHOD(Byte, CChar, jbyte)
+CALL_METHOD(Char, Word16, jchar)
+CALL_METHOD(Short, Int16, jshort)
+CALL_METHOD(Int, Int32, jint)
+CALL_METHOD(Long, Int64, jlong)
+CALL_METHOD(Float, Float, jfloat)
+CALL_METHOD(Double, Double, jdouble)
+
+#define CALL_STATIC_METHOD(name, hs_rettype, c_rettype) \
+callStatic/**/name/**/Method :: JClass -> JMethodID -> [JValue] -> IO hs_rettype; \
+callStatic/**/name/**/Method cls method args = throwIfJNull cls $ \
+    withJNIEnv $ \env -> \
+    throwIfException env $ \
+    withJValues args $ \cargs -> \
+    [C.exp| c_rettype { \
+      (*$(JNIEnv *env))->CallStatic/**/name/**/MethodA($(JNIEnv *env), \
+                                                       $fptr-ptr:(jclass cls), \
+                                                       $(jmethodID method), \
+                                                       $(jvalue *cargs)) } |]
+
+CALL_STATIC_METHOD(Void, (), void)
+callStaticObjectMethod :: JClass -> JMethodID -> [JValue] -> IO JObject
+callStaticObjectMethod x y z =
+    let CALL_STATIC_METHOD(Object, (Ptr JObject), jobject)
+    in objectFromPtr =<< callStaticObjectMethod x y z
+callStaticBooleanMethod :: JClass -> JMethodID -> [JValue] -> IO Bool
+callStaticBooleanMethod x y z =
+    let CALL_STATIC_METHOD(Boolean, Word8, jboolean)
+    in toEnum . fromIntegral <$> callStaticBooleanMethod x y z
+CALL_STATIC_METHOD(Byte, CChar, jbyte)
+CALL_STATIC_METHOD(Char, Word16, jchar)
+CALL_STATIC_METHOD(Short, Int16, jshort)
+CALL_STATIC_METHOD(Int, Int32, jint)
+CALL_STATIC_METHOD(Long, Int64, jlong)
+CALL_STATIC_METHOD(Float, Float, jfloat)
+CALL_STATIC_METHOD(Double, Double, jdouble)
+
+newObjectArray :: Int32 -> JClass -> IO JObjectArray
+newObjectArray sz cls = throwIfJNull cls $ withJNIEnv $ \env ->
+    throwIfException env $
+    objectFromPtr =<<
+    [CU.exp| jobjectArray {
+      (*$(JNIEnv *env))->NewObjectArray($(JNIEnv *env),
+                                        $(jsize sz),
+                                        $fptr-ptr:(jclass cls),
+                                        NULL) } |]
+
+#define NEW_ARRAY(name, c_rettype) \
+new/**/name/**/Array :: Int32 -> IO J/**/name/**/Array; \
+new/**/name/**/Array sz = withJNIEnv $ \env -> \
+    throwIfException env $ \
+    objectFromPtr =<< \
+    [CU.exp| c_rettype/**/Array { \
+      (*$(JNIEnv *env))->New/**/name/**/Array($(JNIEnv *env), \
+                                              $(jsize sz)) } |]
+
+NEW_ARRAY(Boolean, jboolean)
+NEW_ARRAY(Byte, jbyte)
+NEW_ARRAY(Char, jchar)
+NEW_ARRAY(Short, jshort)
+NEW_ARRAY(Int, jint)
+NEW_ARRAY(Long, jlong)
+NEW_ARRAY(Float, jfloat)
+NEW_ARRAY(Double, jdouble)
+
+newString :: Ptr Word16 -> Int32 -> IO JString
+newString ptr len = withJNIEnv $ \env ->
+    throwIfException env $
+    objectFromPtr =<<
+    [CU.exp| jstring {
+      (*$(JNIEnv *env))->NewString($(JNIEnv *env),
+                                   $(jchar *ptr),
+                                   $(jsize len)) } |]
+
+getArrayLength :: Coercible o (JArray a) => o -> IO Int32
+getArrayLength (coerce -> upcast -> array) = throwIfJNull array $
+    withJNIEnv $ \env ->
+    [C.exp| jsize {
+      (*$(JNIEnv *env))->GetArrayLength($(JNIEnv *env),
+                                        $fptr-ptr:(jarray array)) } |]
+
+getStringLength :: JString -> IO Int32
+getStringLength jstr = throwIfJNull jstr $ withJNIEnv $ \env ->
+    [CU.exp| jsize {
+      (*$(JNIEnv *env))->GetStringLength($(JNIEnv *env),
+                                         $fptr-ptr:(jstring jstr)) } |]
+
+#define GET_ARRAY_ELEMENTS(name, hs_rettype, c_rettype) \
+get/**/name/**/ArrayElements :: J/**/name/**/Array -> IO (Ptr hs_rettype); \
+get/**/name/**/ArrayElements (upcast -> array) = throwIfJNull array $ \
+    withJNIEnv $ \env -> \
+    throwIfNull $ \
+    [CU.exp| c_rettype* { \
+      (*$(JNIEnv *env))->Get/**/name/**/ArrayElements($(JNIEnv *env), \
+                                                      $fptr-ptr:(jobject array), \
+                                                      NULL) } |]
+
+GET_ARRAY_ELEMENTS(Boolean, Word8, jboolean)
+GET_ARRAY_ELEMENTS(Byte, CChar, jbyte)
+GET_ARRAY_ELEMENTS(Char, Word16, jchar)
+GET_ARRAY_ELEMENTS(Short, Int16, jshort)
+GET_ARRAY_ELEMENTS(Int, Int32, jint)
+GET_ARRAY_ELEMENTS(Long, Int64, jlong)
+GET_ARRAY_ELEMENTS(Float, Float, jfloat)
+GET_ARRAY_ELEMENTS(Double, Double, jdouble)
+
+getStringChars :: JString -> IO (Ptr Word16)
+getStringChars jstr = throwIfJNull jstr $ withJNIEnv $ \env ->
+    throwIfNull $
+    [CU.exp| const jchar* {
+      (*$(JNIEnv *env))->GetStringChars($(JNIEnv *env),
+                                        $fptr-ptr:(jstring jstr),
+                                        NULL) } |]
+
+#define SET_ARRAY_REGION(name, hs_argtype, c_argtype) \
+set/**/name/**/ArrayRegion :: J/**/name/**/Array -> Int32 -> Int32 -> Ptr hs_argtype -> IO (); \
+set/**/name/**/ArrayRegion array start len buf = throwIfJNull array $ \
+    withJNIEnv $ \env -> \
+    throwIfException env $ \
+    [CU.exp| void { \
+      (*$(JNIEnv *env))->Set/**/name/**/ArrayRegion($(JNIEnv *env), \
+                                                    $fptr-ptr:(c_argtype/**/Array array), \
+                                                    $(jsize start), \
+                                                    $(jsize len), \
+                                                    $(c_argtype *buf)) } |]
+
+SET_ARRAY_REGION(Boolean, Word8, jboolean)
+SET_ARRAY_REGION(Byte, CChar, jbyte)
+SET_ARRAY_REGION(Char, Word16, jchar)
+SET_ARRAY_REGION(Short, Int16, jshort)
+SET_ARRAY_REGION(Int, Int32, jint)
+SET_ARRAY_REGION(Long, Int64, jlong)
+SET_ARRAY_REGION(Float, Float, jfloat)
+SET_ARRAY_REGION(Double, Double, jdouble)
+
+#define RELEASE_ARRAY_ELEMENTS(name, hs_argtype, c_argtype) \
+release/**/name/**/ArrayElements :: J/**/name/**/Array -> Ptr hs_argtype -> IO (); \
+release/**/name/**/ArrayElements (upcast -> array) xs = throwIfJNull array $ \
+    withJNIEnv $ \env -> \
+    [CU.exp| void { \
+      (*$(JNIEnv *env))->Release/**/name/**/ArrayElements($(JNIEnv *env), \
+                                                          $fptr-ptr:(jobject array), \
+                                                          $(c_argtype *xs), \
+                                                          JNI_ABORT) } |]
+
+RELEASE_ARRAY_ELEMENTS(Boolean, Word8, jboolean)
+RELEASE_ARRAY_ELEMENTS(Byte, CChar, jbyte)
+RELEASE_ARRAY_ELEMENTS(Char, Word16, jchar)
+RELEASE_ARRAY_ELEMENTS(Short, Int16, jshort)
+RELEASE_ARRAY_ELEMENTS(Int, Int32, jint)
+RELEASE_ARRAY_ELEMENTS(Long, Int64, jlong)
+RELEASE_ARRAY_ELEMENTS(Float, Float, jfloat)
+RELEASE_ARRAY_ELEMENTS(Double, Double, jdouble)
+
+releaseStringChars :: JString -> Ptr Word16 -> IO ()
+releaseStringChars jstr chars = throwIfJNull jstr $ withJNIEnv $ \env ->
+    [CU.exp| void {
+      (*$(JNIEnv *env))->ReleaseStringChars($(JNIEnv *env),
+                                            $fptr-ptr:(jstring jstr),
+                                            $(jchar *chars)) } |]
+
+getObjectArrayElement
+  :: forall a o.
+     (IsReferenceType a, Coercible o (J a))
+  => JArray a
+  -> Int32
+  -> IO o
+getObjectArrayElement (arrayUpcast -> array) i = throwIfJNull array $
+    withJNIEnv $ \env ->
+    ( (coerce :: J a -> o)
+    . (unsafeCast :: JObject -> J a)
+    ) <$> (objectFromPtr =<<)
+    [C.exp| jobject {
+      (*$(JNIEnv *env))->GetObjectArrayElement($(JNIEnv *env),
+                                               $fptr-ptr:(jarray array),
+                                               $(jsize i)) } |]
+
+setObjectArrayElement
+  :: forall a o.
+     (IsReferenceType a, Coercible o (J a))
+  => JArray a
+  -> Int32
+  -> o
+  -> IO ()
+setObjectArrayElement (arrayUpcast -> array)
+                      i
+                      ((coerce :: o -> J a) -> upcast -> x) =
+    throwIfJNull array $
+    withJNIEnv $ \env ->
+    [C.exp| void {
+      (*$(JNIEnv *env))->SetObjectArrayElement($(JNIEnv *env),
+                                               $fptr-ptr:(jobjectArray array),
+                                               $(jsize i),
+                                               $fptr-ptr:(jobject x)); } |]
