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