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 c_compiler_option
("-I $(JAVA_HOME)/include/")
23 c_linker_option
("-L $(JNI_LIB_PATH) -ljvm")
30 # Utility to select options to create the VM using `create_jvm`
34 # var builder = new JavaVMBuilder
35 # builder.options.add "-Djava.class.path=."
36 # var jvm = builder.create_jvm
37 # var env = builder.jni_env
42 var version
: Int = "00010002".to_hex
43 var options
= new Array[String]
45 fun create_jvm
: nullable JavaVM
47 var args
= new JavaVMInitArgs
48 args
.version
= version
50 args
.n_options
= options
.length
52 var c_options
= new JavaVMOptionArray(options
.length
)
53 for o
in options
.length
.times
do
54 var option
= options
[o
]
55 var c_option
= c_options
[o
]
56 c_option
.string
= option
59 args
.options
= c_options
61 var jvm
= new JavaVM(args
, self)
66 if jvm
.address_is_null
then return null
71 extern class JavaVMInitArgs `{ JavaVMInitArgs* `}
72 new `{ return (JavaVMInitArgs*)malloc(sizeof(JavaVMInitArgs)); `}
74 # Set the defaut config for a VM
75 # Can be called after setting the version
77 # Unavailable on Android, where you cannot instanciate a new JVM.
80 JNI_GetDefaultJavaVMInitArgs(recv);
84 fun version
: Int `{ return recv->version; `}
85 fun version=(v: Int) `{ recv->version = v; `}
87 fun options
: JavaVMOptionArray `{ return recv->options; `}
88 fun options=(v: JavaVMOptionArray) `{ recv->options = v; `}
90 fun n_options
: Int `{ return recv->nOptions; `}
91 fun n_options=(v: Int) `{ recv->nOptions = v; `}
94 extern class JavaVMOption `{ JavaVMOption* `}
95 fun string: String import NativeString.to_s `{
96 return NativeString_to_s((char
*)recv-
>optionString
);
98 fun string=(v: String) import String.to_cstring `{
99 recv-
>optionString
= String_to_cstring(v
);
102 fun extra_info: String import NativeString.to_s `{
103 return NativeString_to_s((char
*)recv-
>extraInfo
);
105 fun extra_info=(v: String) import String.to_cstring `{
106 recv-
>extraInfo
= String_to_cstring(v
);
110 extern class JavaVMOptionArray `{ JavaVMOption* `}
111 new(size
: Int) `{ return (JavaVMOption*)malloc(sizeof(JavaVMOption)*size); `}
113 fun [](i: Int): JavaVMOption `{ return recv+i; `}
116 # Represents a jni JavaVM
117 extern class JavaVM `{JavaVM *`}
118 # Create the JVM, returns its handle and store the a pointer to JniEnv in `env_ref
`
120 # Unavailable on Android, where you cannot instanciate a new JVM.
121 new(args: JavaVMInitArgs, env_ref: JniEnvRef) import jni_error, JniEnvRef.jni_env=, JniEnv as nullable `{
124 JavaVM_jni_error(NULL, "JVM creation not supported on Android", 0);
132 res
= JNI_CreateJavaVM(&jvm
, (void
**)&env
, args
);
135 JavaVM_jni_error(NULL, "Could not create Java VM", res
);
139 JniEnvRef_jni_env__assign(env_ref
, JniEnv_as_nullable_JniEnv(env
));
144 fun jni_error(msg: NativeString, v: Int)
146 print "JNI Error: {msg} ({v})"
151 (*recv
)->DestroyJavaVM(recv
);
154 fun env: JniEnv import jni_error `{
156 int res
= (*recv
)->GetEnv(recv
, (void
**)&env
, JNI_VERSION_1_6);
158 JavaVM_jni_error(NULL, "Could not get JNIEnv from Java VM", res
);
164 fun attach_current_thread: JniEnv `{
166 int res
= (*recv
)->AttachCurrentThread(recv
, (void
**)&env
, NULL);
168 JavaVM_jni_error(NULL, "Could not attach current thread to Java VM", res
);
174 # Represents a jni JNIEnv, which is a thread in a JavaVM
175 extern class JniEnv `{JNIEnv *`}
177 # Get a class object from its fully-qualified name or null if the class cannot be found
178 fun find_class
(class_name
: String): JClass import String.to_cstring
`{
179 return (*recv)->FindClass(recv,String_to_cstring(class_name));
182 # Return the method id for an instance of a class or interface
183 # The method is determined by its name and signature
184 # To obtain the method ID of a constructor, supply "<init>" as the method name and "void(V)" as the return type
185 fun get_method_id
(clazz
: JClass, name
: String, signature
: String): JMethodID import String.to_cstring
`{
186 return (*recv)->GetMethodID(recv, clazz, String_to_cstring(name), String_to_cstring(signature));
189 # Construct a new Java object from the `clazz`, using the constructor ̀ method_id`
190 fun new_object
(clazz
: JClass, method_id
: JMethodID): JavaObject `{
191 return (*recv)->NewObject(recv, clazz, method_id);
194 # Return the JClass of `obj`
195 fun get_object_class
(obj
: JavaObject): JClass `{
196 return (*recv)->GetObjectClass(recv, obj);
199 # Registers native methods with the class specified by the `clazz` argument
200 fun register_natives
(clazz
: JClass, method
: JNINativeMethod, n_method
: Int): Int `{
201 return (*recv)->RegisterNatives(recv, clazz, method, n_method);
204 # Call a method on `obj` designed by `method_id` with an array `args` of arguments
205 fun call_void_method
(obj
: JavaObject, method_id
: JMethodID, args
: nullable Array[nullable Object]) import convert_args_to_jni
`{
206 jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
207 (*recv)->CallVoidMethodA(recv, obj, method_id, args_tab);
211 # Call a method on `obj` designed by `method_id` with an array `args` of argument returning a JavaObject
212 fun call_object_method
(obj
: JavaObject, method_id
: JMethodID, args
: nullable Array[nullable Object]): JavaObject import convert_args_to_jni
`{
213 jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
214 (*recv)->CallObjectMethod(recv, obj, method_id, args_tab);
218 # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Bool
219 fun call_boolean_method
(obj
: JavaObject, method_id
: JMethodID, args
: nullable Array[nullable Object]): Bool import convert_args_to_jni
`{
220 jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
221 return (*recv)->CallBooleanMethod(recv, obj, method_id, args_tab);
225 # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Char
226 fun call_char_method
(obj
: JavaObject, method_id
: JMethodID, args
: nullable Array[nullable Object]): Char import convert_args_to_jni
`{
227 jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
228 return (*recv)->CallCharMethod(recv, obj, method_id, args_tab);
232 # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning an Int
233 fun call_int_method
(obj
: JavaObject, method_id
: JMethodID, args
: nullable Array[nullable Object]): Int import convert_args_to_jni
`{
234 jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
235 return (*recv)->CallIntMethod(recv, obj, method_id, args_tab);
239 # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Float
240 fun call_float_method
(obj
: JavaObject, method_id
: JMethodID, args
: nullable Array[nullable Object]): Float import convert_args_to_jni
`{
241 jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
242 return (*recv)->CallFloatMethod(recv, obj, method_id, args_tab);
246 # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a NativeString
247 fun call_string_method
(obj
: JavaObject, method_id
: JMethodID, args
: nullable Array[nullable Object]): NativeString import convert_args_to_jni
`{
248 jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
249 jobject jobj = (*recv)->CallObjectMethod(recv, obj, method_id, args_tab);
251 return (char*)(*recv)->GetStringUTFChars(recv, (jstring)jobj, NULL);
254 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
`{
255 if(nullable_Array_of_nullable_Object_is_null(args)){
258 Array_of_nullable_Object nit_array = nullable_Array_of_nullable_Object_as_Array_of_nullable_Object(args);
259 int nit_array_length = Array_of_nullable_Object_length(nit_array);
261 jvalue *c_array = malloc(sizeof(jvalue)*(nit_array_length));
262 for (i = 0; i < nit_array_length; i ++) {
263 nullable_Object nullable_obj = Array_of_nullable_Object__index(nit_array, i);
264 if(nullable_Object_is_a_Int(nullable_obj)) {
265 int val = nullable_Object_as_Int(nullable_obj);
267 } else if (nullable_Object_is_a_Char(nullable_obj)){
268 char val = nullable_Object_as_Char(nullable_obj);
270 } else if (nullable_Object_is_a_Bool(nullable_obj)){
271 int val = nullable_Object_as_Bool(nullable_obj);
273 } else if(nullable_Object_is_a_Float(nullable_obj)){
274 float val = nullable_Object_as_Float(nullable_obj);
276 } else if(nullable_Object_is_a_JavaObject(nullable_obj)){
277 jobject val = nullable_Object_as_JavaObject(nullable_obj);
279 } else if(nullable_Object_is_a_String(nullable_obj)){
280 String val = nullable_Object_as_String(nullable_obj);
281 char* c = String_to_cstring(val);
282 jstring js = (*recv)->NewStringUTF(recv, c);
285 fprintf(stderr, "NOT YET SUPPORTED: nit objects are not supported\n");
292 # Returns the field ID for an instance field of a class. The field is specified by its name and signature
293 fun get_field_id
(clazz
: JClass, name
: String, sign
: String): JFieldID import String.to_cstring
`{
294 return (*recv)->GetFieldID(recv, clazz, String_to_cstring(name), String_to_cstring(sign));
297 # 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()
298 fun get_object_field
(obj
: JavaObject, fieldID
: JFieldID): JavaObject `{
299 return (*recv)->GetObjectField(recv, obj, fieldID);
302 fun get_boolean_field
(obj
: JavaObject, fieldID
: JFieldID): Bool `{
303 return (*recv)->GetBooleanField(recv, obj, fieldID);
306 fun get_char_field
(obj
: JavaObject, fieldID
: JFieldID): Char `{
307 return (*recv)->GetCharField(recv, obj, fieldID);
310 fun get_int_field
(obj
: JavaObject, fieldID
: JFieldID): Int `{
311 return (*recv)->GetIntField(recv, obj, fieldID);
314 fun get_float_field
(obj
: JavaObject, fieldID
: JFieldID): Float `{
315 return (*recv)->GetFloatField(recv, obj, fieldID);
318 fun set_object_field
(obj
: JavaObject, fieldID
: JFieldID, value
: JavaObject) `{
319 (*recv)->SetObjectField(recv, obj, fieldID, value);
322 fun set_boolean_field
(obj
: JavaObject, fieldID
: JFieldID, value
: Bool) `{
323 (*recv)->SetBooleanField(recv, obj, fieldID, value);
326 fun set_char_field
(obj
: JavaObject, fieldID
: JFieldID, value
: Char) `{
327 (*recv)->SetCharField(recv, obj, fieldID, value);
330 fun set_int_field
(obj
: JavaObject, fieldID
: JFieldID, value
: Int) `{
331 (*recv)->SetIntField(recv, obj, fieldID, value);
334 fun set_float_field
(obj
: JavaObject, fieldID
: JFieldID, value
: Float) `{
335 (*recv)->SetFloatField(recv, obj, fieldID, value);
338 # Check for pending exception without creating a local reference to the exception object
339 fun exception_check
: Bool `{
340 return (*recv)->ExceptionCheck(recv);
343 # Construct an exception object from the specified class with the message specified by `message` and causes that exception to be thrown
344 fun throw_new
(clazz
: JClass, message
: String): Int import String.to_cstring
`{
345 return (*recv)->ThrowNew(recv, clazz, String_to_cstring(message));
348 # return the exception if there is one in the process of being thrown, or NULL if no exception is currently being thrown
349 fun exception_occurred
: JavaObject `{
350 return (*recv)->ExceptionOccurred(recv);
353 # prints an exception and backtrace to error channel
354 fun exception_describe
`{
355 return (*recv)->ExceptionDescribe(recv);
358 # clears any exception currently being thrown, has no effect if there is no exception
359 fun exception_clear
`{
360 return (*recv)->ExceptionClear(recv);
363 # Raise a fatal error
364 fun fatal_error
(msg
: String) import String.to_cstring
`{
365 (*recv)->FatalError(recv, String_to_cstring(msg));
368 # Transform a NIT String into a JavaObject
369 fun string_to_jobject
(string
: String): JavaObject `{
370 return (*recv)->NewStringUTF(recv, String_to_cstring(string));
374 # used to initialize a JavaVM
376 var jni_env
: nullable JniEnv = null
379 # Represents a jni jclass
380 extern class JClass `{jclass`}
383 # Represents a jni jmethodID
384 extern class JMethodID `{jmethodID`}
387 # Represens a jni jobject
388 extern class JavaObject `{jobject`}
391 # Represents a jni JNINNativeMethod
392 extern class JNINativeMethod `{ JNINativeMethod* `}
393 fun name
: String import NativeString.to_s
`{
394 return NativeString_to_s((void*)recv->name);
397 fun name
=(name
: String) import String.to_cstring
`{
398 recv->name = String_to_cstring(name);
401 fun signature
: String import NativeString.to_s
`{
402 return NativeString_to_s((void*)recv->signature);
405 fun signature
=(signature
: String) import String.to_cstring
`{
406 recv->signature = String_to_cstring(signature);
410 # Represents a jni jfieldID
411 extern class JFieldID `{jfieldID`}
414 # Reprents a jni jvalue
415 extern class JValue `{jvalue`}
417 fun set_boolean
(b
: Bool) `{
421 fun get_boolean
:Bool `{
425 fun set_char
(c
: Char)`{
429 fun get_char
: Char `{
433 fun set_int
(i
: Int) `{
441 fun set_float
(f
: Float) `{
445 fun get_float
: Float `{
449 fun set_jobject
(obj
: JavaObject) `{
453 fun get_jobject
: JavaObject `{
459 redef fun to_jvalue
(env
): JValue `{
467 redef fun to_jvalue
(env
): JValue `{
475 redef fun to_jvalue
(env
): JValue `{
482 redef class NativeString
483 redef fun to_jvalue
(env
)`{
485 value.l = (*env)->NewStringUTF(env, recv);
491 redef fun to_jvalue
(env
) do
492 return self.to_cstring
.to_jvalue
(env
)
497 fun to_jvalue
(env
: JniEnv): JValue is abstract