examples: annotate examples
[nit.git] / lib / jvm.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Romain Chanoir <romain.chanoir@courrier.uqam.ca>
4 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17
18 # Java Virtual Machine invocation API and others services from the JNI C API
19 #
20 # Users of this module and the Java FFI, on desktop computers, must define three environment variables:
21 # * `JAVA_HOME` points to the installation folder of the target Java VM.
22 # This folder should contain the JNI header file `include/jni.h`.
23 # e.g. `/usr/lib/jvm/default-java` on Debian Jessie.
24 # * `JNI_LIB_PATH` points to the folder with `libjvm.so`.
25 # e.g. `/usr/lib/jvm/default-java/jre/lib/amd64/server/` on Debian Jessie.
26 # * `LD_LIBRARY_PATH` has the path to the folder with `libjvm.so`.
27 # It's the same value as `JNI_LIB_PATH` but `LD_LIBRARY_PATH` is a colon separated list
28 # which may contain other paths.
29 #
30 # See: http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html
31 module jvm is
32 cflags "-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
33 ldflags "-L $(JNI_LIB_PATH) -ljvm"
34 end
35
36 in "C Header" `{
37 #include <jni.h>
38 `}
39
40 # Utility to select options to create the VM using `create_jvm`
41 #
42 # Usage example:
43 #
44 # ~~~~nitish
45 # var builder = new JavaVMBuilder
46 # builder.options.add "-Djava.class.path=."
47 # var jvm = builder.create_jvm
48 # var env = builder.jni_env
49 # ~~~~
50 class JavaVMBuilder
51
52 # Version code of the JVM requested by `create_jvm`
53 #
54 # Default at 0x00010002 for `JNI_VERSION_1_2`.
55 var version = 0x00010002 is writable
56
57 # Additional option strings
58 var options = new Array[String]
59
60 # Create a JVM instance, or return `null` on error
61 fun create_jvm: nullable JavaVM
62 do
63 var args = new JavaVMInitArgs
64 args.version = version
65 args.set_default
66 args.n_options = options.length
67
68 var c_options = new JavaVMOptionArray(options.length)
69 for o in options.length.times do
70 var option = options[o]
71 var c_option = c_options[o]
72 c_option.string = option
73 end
74
75 args.options = c_options
76
77 var jvm = new JavaVM(args)
78
79 args.free
80 c_options.free
81
82 if jvm.address_is_null then return null
83 return jvm
84 end
85 end
86
87 private extern class JavaVMInitArgs `{ JavaVMInitArgs* `}
88 new `{ return (JavaVMInitArgs*)malloc(sizeof(JavaVMInitArgs)); `}
89
90 # Set the defaut config for a VM
91 # Can be called after setting the version
92 #
93 # Unavailable on Android, where you cannot instanciate a new JVM.
94 fun set_default `{
95 #ifndef ANDROID
96 JNI_GetDefaultJavaVMInitArgs(self);
97 #endif
98 `}
99
100 fun version: Int `{ return self->version; `}
101 fun version=(v: Int) `{ self->version = v; `}
102
103 fun options: JavaVMOptionArray `{ return self->options; `}
104 fun options=(v: JavaVMOptionArray) `{ self->options = v; `}
105
106 fun n_options: Int `{ return self->nOptions; `}
107 fun n_options=(v: Int) `{ self->nOptions = v; `}
108 end
109
110 private extern class JavaVMOption `{ JavaVMOption* `}
111 fun string: String import CString.to_s `{
112 return CString_to_s((char*)self->optionString);
113 `}
114 fun string=(v: String) import String.to_cstring `{
115 self->optionString = String_to_cstring(v);
116 `}
117
118 fun extra_info: String import CString.to_s `{
119 return CString_to_s((char*)self->extraInfo);
120 `}
121 fun extra_info=(v: String) import String.to_cstring `{
122 self->extraInfo = String_to_cstring(v);
123 `}
124 end
125
126 private extern class JavaVMOptionArray `{ JavaVMOption* `}
127 new(size: Int) `{ return (JavaVMOption*)malloc(sizeof(JavaVMOption)*size); `}
128
129 fun [](i: Int): JavaVMOption `{ return self+i; `}
130 end
131
132 # Represents a jni JavaVM
133 extern class JavaVM `{JavaVM *`}
134 # Create the JVM
135 #
136 # The corresponding `JniEnv` can be obtained by calling `env`.
137 #
138 # Unavailable on some platforms, including Android where you cannot instanciate a new JVM.
139 private new(args: JavaVMInitArgs) import jni_error `{
140
141 #ifdef ANDROID
142 JavaVM_jni_error(NULL, "JVM creation not supported on Android", 0);
143 return NULL;
144 #endif
145
146 JavaVM *jvm;
147 JNIEnv *env;
148 jint res;
149
150 res = JNI_CreateJavaVM(&jvm, (void**)&env, args);
151
152 if (res != JNI_OK) {
153 JavaVM_jni_error(NULL, "Could not create Java VM", res);
154 return NULL;
155 }
156
157 return jvm;
158 `}
159
160 private fun jni_error(msg: CString, v: Int)
161 do
162 print "JNI Error: {msg} ({v})"
163 abort
164 end
165
166 # Unload the Java VM when the calling thread is the only remaining non-daemon attached user thread
167 fun destroy `{
168 (*self)->DestroyJavaVM(self);
169 `}
170
171 # `JniEnv` attached to the calling thread
172 #
173 # A null pointer is returned if the calling thread is not attached to the JVM.
174 fun env: JniEnv import jni_error `{
175 JNIEnv *env;
176 int res = (*self)->GetEnv(self, (void **)&env, JNI_VERSION_1_6);
177 if (res == JNI_EDETACHED) {
178 JavaVM_jni_error(NULL, "Requesting JNIEnv from an unattached thread", res);
179 return NULL;
180 }
181 else if (res != JNI_OK) {
182 JavaVM_jni_error(NULL, "Could not get JNIEnv from Java VM", res);
183 return NULL;
184 }
185 return env;
186 `}
187
188 # Attach the calling thread to the JVM and return its `JniEnv`
189 fun attach_current_thread: JniEnv import jni_error `{
190 JNIEnv *env;
191 #ifdef ANDROID
192 // the signature is different (better actually) on Android
193 int res = (*self)->AttachCurrentThread(self, &env, NULL);
194 #else
195 int res = (*self)->AttachCurrentThread(self, (void**)&env, NULL);
196 #endif
197 if (res != JNI_OK) {
198 JavaVM_jni_error(NULL, "Could not attach current thread to Java VM", res);
199 return NULL;
200 }
201 return env;
202 `}
203
204 # Detach the calling thread from this JVM
205 fun detach_current_thread import jni_error `{
206 int res = (*self)->DetachCurrentThread(self);
207 if (res != JNI_OK) {
208 JavaVM_jni_error(NULL, "Could not detach current thread to Java VM", res);
209 }
210 `}
211 end
212
213 # Represents a jni JNIEnv, which is a thread in a JavaVM
214 extern class JniEnv `{JNIEnv *`}
215
216 # Get a class object from its fully-qualified name or null if the class cannot be found
217 fun find_class(class_name : String): JClass import String.to_cstring `{
218 return (*self)->FindClass(self,String_to_cstring(class_name));
219 `}
220
221 # Return the method id for an instance of a class or interface
222 # The method is determined by its name and signature
223 # To obtain the method ID of a constructor, supply "<init>" as the method name and "void(V)" as the return type
224 fun get_method_id(clazz : JClass, name : String, signature : String): JMethodID import String.to_cstring `{
225 return (*self)->GetMethodID(self, clazz, String_to_cstring(name), String_to_cstring(signature));
226 `}
227
228 # Construct a new Java object from the `clazz`, using the constructor ̀ method_id`
229 fun new_object(clazz: JClass, method_id: JMethodID): JavaObject `{
230 return (*self)->NewObject(self, clazz, method_id);
231 `}
232
233 # Return the JClass of `obj`
234 fun get_object_class(obj: JavaObject): JClass `{
235 return (*self)->GetObjectClass(self, obj);
236 `}
237
238 # Registers native methods with the class specified by the `clazz` argument
239 fun register_natives(clazz: JClass, method: JNINativeMethod, n_method : Int): Int `{
240 return (*self)->RegisterNatives(self, clazz, method, n_method);
241 `}
242
243 # Call a method on `obj` designed by `method_id` with an array `args` of arguments
244 fun call_void_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]) import convert_args_to_jni `{
245 jvalue * args_tab = JniEnv_convert_args_to_jni(self, args);
246 (*self)->CallVoidMethodA(self, obj, method_id, args_tab);
247 free(args_tab);
248 `}
249
250 # Call a method on `obj` designed by `method_id` with an array `args` of argument returning a JavaObject
251 fun call_object_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): JavaObject import convert_args_to_jni `{
252 jvalue * args_tab = JniEnv_convert_args_to_jni(self, args);
253 jobject res = (*self)->CallObjectMethod(self, obj, method_id, args_tab);
254 free(args_tab);
255 return res;
256 `}
257
258 # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Bool
259 fun call_boolean_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Bool import convert_args_to_jni `{
260 jvalue * args_tab = JniEnv_convert_args_to_jni(self, args);
261 jboolean res = (*self)->CallBooleanMethod(self, obj, method_id, args_tab);
262 free(args_tab);
263 return res;
264 `}
265
266 # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Char
267 fun call_char_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Char import convert_args_to_jni `{
268 jvalue * args_tab = JniEnv_convert_args_to_jni(self, args);
269 jchar res = (*self)->CallCharMethod(self, obj, method_id, args_tab);
270 free(args_tab);
271 return res;
272 `}
273
274 # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning an Int
275 fun call_int_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Int import convert_args_to_jni `{
276 jvalue * args_tab = JniEnv_convert_args_to_jni(self, args);
277 jint res = (*self)->CallIntMethod(self, obj, method_id, args_tab);
278 free(args_tab);
279 return res;
280 `}
281
282 # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Float
283 fun call_float_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Float import convert_args_to_jni `{
284 jvalue * args_tab = JniEnv_convert_args_to_jni(self, args);
285 jfloat res = (*self)->CallFloatMethod(self, obj, method_id, args_tab);
286 free(args_tab);
287 return res;
288 `}
289
290 # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a CString
291 fun call_string_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): CString import convert_args_to_jni `{
292 jvalue * args_tab = JniEnv_convert_args_to_jni(self, args);
293 jobject jobj = (*self)->CallObjectMethod(self, obj, method_id, args_tab);
294 free(args_tab);
295 return (char*)(*self)->GetStringUTFChars(self, (jstring)jobj, NULL);
296 `}
297
298 private fun convert_args_to_jni(args: nullable Array[nullable Object]): Pointer import Array[nullable Object].as not nullable, Array[nullable Object].[], Array[nullable Object].length, nullable Object.as(Int), nullable Object.as(Char), nullable Object.as(Bool), nullable Object.as(Float), nullable Object.as(JavaObject), nullable Object.as(String), String.to_cstring, String.length `{
299 if(nullable_Array_of_nullable_Object_is_null(args)){
300 return NULL;
301 }
302 Array_of_nullable_Object nit_array = nullable_Array_of_nullable_Object_as_Array_of_nullable_Object(args);
303 int nit_array_length = Array_of_nullable_Object_length(nit_array);
304 int i;
305 jvalue *c_array = malloc(sizeof(jvalue)*(nit_array_length));
306 for (i = 0; i < nit_array_length; i ++) {
307 nullable_Object nullable_obj = Array_of_nullable_Object__index(nit_array, i);
308 if(nullable_Object_is_a_Int(nullable_obj)) {
309 int val = nullable_Object_as_Int(nullable_obj);
310 c_array[i].i = val;
311 } else if (nullable_Object_is_a_Char(nullable_obj)){
312 char val = nullable_Object_as_Char(nullable_obj);
313 c_array[i].c = val;
314 } else if (nullable_Object_is_a_Bool(nullable_obj)){
315 int val = nullable_Object_as_Bool(nullable_obj);
316 c_array[i].z = val;
317 } else if(nullable_Object_is_a_Float(nullable_obj)){
318 float val = nullable_Object_as_Float(nullable_obj);
319 c_array[i].f = val;
320 } else if(nullable_Object_is_a_JavaObject(nullable_obj)){
321 jobject val = nullable_Object_as_JavaObject(nullable_obj);
322 c_array[i].l = val;
323 } else if(nullable_Object_is_a_String(nullable_obj)){
324 String val = nullable_Object_as_String(nullable_obj);
325 char* c = String_to_cstring(val);
326 jstring js = (*self)->NewStringUTF(self, c);
327 c_array[i].l = js;
328 } else {
329 fprintf(stderr, "NOT YET SUPPORTED: nit objects are not supported\n");
330 exit(1);
331 }
332 }
333 return c_array;
334 `}
335
336 # Returns the field ID for an instance field of a class. The field is specified by its name and signature
337 fun get_field_id(clazz: JClass, name: String, sign: String): JFieldID import String.to_cstring `{
338 return (*self)->GetFieldID(self, clazz, String_to_cstring(name), String_to_cstring(sign));
339 `}
340
341 # returns the value of an instance (nonstatic) field of an object. The field to access is specified by a field ID obtained by calling get_field_id()
342 fun get_object_field(obj: JavaObject, fieldID: JFieldID): JavaObject `{
343 return (*self)->GetObjectField(self, obj, fieldID);
344 `}
345
346 fun get_boolean_field(obj: JavaObject, fieldID: JFieldID): Bool `{
347 return (*self)->GetBooleanField(self, obj, fieldID);
348 `}
349
350 fun get_char_field(obj: JavaObject, fieldID: JFieldID): Char `{
351 return (*self)->GetCharField(self, obj, fieldID);
352 `}
353
354 fun get_int_field(obj: JavaObject, fieldID: JFieldID): Int `{
355 return (*self)->GetIntField(self, obj, fieldID);
356 `}
357
358 fun get_float_field(obj: JavaObject, fieldID: JFieldID): Float `{
359 return (*self)->GetFloatField(self, obj, fieldID);
360 `}
361
362 fun set_object_field(obj: JavaObject, fieldID: JFieldID, value: JavaObject) `{
363 (*self)->SetObjectField(self, obj, fieldID, value);
364 `}
365
366 fun set_boolean_field(obj: JavaObject, fieldID: JFieldID, value: Bool) `{
367 (*self)->SetBooleanField(self, obj, fieldID, value);
368 `}
369
370 fun set_char_field(obj: JavaObject, fieldID: JFieldID, value: Char) `{
371 (*self)->SetCharField(self, obj, fieldID, value);
372 `}
373
374 fun set_int_field(obj: JavaObject, fieldID: JFieldID, value: Int) `{
375 (*self)->SetIntField(self, obj, fieldID, value);
376 `}
377
378 fun set_float_field(obj: JavaObject, fieldID: JFieldID, value: Float) `{
379 (*self)->SetFloatField(self, obj, fieldID, value);
380 `}
381
382 # Check for pending exception without creating a local reference to the exception object
383 fun exception_check: Bool `{
384 return (*self)->ExceptionCheck(self);
385 `}
386
387 # Construct an exception object from the specified class with the message specified by `message` and causes that exception to be thrown
388 fun throw_new(clazz: JClass, message: String): Int import String.to_cstring `{
389 return (*self)->ThrowNew(self, clazz, String_to_cstring(message));
390 `}
391
392 # return the exception if there is one in the process of being thrown, or NULL if no exception is currently being thrown
393 fun exception_occurred: JavaObject `{
394 return (*self)->ExceptionOccurred(self);
395 `}
396
397 # prints an exception and backtrace to error channel
398 fun exception_describe `{
399 return (*self)->ExceptionDescribe(self);
400 `}
401
402 # clears any exception currently being thrown, has no effect if there is no exception
403 fun exception_clear `{
404 return (*self)->ExceptionClear(self);
405 `}
406
407 # Raise a fatal error
408 fun fatal_error(msg: String) import String.to_cstring `{
409 (*self)->FatalError(self, String_to_cstring(msg));
410 `}
411
412 # Transform a NIT String into a JavaObject
413 fun string_to_jobject(string: String): JavaObject `{
414 return (*self)->NewStringUTF(self, String_to_cstring(string));
415 `}
416
417 # Pushes a local reference frame on the JNI stack
418 fun push_local_frame(capacity: Int): Bool `{
419 return (*self)->PushLocalFrame(self, capacity);
420 `}
421
422 # Pops the current local reference frame on the JNI stack
423 #
424 # Similiar to `JavaObject::pop_from_local_frame` which returns a value.
425 fun pop_local_frame `{
426 (*self)->PopLocalFrame(self, NULL);
427 `}
428 end
429
430 # Represents a jni jclass
431 extern class JClass `{jclass`}
432 end
433
434 # Represents a jni jmethodID
435 extern class JMethodID `{jmethodID`}
436 end
437
438 # Represens a jni jobject
439 extern class JavaObject in "Java" `{ java.lang.Object `}
440 end
441
442 # Represents a jni JNINNativeMethod
443 extern class JNINativeMethod `{ JNINativeMethod* `}
444 fun name: String import CString.to_s `{
445 return CString_to_s((void*)self->name);
446 `}
447
448 fun name=(name: String) import String.to_cstring `{
449 self->name = String_to_cstring(name);
450 `}
451
452 fun signature: String import CString.to_s `{
453 return CString_to_s((void*)self->signature);
454 `}
455
456 fun signature=(signature: String) import String.to_cstring `{
457 self->signature = String_to_cstring(signature);
458 `}
459 end
460
461 # Represents a jni jfieldID
462 extern class JFieldID `{jfieldID`}
463 end
464
465 # Reprents a jni jvalue
466 extern class JValue `{jvalue`}
467
468 fun set_boolean(b: Bool) `{
469 self.z = b;
470 `}
471
472 fun get_boolean:Bool `{
473 return self.z;
474 `}
475
476 fun set_char(c: Char)`{
477 self.c = c;
478 `}
479
480 fun get_char: Char `{
481 return self.c;
482 `}
483
484 fun set_int(i: Int) `{
485 self.i = i;
486 `}
487
488 fun get_int: Int `{
489 return self.i;
490 `}
491
492 fun set_float(f: Float) `{
493 self.f = f;
494 `}
495
496 fun get_float: Float `{
497 return self.f;
498 `}
499
500 fun set_jobject(obj: JavaObject) `{
501 self.l = obj;
502 `}
503
504 fun get_jobject: JavaObject `{
505 return self.l;
506 `}
507 end
508
509 redef class Int
510 redef fun to_jvalue(env): JValue `{
511 jvalue value;
512 value.i = self;
513 return value;
514 `}
515 end
516
517 redef class Float
518 redef fun to_jvalue(env): JValue `{
519 jvalue value;
520 value.f = self;
521 return value;
522 `}
523 end
524
525 redef class Bool
526 redef fun to_jvalue(env): JValue `{
527 jvalue value;
528 value.z = self;
529 return value;
530 `}
531 end
532
533 redef class CString
534 redef fun to_jvalue(env)`{
535 jvalue value;
536 value.l = (*env)->NewStringUTF(env, self);
537 return value;
538 `}
539 end
540
541 redef class String
542 redef fun to_jvalue(env) do
543 return self.to_cstring.to_jvalue(env)
544 end
545 end
546
547 redef class Object
548 fun to_jvalue(env: JniEnv): JValue is abstract
549 end