1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Supporting services for the FFI with Java
19 # This modules relies on `Sys::jvm`, `Sys::jni_env` and
20 # `Sys::create_default_jvm` to get a handle on a JVM. You can adapt the
21 # behavior of the FFI and services in this module by redefing
22 # `Sys::create_default_jvm` and supply your own JVM object. You can manage
23 # multiple java thread by switching the current environment in a redef
24 # of `Sys::jni_env`, and multiple JVM using `Sys::jvm`.
26 # The module `jvm` gives more control over the JVM instances and wraps
27 # most of JNI functions. You can use it to further customize the behavior
30 c_compiler_option
("-I $(JAVA_HOME)/include/")
31 c_linker_option
("-L $(JNI_LIB_PATH) -ljvm")
32 new_annotation extra_java_files
38 private var jvm_cache
: nullable JavaVM = null
39 private var jni_env_cache
: nullable JniEnv = null
41 # Default Java Virtual Machine to use (will be instanciated using
42 # `create_default_jvm` if not already set)
45 if jvm_cache
== null then create_default_jvm
46 return jvm_cache
.as(not null)
49 # Sets the current default Java Virtual Machine (use with `jni_env=`)
50 fun jvm
=(jvm
: JavaVM) do jvm_cache
= jvm
52 # Current main `JniEnv`
55 if jni_env_cache
== null then create_default_jvm
56 return jni_env_cache
.as(not null)
59 # Sets the current default JNI env (use with `jvm=`)
60 fun jni_env
=(jni_env
: JniEnv) do jni_env_cache
= jni_env
62 # Called by `jvm` and `jni_env` to instanciate a Java Virual Machine.
63 # Used mostly for the FFI with Java.
64 protected fun create_default_jvm
66 var builder
= new JavaVMBuilder
68 # By default, look for Java classes in a jar file the same dir as the executable
69 builder
.options
.add
"-Djava.class.path={sys.program_name}.jar"
71 var jvm
= builder
.create_jvm
72 assert jvm
!= null else print
"JVM creation failed"
75 self.jni_env
= builder
.jni_env
.as(not null)
78 # Get a Java class by its name from the current `jni_env`
79 fun load_jclass
(name
: NativeString): JClass import jni_env
`{
80 JNIEnv *nit_ffi_jni_env = Sys_jni_env(recv);
82 // retreive the implementation Java class
83 jclass java_class = (*nit_ffi_jni_env)->FindClass(nit_ffi_jni_env, name);
84 if (java_class == NULL) {
85 fprintf(stderr, "Nit FFI with Java error: failed to load class.\\n");
86 (*nit_ffi_jni_env)->ExceptionDescribe(nit_ffi_jni_env);
94 # A standard Java string `java.lang.String`
96 # Converted to a Nit string using `to_s`, or to a C string with `to_cstring`.
97 # Created using `String::to_java_string` or `NativeString::to_java_string`.
98 extern class JavaString in "Java" `{ java.lang.String `}
101 redef type SELF: JavaString
103 # Get the string from Java and copy it to Nit memory
104 fun to_cstring: NativeString import sys, Sys.jni_env `{
105 Sys sys
= JavaString_sys(recv
);
106 JNIEnv *env
= Sys_jni_env(sys
);
108 // Get the data from
Java
109 const jbyte
*java_cstr
= (char
*)(*env
)->GetStringUTFChars(env
, recv
, NULL);
110 jsize len
= (*env
)->GetStringUTFLength(env
, recv
);
112 // Copy it
in control of
Nit
113 char
*nit_cstr
= (char
*)malloc
(len
+1);
114 memcpy
(nit_cstr
, java_cstr
, len
);
115 nit_cstr
[len
] = '\0';
117 // Free JNI ref
and return
118 (*env
)->ReleaseStringUTFChars(env
, recv
, java_cstr
);
122 redef fun to_s do return to_cstring.to_s
125 redef class NativeString
126 # Get a Java string from this C string
128 # This instance is only valid until the next execution of Java code.
129 # You can use `new_local_ref
` to keep it longer.
130 fun to_java_string: JavaString import sys, Sys.jni_env `{
131 Sys sys
= JavaString_sys(recv
);
132 JNIEnv *env
= Sys_jni_env(sys
);
133 return (*env
)->NewStringUTF(env
, recv
);
138 fun to_java_string: JavaString do return to_cstring.to_java_string
141 redef extern class JavaObject
142 type SELF: JavaObject
144 # Returns a global reference to the Java object behind this reference
146 # You must use a global reference when keeping a Java object
147 # across execution of Java code, per JNI specification.
148 fun new_global_ref: SELF import sys, Sys.jni_env `{
149 Sys sys
= JavaObject_sys(recv
);
150 JNIEnv *env
= Sys_jni_env(sys
);
151 return (*env
)->NewGlobalRef(env
, recv
);
154 # Delete this global reference
155 fun delete_global_ref import sys, Sys.jni_env `{
156 Sys sys
= JavaObject_sys(recv
);
157 JNIEnv *env
= Sys_jni_env(sys
);
158 (*env
)->DeleteGlobalRef(env
, recv
);
161 # Delete this local reference
162 fun delete_local_ref import sys, Sys.jni_env `{
163 Sys sys
= JavaObject_sys(recv
);
164 JNIEnv *env
= Sys_jni_env(sys
);
165 (*env
)->DeleteLocalRef(env
, recv
);
168 # Pops the current local reference frame and return a valid reference to self
170 # Similiar to `JavaVM::pop_local_frame
` but returns a value.
171 fun pop_from_local_frame: SELF
173 var jni_env = sys.jni_env
174 return pop_from_local_frame_with_env(jni_env)
177 private fun pop_from_local_frame_with_env(jni_env: JniEnv): SELF `{
178 return (*jni_env
)->PopLocalFrame(jni_env
, recv
);