java: intro JavaObject::delete_local_ref
[nit.git] / lib / java.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Supporting services for the FFI with Java
18 #
19 # This modules relies on `Sys::jvm`, `Sys::jni_env` and
20 # `Sys::create_default_jvm` to get a handle on a JVM. You can adapt the
21 # behavior of the FFI and services in this module by redefing
22 # `Sys::create_default_jvm` and supply your own JVM object. You can manage
23 # multiple java thread by switching the current environment in a redef
24 # of `Sys::jni_env`, and multiple JVM using `Sys::jvm`.
25 #
26 # The module `jvm` gives more control over the JVM instances and wraps
27 # most of JNI functions. You can use it to further customize the behavior
28 # of your code.
29 module java is
30 c_compiler_option("-I $(JAVA_HOME)/include/")
31 c_linker_option("-L $(JNI_LIB_PATH) -ljvm")
32 end
33
34 import jvm
35
36 redef class Sys
37 private var jvm_cache: nullable JavaVM = null
38 private var jni_env_cache: nullable JniEnv = null
39
40 # Default Java Virtual Machine to use (will be instanciated using
41 # `create_default_jvm` if not already set)
42 fun jvm: JavaVM
43 do
44 if jvm_cache == null then create_default_jvm
45 return jvm_cache.as(not null)
46 end
47
48 # Sets the current default Java Virtual Machine (use with `jni_env=`)
49 fun jvm=(jvm: JavaVM) do jvm_cache = jvm
50
51 # Current main `JniEnv`
52 fun jni_env: JniEnv
53 do
54 if jni_env_cache == null then create_default_jvm
55 return jni_env_cache.as(not null)
56 end
57
58 # Sets the current default JNI env (use with `jvm=`)
59 fun jni_env=(jni_env: JniEnv) do jni_env_cache = jni_env
60
61 # Called by `jvm` and `jni_env` to instanciate a Java Virual Machine.
62 # Used mostly for the FFI with Java.
63 protected fun create_default_jvm
64 do
65 var builder = new JavaVMBuilder
66
67 # By default, look for Java classes in a jar file the same dir as the executable
68 builder.options.add "-Djava.class.path={sys.program_name}.jar"
69
70 var jvm = builder.create_jvm
71 assert jvm != null else print "JVM creation failed"
72
73 self.jvm = jvm
74 self.jni_env = builder.jni_env.as(not null)
75 end
76
77 # Get a Java class by its name from the current `jni_env`
78 fun load_jclass(name: NativeString): JClass import jni_env `{
79 JNIEnv *nit_ffi_jni_env = Sys_jni_env(recv);
80
81 // retreive the implementation Java class
82 jclass java_class = (*nit_ffi_jni_env)->FindClass(nit_ffi_jni_env, name);
83 if (java_class == NULL) {
84 fprintf(stderr, "Nit FFI with Java error: failed to load class.\\n");
85 (*nit_ffi_jni_env)->ExceptionDescribe(nit_ffi_jni_env);
86 exit(1);
87 }
88
89 return java_class;
90 `}
91 end
92
93 # A standard Java string `java.lang.String`
94 #
95 # Converted to a Nit string using `to_s`, or to a C string with `to_cstring`.
96 # Created using `String::to_java_string` or `NativeString::to_java_string`.
97 extern class JavaString in "Java" `{ java.lang.String `}
98 super JavaObject
99
100 redef type SELF: JavaString
101
102 # Get the string from Java and copy it to Nit memory
103 fun to_cstring: NativeString import sys, Sys.jni_env `{
104 Sys sys = JavaString_sys(recv);
105 JNIEnv *env = Sys_jni_env(sys);
106
107 // Get the data from Java
108 const jbyte *java_cstr = (char*)(*env)->GetStringUTFChars(env, recv, NULL);
109 jsize len = (*env)->GetStringUTFLength(env, recv);
110
111 // Copy it in control of Nit
112 char *nit_cstr = (char*)malloc(len+1);
113 memcpy(nit_cstr, java_cstr, len);
114 nit_cstr[len] = '\0';
115
116 // Free JNI ref and return
117 (*env)->ReleaseStringUTFChars(env, recv, java_cstr);
118 return nit_cstr;
119 `}
120
121 redef fun to_s do return to_cstring.to_s
122 end
123
124 redef class NativeString
125 # Get a Java string from this C string
126 #
127 # This instance is only valid until the next execution of Java code.
128 # You can use `new_local_ref` to keep it longer.
129 fun to_java_string: JavaString import sys, Sys.jni_env `{
130 Sys sys = JavaString_sys(recv);
131 JNIEnv *env = Sys_jni_env(sys);
132 return (*env)->NewStringUTF(env, recv);
133 `}
134 end
135
136 redef class String
137 fun to_java_string: JavaString do return to_cstring.to_java_string
138 end
139
140 redef extern class JavaObject
141 type SELF: JavaObject
142
143 # Returns a global reference to the Java object behind this reference
144 #
145 # You must use a global reference when keeping a Java object
146 # across execution of Java code, per JNI specification.
147 fun new_global_ref: SELF import sys, Sys.jni_env `{
148 Sys sys = JavaObject_sys(recv);
149 JNIEnv *env = Sys_jni_env(sys);
150 return (*env)->NewGlobalRef(env, recv);
151 `}
152
153 # Delete this global reference
154 fun delete_global_ref import sys, Sys.jni_env `{
155 Sys sys = JavaObject_sys(recv);
156 JNIEnv *env = Sys_jni_env(sys);
157 (*env)->DeleteGlobalRef(env, recv);
158 `}
159
160 # Delete this local reference
161 fun delete_local_ref import sys, Sys.jni_env `{
162 Sys sys = JavaObject_sys(recv);
163 JNIEnv *env = Sys_jni_env(sys);
164 (*env)->DeleteLocalRef(env, recv);
165 `}
166 end