5fafd18a0a3d45775cb84e69e9e8b242b216fab2
[nit.git] / lib / java / ffi_support.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Core supporting services for the FFI with Java
16 #
17 # This module *must* be imported by modules using the Java FFI.
18 # Some might prefer to import the whole `java` package as it provides
19 # other useful services.
20 module ffi_support is
21 cflags "-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
22 ldflags "-L $(JNI_LIB_PATH) -ljvm"
23 new_annotation extra_java_files
24 end
25
26 import jvm
27
28 redef class Sys
29 private var jvm_cache: nullable JavaVM = null
30 private var jni_env_cache: nullable JniEnv = null
31
32 # Default Java Virtual Machine to use
33 #
34 # Instantiated using `create_default_jvm` if not already set.
35 fun jvm: JavaVM
36 do
37 if jvm_cache == null then create_default_jvm
38 return jvm_cache.as(not null)
39 end
40
41 # Sets the current default Java Virtual Machine (use with `jni_env=`)
42 fun jvm=(jvm: JavaVM) do jvm_cache = jvm
43
44 # Current main `JniEnv`
45 fun jni_env: JniEnv
46 do
47 if jni_env_cache == null then create_default_jvm
48 return jni_env_cache.as(not null)
49 end
50
51 # Sets the current default JNI env (use with `jvm=`)
52 fun jni_env=(jni_env: JniEnv) do jni_env_cache = jni_env
53
54 # Called by `jvm` and `jni_env` to instantiate a Java Virtual Machine.
55 # Used mostly for the FFI with Java.
56 protected fun create_default_jvm
57 do
58 var builder = new JavaVMBuilder
59
60 # By default, look for Java classes in a jar file the same directory as the executable
61 builder.options.add "-Djava.class.path={sys.program_name}.jar"
62
63 var jvm = builder.create_jvm
64 assert jvm != null else print "JVM creation failed"
65
66 self.jvm = jvm
67 assert not jvm.address_is_null
68 self.jni_env = jvm.env
69 assert not jni_env.address_is_null
70 end
71
72 # Get a Java class by its name from the current `jni_env`
73 fun load_jclass(name: CString): JClass import jni_env `{
74 JNIEnv *nit_ffi_jni_env = Sys_jni_env(self);
75
76 // retrieve the implementation Java class
77 jclass java_class = (*nit_ffi_jni_env)->FindClass(nit_ffi_jni_env, name);
78 if (java_class == NULL) {
79 fprintf(stderr, "Nit FFI with Java error: failed to load class.\\n");
80 (*nit_ffi_jni_env)->ExceptionDescribe(nit_ffi_jni_env);
81 exit(1);
82 }
83
84 return java_class;
85 `}
86 end
87
88 # A standard Java string `java.lang.String`
89 #
90 # Converted to a Nit string using `to_s`, or to a C string with `to_cstring`.
91 # Created using `String::to_java_string` or `CString::to_java_string`.
92 extern class JavaString in "Java" `{ java.lang.String `}
93 super JavaObject
94
95 # Get the string from Java and copy it to Nit memory
96 fun to_cstring: CString import sys, Sys.jni_env `{
97 Sys sys = JavaString_sys(self);
98 JNIEnv *env = Sys_jni_env(sys);
99
100 // Get the data from Java
101 const char *java_cstr = (*env)->GetStringUTFChars(env, self, NULL);
102 jsize len = (*env)->GetStringUTFLength(env, self);
103
104 // Copy it in control of Nit
105 char *nit_cstr = (char*)malloc(len+1);
106 memcpy(nit_cstr, java_cstr, len);
107 nit_cstr[len] = '\0';
108
109 // Free JNI ref and return
110 (*env)->ReleaseStringUTFChars(env, self, java_cstr);
111 return nit_cstr;
112 `}
113
114 redef fun to_s
115 do
116 if is_java_null then return "<{inspect_head}:null>"
117 return to_cstring.to_s
118 end
119 end
120
121 redef class CString
122 # Get a Java string from this C string
123 #
124 # This instance is only valid until the next execution of Java code.
125 # You can use `new_local_ref` to keep it longer.
126 fun to_java_string: JavaString import sys, Sys.jni_env `{
127 Sys sys = JavaString_sys(self);
128 JNIEnv *env = Sys_jni_env(sys);
129 return (*env)->NewStringUTF(env, self);
130 `}
131 end
132
133 redef class Text
134 # Get `self` as a `JavaString`
135 fun to_java_string: JavaString do return to_cstring.to_java_string
136 end
137
138 redef extern class JavaObject
139
140 # Returns a global reference to the Java object behind this reference
141 #
142 # You must use a global reference when keeping a Java object
143 # across execution of Java code, per JNI specification.
144 fun new_global_ref: SELF import sys, Sys.jni_env `{
145 Sys sys = JavaObject_sys(self);
146 JNIEnv *env = Sys_jni_env(sys);
147 return (*env)->NewGlobalRef(env, self);
148 `}
149
150 # Delete this global reference
151 fun delete_global_ref import sys, Sys.jni_env `{
152 Sys sys = JavaObject_sys(self);
153 JNIEnv *env = Sys_jni_env(sys);
154 (*env)->DeleteGlobalRef(env, self);
155 `}
156
157 # Delete this local reference
158 fun delete_local_ref import sys, Sys.jni_env `{
159 Sys sys = JavaObject_sys(self);
160 JNIEnv *env = Sys_jni_env(sys);
161 (*env)->DeleteLocalRef(env, self);
162 `}
163
164 # Pops the current local reference frame and return a valid reference to self
165 #
166 # Similar to `JavaVM::pop_local_frame` but returns a value.
167 fun pop_from_local_frame: SELF
168 do
169 var jni_env = sys.jni_env
170 return pop_from_local_frame_with_env(jni_env)
171 end
172
173 # Java implementation of `pop_from_local_frame`
174 protected fun pop_from_local_frame_with_env(jni_env: JniEnv): SELF `{
175 return (*jni_env)->PopLocalFrame(jni_env, self);
176 `}
177
178 # Is `self` null in Java?
179 #
180 # Since Java type system doesn't have the same `nullable` concept as Nit's,
181 # the two systems are not directly compatible. Any Nit instances of
182 # `JavaObject` may hold a Java null.
183 #
184 # To benefit from the safer type system of Nit, it is recommended to check
185 # the return of all extern methods implemented in Java to ensure the value
186 # is not a Java null. In case it is, you should replace it by a normal Nit
187 # `null`.
188 fun is_java_null: Bool in "Java" `{ return self == null; `}
189
190 # `JavaString` representation of `self` using Java's `toString`
191 fun to_java_string: JavaString in "Java" `{ return self.toString(); `}
192
193 # Use Java's `toString` for any `JavaObject`
194 redef fun to_s
195 do
196 if is_java_null then return "<{inspect_head}:null>"
197 return to_java_string.to_s
198 end
199 end
200
201 # Java class: java.lang.Throwable
202 extern class JavaThrowable in "Java" `{ java.lang.Throwable `}
203 super JavaObject
204
205 # Java implementation: java.lang.String java.lang.Throwable.getMessage()
206 fun message: JavaString in "Java" `{
207 return self.getMessage();
208 `}
209
210 # Java implementation: java.lang.String java.lang.Throwable.getLocalizedMessage()
211 fun localized_message: JavaString in "Java" `{
212 return self.getLocalizedMessage();
213 `}
214
215 # Java implementation: java.lang.Throwable.printStackTrace()
216 fun print_stack_trace in "Java" `{
217 self.printStackTrace();
218 `}
219
220 # Java implementation: java.lang.Throwable java.lang.Throwable.getCause()
221 fun cause: JavaThrowable in "Java" `{
222 return self.getCause();
223 `}
224
225 redef fun new_global_ref import sys, Sys.jni_env `{
226 Sys sys = JavaThrowable_sys(self);
227 JNIEnv *env = Sys_jni_env(sys);
228 return (*env)->NewGlobalRef(env, self);
229 `}
230
231 redef fun pop_from_local_frame_with_env(jni_env) `{
232 return (*jni_env)->PopLocalFrame(jni_env, self);
233 `}
234 end
235
236 # Java class: java.lang.Exception
237 extern class JavaException in "Java" `{ java.lang.Exception `}
238 super JavaThrowable
239
240 redef fun new_global_ref import sys, Sys.jni_env `{
241 Sys sys = JavaException_sys(self);
242 JNIEnv *env = Sys_jni_env(sys);
243 return (*env)->NewGlobalRef(env, self);
244 `}
245
246 redef fun pop_from_local_frame_with_env(jni_env) `{
247 return (*jni_env)->PopLocalFrame(jni_env, self);
248 `}
249 end
250
251 redef class JniEnv
252 # Create new `Java.nio.ByteBuffer` referring to `address` with the given `capacity` in bytes
253 #
254 # JNI function: NewDirectByteBuffer
255 fun new_direct_byte_buffer(address: Pointer, capacity: Int): Java_nio_ByteBuffer `{
256 return (*self)->NewDirectByteBuffer(self, address, capacity);
257 `}
258 end
259
260 # Container for data of a specific primitive type
261 #
262 # Java class: java.nio.Buffer
263 extern class Java_nio_Buffer in "Java" `{ java.nio.Buffer `}
264 super JavaObject
265
266 # Address pointed by this buffer
267 #
268 # JNI function: GetDirectBufferAddress
269 fun direct_buffer_address(jni_env: JniEnv): Pointer `{
270 return (*jni_env)->GetDirectBufferAddress(jni_env, self);
271 `}
272
273 # Capacity of this this buffer
274 #
275 # JNI function: GetDirectBufferCapacity
276 fun direct_buffer_capacity(jni_env: JniEnv): Int `{
277 return (*jni_env)->GetDirectBufferCapacity(jni_env, self);
278 `}
279 end
280
281 # A byte buffer
282 #
283 # Java class: java.nio.ByteBuffer
284 extern class Java_nio_ByteBuffer in "Java" `{ java.nio.ByteBuffer `}
285 super Java_nio_Buffer
286
287 # Allocate a new `java.nio.ByteBuffer` with `allocateDirect`
288 new direct(size: Int) in "Java" `{
289 return java.nio.ByteBuffer.allocateDirect((int)size);
290 `}
291 end