1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Core supporting services for the FFI with Java
17 # This module *must* be imported by modules using the Java FFI.
18 # Some might prefer to import the whole `java` package as it provides
19 # other useful services.
21 cflags
"-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
22 ldflags
"-L $(JNI_LIB_PATH) -ljvm"
23 new_annotation extra_java_files
29 private var jvm_cache
: nullable JavaVM = null
30 private var jni_env_cache
: nullable JniEnv = null
32 # Default Java Virtual Machine to use (will be instantiated using
33 # `create_default_jvm` if not already set)
36 if jvm_cache
== null then create_default_jvm
37 return jvm_cache
.as(not null)
40 # Sets the current default Java Virtual Machine (use with `jni_env=`)
41 fun jvm
=(jvm
: JavaVM) do jvm_cache
= jvm
43 # Current main `JniEnv`
46 if jni_env_cache
== null then create_default_jvm
47 return jni_env_cache
.as(not null)
50 # Sets the current default JNI env (use with `jvm=`)
51 fun jni_env
=(jni_env
: JniEnv) do jni_env_cache
= jni_env
53 # Called by `jvm` and `jni_env` to instantiate a Java Virtual Machine.
54 # Used mostly for the FFI with Java.
55 protected fun create_default_jvm
57 var builder
= new JavaVMBuilder
59 # By default, look for Java classes in a jar file the same directory as the executable
60 builder
.options
.add
"-Djava.class.path={sys.program_name}.jar"
62 var jvm
= builder
.create_jvm
63 assert jvm
!= null else print
"JVM creation failed"
66 assert not jvm
.address_is_null
67 self.jni_env
= jvm
.env
68 assert not jni_env
.address_is_null
71 # Get a Java class by its name from the current `jni_env`
72 fun load_jclass
(name
: NativeString): JClass import jni_env
`{
73 JNIEnv *nit_ffi_jni_env = Sys_jni_env(self);
75 // retrieve the implementation Java class
76 jclass java_class = (*nit_ffi_jni_env)->FindClass(nit_ffi_jni_env, name);
77 if (java_class == NULL) {
78 fprintf(stderr, "Nit FFI with Java error: failed to load class.\\n");
79 (*nit_ffi_jni_env)->ExceptionDescribe(nit_ffi_jni_env);
87 # A standard Java string `java.lang.String`
89 # Converted to a Nit string using `to_s`, or to a C string with `to_cstring`.
90 # Created using `String::to_java_string` or `NativeString::to_java_string`.
91 extern class JavaString in "Java" `{ java.lang.String `}
94 # Get the string from Java and copy it to Nit memory
95 fun to_cstring: NativeString import sys, Sys.jni_env `{
96 Sys sys
= JavaString_sys(self);
97 JNIEnv *env
= Sys_jni_env(sys
);
99 // Get the data from
Java
100 const char
*java_cstr
= (*env
)->GetStringUTFChars(env
, self, NULL);
101 jsize len
= (*env
)->GetStringUTFLength(env
, self);
103 // Copy it
in control of
Nit
104 char
*nit_cstr
= (char
*)malloc
(len
+1);
105 memcpy
(nit_cstr
, java_cstr
, len
);
106 nit_cstr
[len
] = '\0';
108 // Free JNI ref
and return
109 (*env
)->ReleaseStringUTFChars(env
, self, java_cstr
);
113 redef fun to_s do return to_cstring.to_s
116 redef class NativeString
117 # Get a Java string from this C string
119 # This instance is only valid until the next execution of Java code.
120 # You can use `new_local_ref
` to keep it longer.
121 fun to_java_string: JavaString import sys, Sys.jni_env `{
122 Sys sys
= JavaString_sys(self);
123 JNIEnv *env
= Sys_jni_env(sys
);
124 return (*env
)->NewStringUTF(env
, self);
129 # Get `self` as a `JavaString`
130 fun to_java_string: JavaString do return to_cstring.to_java_string
133 redef extern class JavaObject
135 # Returns a global reference to the Java object behind this reference
137 # You must use a global reference when keeping a Java object
138 # across execution of Java code, per JNI specification.
139 fun new_global_ref: SELF import sys, Sys.jni_env `{
140 Sys sys
= JavaObject_sys(self);
141 JNIEnv *env
= Sys_jni_env(sys
);
142 return (*env
)->NewGlobalRef(env
, self);
145 # Delete this global reference
146 fun delete_global_ref import sys, Sys.jni_env `{
147 Sys sys
= JavaObject_sys(self);
148 JNIEnv *env
= Sys_jni_env(sys
);
149 (*env
)->DeleteGlobalRef(env
, self);
152 # Delete this local reference
153 fun delete_local_ref import sys, Sys.jni_env `{
154 Sys sys
= JavaObject_sys(self);
155 JNIEnv *env
= Sys_jni_env(sys
);
156 (*env
)->DeleteLocalRef(env
, self);
159 # Pops the current local reference frame and return a valid reference to self
161 # Similar to `JavaVM::pop_local_frame
` but returns a value.
162 fun pop_from_local_frame: SELF
164 var jni_env = sys.jni_env
165 return pop_from_local_frame_with_env(jni_env)
168 # Java implementation of `pop_from_local_frame
`
169 protected fun pop_from_local_frame_with_env(jni_env: JniEnv): SELF `{
170 return (*jni_env
)->PopLocalFrame(jni_env
, self);
173 # Is `self` null in Java?
175 # Since Java type system doesn't have the same `nullable` concept as Nit's,
176 # the two systems are not directly compatible. Any Nit instances of
177 # `JavaObject` may hold a Java null.
179 # To benefit from the safer type system of Nit, it is recommended to check
180 # the return of all extern methods implemented in Java to ensure the value
181 # is not a Java null. In case it is, you should replace it by a normal Nit
183 fun is_java_null: Bool in "Java" `{ return self == null; `}
185 # `JavaString` representation of `self` using Java's `toString`
186 fun to_java_string
: JavaString in "Java" `{ return self.toString(); `}
188 # Use Java's `toString
` for any `JavaObject`
191 if is_java_null then return super
192 return to_java_string.to_s
196 # Java class: java.lang.Throwable
197 extern class JavaThrowable in "Java" `{ java.lang.Throwable `}
200 # Java implementation: java.lang.String java.lang.Throwable.getMessage()
201 fun message
: JavaString in "Java" `{
202 return self.getMessage();
205 # Java implementation: java.lang.String java.lang.Throwable.getLocalizedMessage()
206 fun localized_message
: JavaString in "Java" `{
207 return self.getLocalizedMessage();
210 # Java implementation: java.lang.Throwable.printStackTrace()
211 fun print_stack_trace
in "Java" `{
212 self.printStackTrace();
215 # Java implementation: java.lang.Throwable java.lang.Throwable.getCause()
216 fun cause
: JavaThrowable in "Java" `{
217 return self.getCause();
220 redef fun new_global_ref
import sys
, Sys.jni_env
`{
221 Sys sys = JavaThrowable_sys(self);
222 JNIEnv *env = Sys_jni_env(sys);
223 return (*env)->NewGlobalRef(env, self);
226 redef fun pop_from_local_frame_with_env
(jni_env
) `{
227 return (*jni_env)->PopLocalFrame(jni_env, self);
231 # Java class: java.lang.Exception
232 extern class JavaException in "Java" `{ java.lang.Exception `}
235 redef fun new_global_ref import sys, Sys.jni_env `{
236 Sys sys
= JavaException_sys(self);
237 JNIEnv *env
= Sys_jni_env(sys
);
238 return (*env
)->NewGlobalRef(env
, self);
241 redef fun pop_from_local_frame_with_env(jni_env) `{
242 return (*jni_env
)->PopLocalFrame(jni_env
, self);
247 # Create new `Java.nio
.ByteBuffer` referring to `address
` with the given `capacity
` in bytes
249 # JNI function: NewDirectByteBuffer
250 fun new_direct_byte_buffer(address: Pointer, capacity: Int): Java_nio_ByteBuffer `{
251 return (*self)->NewDirectByteBuffer(self, address
, capacity
);
255 # Container for data of a specific primitive type
257 # Java class: java.nio.Buffer
258 extern class Java_nio_Buffer in "Java" `{ java.nio.Buffer `}
261 # Address pointed by this buffer
263 # JNI function: GetDirectBufferAddress
264 fun direct_buffer_address
(jni_env
: JniEnv): Pointer `{
265 return (*jni_env)->GetDirectBufferAddress(jni_env, self);
268 # Capacity of this this buffer
270 # JNI function: GetDirectBufferCapacity
271 fun direct_buffer_capacity
(jni_env
: JniEnv): Int `{
272 return (*jni_env)->GetDirectBufferCapacity(jni_env, self);
278 # Java class: java.nio.ByteBuffer
279 extern class Java_nio_ByteBuffer in "Java" `{ java.nio.ByteBuffer `}
280 super Java_nio_Buffer
282 # Allocate a new `java
.nio
.ByteBuffer` with `allocateDirect
`
283 new direct(size: Int) in "Java" `{
284 return java
.nio
.ByteBuffer.allocateDirect
((int
)size
);