1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2014 Romain Chanoir <romain.chanoir@courrier.uqam.ca>
4 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
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
10 # http://www.apache.org/licenses/LICENSE-2.0
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.
18 # Manipulates the Java Virtual Machine
20 # See: http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html
22 cflags
"-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
23 ldflags
"-L $(JNI_LIB_PATH) -ljvm"
30 # Utility to select options to create the VM using `create_jvm`
35 # var builder = new JavaVMBuilder
36 # builder.options.add "-Djava.class.path=."
37 # var jvm = builder.create_jvm
38 # var env = builder.jni_env
43 # Version code of the JVM requested by `create_jvm`
45 # Default at 0x00010002
46 var version
= 0x00010002 is writable
48 # Additional option strings
49 var options
= new Array[String]
51 # Create the JVM and return it on success
52 fun create_jvm
: nullable JavaVM
54 var args
= new JavaVMInitArgs
55 args
.version
= version
57 args
.n_options
= options
.length
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
66 args
.options
= c_options
68 var jvm
= new JavaVM(args
, self)
73 if jvm
.address_is_null
then return null
78 private extern class JavaVMInitArgs `{ JavaVMInitArgs* `}
79 new `{ return (JavaVMInitArgs*)malloc(sizeof(JavaVMInitArgs)); `}
81 # Set the defaut config for a VM
82 # Can be called after setting the version
84 # Unavailable on Android, where you cannot instanciate a new JVM.
87 JNI_GetDefaultJavaVMInitArgs(self);
91 fun version
: Int `{ return self->version; `}
92 fun version=(v: Int) `{ self->version = v; `}
94 fun options
: JavaVMOptionArray `{ return self->options; `}
95 fun options=(v: JavaVMOptionArray) `{ self->options = v; `}
97 fun n_options
: Int `{ return self->nOptions; `}
98 fun n_options=(v: Int) `{ self->nOptions = v; `}
101 private extern class JavaVMOption `{ JavaVMOption* `}
102 fun string: String import NativeString.to_s `{
103 return NativeString_to_s((char
*)self-
>optionString
);
105 fun string=(v: String) import String.to_cstring `{
106 self-
>optionString
= String_to_cstring(v
);
109 fun extra_info: String import NativeString.to_s `{
110 return NativeString_to_s((char
*)self-
>extraInfo
);
112 fun extra_info=(v: String) import String.to_cstring `{
113 self-
>extraInfo
= String_to_cstring(v
);
117 private extern class JavaVMOptionArray `{ JavaVMOption* `}
118 new(size
: Int) `{ return (JavaVMOption*)malloc(sizeof(JavaVMOption)*size); `}
120 fun [](i: Int): JavaVMOption `{ return self+i; `}
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
`
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 `{
132 JavaVM_jni_error(NULL, "JVM creation not supported on Android", 0);
140 res
= JNI_CreateJavaVM(&jvm
, (void
**)&env
, args
);
143 JavaVM_jni_error(NULL, "Could not create Java VM", res
);
147 JniEnvRef_jni_env__assign(env_ref
, JniEnv_as_nullable_JniEnv(env
));
152 private fun jni_error(msg: NativeString, v: Int)
154 print "JNI Error: {msg} ({v})"
159 (*self)->DestroyJavaVM(self);
162 fun env: JniEnv import jni_error `{
164 int res
= (*self)->GetEnv(self, (void
**)&env
, JNI_VERSION_1_6);
166 JavaVM_jni_error(NULL, "Could not get JNIEnv from Java VM", res
);
172 fun attach_current_thread: JniEnv import jni_error `{
175 // the signature
is different
(better actually
) on
Android
176 int res
= (*self)->AttachCurrentThread(self, &env
, NULL);
178 int res
= (*self)->AttachCurrentThread(self, (void
**)&env
, NULL);
181 JavaVM_jni_error(NULL, "Could not attach current thread to Java VM", res
);
188 # Represents a jni JNIEnv, which is a thread in a JavaVM
189 extern class JniEnv `{JNIEnv *`}
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 (*self)->FindClass(self,String_to_cstring(class_name));
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 (*self)->GetMethodID(self, clazz, String_to_cstring(name), String_to_cstring(signature));
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 (*self)->NewObject(self, clazz, method_id);
208 # Return the JClass of `obj`
209 fun get_object_class
(obj
: JavaObject): JClass `{
210 return (*self)->GetObjectClass(self, obj);
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 (*self)->RegisterNatives(self, clazz, method, n_method);
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(self, args);
221 (*self)->CallVoidMethodA(self, obj, method_id, args_tab);
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(self, args);
228 jobject res = (*self)->CallObjectMethod(self, obj, method_id, args_tab);
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(self, args);
236 jboolean res = (*self)->CallBooleanMethod(self, obj, method_id, args_tab);
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(self, args);
244 jchar res = (*self)->CallCharMethod(self, obj, method_id, args_tab);
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(self, args);
252 jint res = (*self)->CallIntMethod(self, obj, method_id, args_tab);
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(self, args);
260 jfloat res = (*self)->CallFloatMethod(self, obj, method_id, args_tab);
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(self, args);
268 jobject jobj = (*self)->CallObjectMethod(self, obj, method_id, args_tab);
270 return (char*)(*self)->GetStringUTFChars(self, (jstring)jobj, NULL);
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)){
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);
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);
286 } else if (nullable_Object_is_a_Char(nullable_obj)){
287 char val = nullable_Object_as_Char(nullable_obj);
289 } else if (nullable_Object_is_a_Bool(nullable_obj)){
290 int val = nullable_Object_as_Bool(nullable_obj);
292 } else if(nullable_Object_is_a_Float(nullable_obj)){
293 float val = nullable_Object_as_Float(nullable_obj);
295 } else if(nullable_Object_is_a_JavaObject(nullable_obj)){
296 jobject val = nullable_Object_as_JavaObject(nullable_obj);
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 = (*self)->NewStringUTF(self, c);
304 fprintf(stderr, "NOT YET SUPPORTED: nit objects are not supported\n");
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 (*self)->GetFieldID(self, clazz, String_to_cstring(name), String_to_cstring(sign));
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 (*self)->GetObjectField(self, obj, fieldID);
321 fun get_boolean_field
(obj
: JavaObject, fieldID
: JFieldID): Bool `{
322 return (*self)->GetBooleanField(self, obj, fieldID);
325 fun get_char_field
(obj
: JavaObject, fieldID
: JFieldID): Char `{
326 return (*self)->GetCharField(self, obj, fieldID);
329 fun get_int_field
(obj
: JavaObject, fieldID
: JFieldID): Int `{
330 return (*self)->GetIntField(self, obj, fieldID);
333 fun get_float_field
(obj
: JavaObject, fieldID
: JFieldID): Float `{
334 return (*self)->GetFloatField(self, obj, fieldID);
337 fun set_object_field
(obj
: JavaObject, fieldID
: JFieldID, value
: JavaObject) `{
338 (*self)->SetObjectField(self, obj, fieldID, value);
341 fun set_boolean_field
(obj
: JavaObject, fieldID
: JFieldID, value
: Bool) `{
342 (*self)->SetBooleanField(self, obj, fieldID, value);
345 fun set_char_field
(obj
: JavaObject, fieldID
: JFieldID, value
: Char) `{
346 (*self)->SetCharField(self, obj, fieldID, value);
349 fun set_int_field
(obj
: JavaObject, fieldID
: JFieldID, value
: Int) `{
350 (*self)->SetIntField(self, obj, fieldID, value);
353 fun set_float_field
(obj
: JavaObject, fieldID
: JFieldID, value
: Float) `{
354 (*self)->SetFloatField(self, obj, fieldID, value);
357 # Check for pending exception without creating a local reference to the exception object
358 fun exception_check
: Bool `{
359 return (*self)->ExceptionCheck(self);
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 (*self)->ThrowNew(self, clazz, String_to_cstring(message));
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 (*self)->ExceptionOccurred(self);
372 # prints an exception and backtrace to error channel
373 fun exception_describe
`{
374 return (*self)->ExceptionDescribe(self);
377 # clears any exception currently being thrown, has no effect if there is no exception
378 fun exception_clear
`{
379 return (*self)->ExceptionClear(self);
382 # Raise a fatal error
383 fun fatal_error
(msg
: String) import String.to_cstring
`{
384 (*self)->FatalError(self, String_to_cstring(msg));
387 # Transform a NIT String into a JavaObject
388 fun string_to_jobject
(string
: String): JavaObject `{
389 return (*self)->NewStringUTF(self, String_to_cstring(string));
392 # Pushes a local reference frame on the JNI stack
393 fun push_local_frame
(capacity
: Int): Bool `{
394 return (*self)->PushLocalFrame(self, capacity);
397 # Pops the current local reference frame on the JNI stack
399 # Similiar to `JavaObject::pop_from_local_frame` which returns a value.
400 fun pop_local_frame
`{
401 (*self)->PopLocalFrame(self, NULL);
405 # used to initialize a JavaVM
407 var jni_env
: nullable JniEnv = null
410 # Represents a jni jclass
411 extern class JClass `{jclass`}
414 # Represents a jni jmethodID
415 extern class JMethodID `{jmethodID`}
418 # Represens a jni jobject
419 extern class JavaObject in "Java" `{ java.lang.Object `}
422 # Represents a jni JNINNativeMethod
423 extern class JNINativeMethod `{ JNINativeMethod* `}
424 fun name
: String import NativeString.to_s
`{
425 return NativeString_to_s((void*)self->name);
428 fun name
=(name
: String) import String.to_cstring
`{
429 self->name = String_to_cstring(name);
432 fun signature
: String import NativeString.to_s
`{
433 return NativeString_to_s((void*)self->signature);
436 fun signature
=(signature
: String) import String.to_cstring
`{
437 self->signature = String_to_cstring(signature);
441 # Represents a jni jfieldID
442 extern class JFieldID `{jfieldID`}
445 # Reprents a jni jvalue
446 extern class JValue `{jvalue`}
448 fun set_boolean
(b
: Bool) `{
452 fun get_boolean
:Bool `{
456 fun set_char
(c
: Char)`{
460 fun get_char
: Char `{
464 fun set_int
(i
: Int) `{
472 fun set_float
(f
: Float) `{
476 fun get_float
: Float `{
480 fun set_jobject
(obj
: JavaObject) `{
484 fun get_jobject
: JavaObject `{
490 redef fun to_jvalue
(env
): JValue `{
498 redef fun to_jvalue
(env
): JValue `{
506 redef fun to_jvalue
(env
): JValue `{
513 redef class NativeString
514 redef fun to_jvalue
(env
)`{
516 value.l = (*env)->NewStringUTF(env, self);
522 redef fun to_jvalue
(env
) do
523 return self.to_cstring
.to_jvalue
(env
)
528 fun to_jvalue
(env
: JniEnv): JValue is abstract