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