Merge: Nitsmell : Adding new code smells and print console updated
[nit.git] / lib / jvm.nit
index 62cebe1..692a1c9 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Java Virtual Machine services
+# Java Virtual Machine invocation API and others services from the JNI C API
+#
+# Users of this module and the Java FFI, on desktop computers, must define three environment variables:
+# * `JAVA_HOME` points to the installation folder of the target Java VM.
+#   This folder should contain the JNI header file `include/jni.h`.
+#   e.g. `/usr/lib/jvm/default-java` on Debian Jessie.
+# * `JNI_LIB_PATH` points to the folder with `libjvm.so`.
+#   e.g. `/usr/lib/jvm/default-java/jre/lib/amd64/server/` on Debian Jessie.
+# * `LD_LIBRARY_PATH` has the path to the folder with `libjvm.so`.
+#   It's the same value as `JNI_LIB_PATH` but `LD_LIBRARY_PATH` is a colon separated list
+#   which may contain other paths.
 #
 # See: http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html
 module jvm is
@@ -38,17 +48,16 @@ in "C Header" `{
 # var env = builder.jni_env
 # ~~~~
 class JavaVMBuilder
-       super JniEnvRef
 
        # Version code of the JVM requested by `create_jvm`
        #
-       # Default at 0x00010002
+       # Default at 0x00010002 for `JNI_VERSION_1_2`.
        var version = 0x00010002 is writable
 
        # Additional option strings
        var options = new Array[String]
 
-       # Create the JVM and return it on success
+       # Create a JVM instance, or return `null` on error
        fun create_jvm: nullable JavaVM
        do
                var args = new JavaVMInitArgs
@@ -65,7 +74,7 @@ class JavaVMBuilder
 
                args.options = c_options
 
-               var jvm = new JavaVM(args, self)
+               var jvm = new JavaVM(args)
 
                args.free
                c_options.free
@@ -99,15 +108,15 @@ private extern class JavaVMInitArgs `{ JavaVMInitArgs* `}
 end
 
 private extern class JavaVMOption `{ JavaVMOption* `}
-       fun string: String import NativeString.to_s `{
-               return NativeString_to_s((char*)self->optionString);
+       fun string: String import CString.to_s `{
+               return CString_to_s((char*)self->optionString);
        `}
        fun string=(v: String) import String.to_cstring `{
                self->optionString = String_to_cstring(v);
        `}
 
-       fun extra_info: String import NativeString.to_s `{
-               return NativeString_to_s((char*)self->extraInfo);
+       fun extra_info: String import CString.to_s `{
+               return CString_to_s((char*)self->extraInfo);
        `}
        fun extra_info=(v: String) import String.to_cstring `{
                self->extraInfo = String_to_cstring(v);
@@ -122,11 +131,12 @@ end
 
 # Represents a jni JavaVM
 extern class JavaVM `{JavaVM *`}
-       # Create the JVM, returns its handle and store the a pointer to JniEnv in `env_ref`
+       # Create the JVM
        #
-       # Unavailable on Android, where you cannot instanciate a new JVM.
-       private new(args: JavaVMInitArgs, env_ref: JniEnvRef)
-       import jni_error, JniEnvRef.jni_env=, JniEnv.as nullable `{
+       # The corresponding `JniEnv` can be obtained by calling `env`.
+       #
+       # Unavailable on some platforms, including Android where you cannot instanciate a new JVM.
+       private new(args: JavaVMInitArgs) import jni_error `{
 
        #ifdef ANDROID
                JavaVM_jni_error(NULL, "JVM creation not supported on Android", 0);
@@ -139,36 +149,43 @@ extern class JavaVM `{JavaVM *`}
 
                res = JNI_CreateJavaVM(&jvm, (void**)&env, args);
 
-               if (res != 0) {
+               if (res != JNI_OK) {
                        JavaVM_jni_error(NULL, "Could not create Java VM", res);
                        return NULL;
                }
-               else {
-                       JniEnvRef_jni_env__assign(env_ref, JniEnv_as_nullable_JniEnv(env));
-                       return jvm;
-               }
+
+               return jvm;
        `}
 
-       private fun jni_error(msg: NativeString, v: Int)
+       private fun jni_error(msg: CString, v: Int)
        do
                print "JNI Error: {msg} ({v})"
                abort
        end
 
+       # Unload the Java VM when the calling thread is the only remaining non-daemon attached user thread
        fun destroy `{
                (*self)->DestroyJavaVM(self);
        `}
 
+       # `JniEnv` attached to the calling thread
+       #
+       # A null pointer is returned if the calling thread is not attached to the JVM.
        fun env: JniEnv import jni_error `{
                JNIEnv *env;
                int res = (*self)->GetEnv(self, (void **)&env, JNI_VERSION_1_6);
-               if (res != JNI_OK) {
+               if (res == JNI_EDETACHED) {
+                       JavaVM_jni_error(NULL, "Requesting JNIEnv from an unattached thread", res);
+                       return NULL;
+               }
+               else if (res != JNI_OK) {
                        JavaVM_jni_error(NULL, "Could not get JNIEnv from Java VM", res);
                        return NULL;
                }
                return env;
        `}
 
+       # Attach the calling thread to the JVM and return its `JniEnv`
        fun attach_current_thread: JniEnv import jni_error `{
                JNIEnv *env;
        #ifdef ANDROID
@@ -183,6 +200,14 @@ extern class JavaVM `{JavaVM *`}
                }
                return env;
        `}
+
+       # Detach the calling thread from this JVM
+       fun detach_current_thread import jni_error `{
+               int res = (*self)->DetachCurrentThread(self);
+               if (res != JNI_OK) {
+                       JavaVM_jni_error(NULL, "Could not detach current thread to Java VM", res);
+               }
+       `}
 end
 
 # Represents a jni JNIEnv, which is a thread in a JavaVM
@@ -262,8 +287,8 @@ extern class JniEnv `{JNIEnv *`}
                return res;
        `}
 
-       # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a NativeString
-       fun call_string_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): NativeString import convert_args_to_jni `{
+       # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a CString
+       fun call_string_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): CString import convert_args_to_jni `{
                jvalue * args_tab = JniEnv_convert_args_to_jni(self, args);
                jobject jobj = (*self)->CallObjectMethod(self, obj, method_id, args_tab);
                free(args_tab);
@@ -402,11 +427,6 @@ extern class JniEnv `{JNIEnv *`}
        `}
 end
 
-# used to initialize a JavaVM
-class JniEnvRef
-       var jni_env: nullable JniEnv = null
-end
-
 # Represents a jni jclass
 extern class JClass `{jclass`}
 end
@@ -421,16 +441,16 @@ end
 
 # Represents a jni JNINNativeMethod
 extern class JNINativeMethod `{ JNINativeMethod* `}
-       fun name: String import NativeString.to_s `{
-               return NativeString_to_s((void*)self->name);
+       fun name: String import CString.to_s `{
+               return CString_to_s((void*)self->name);
        `}
 
        fun name=(name: String) import String.to_cstring `{
                self->name = String_to_cstring(name);
        `}
 
-       fun signature: String import NativeString.to_s `{
-               return NativeString_to_s((void*)self->signature);
+       fun signature: String import CString.to_s `{
+               return CString_to_s((void*)self->signature);
        `}
 
        fun signature=(signature: String) import String.to_cstring `{
@@ -510,7 +530,7 @@ redef class Bool
        `}
 end
 
-redef class NativeString
+redef class CString
        redef fun to_jvalue(env)`{
                jvalue value;
                value.l = (*env)->NewStringUTF(env, self);