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