389c6f6e542f2b18db919957fc9ff7a480b042b1
[nit.git] / lib / java / java.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
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 # Supporting services for the FFI with Java
18 #
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`.
25 #
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
28 # of your code.
29 module java is
30 c_compiler_option("-I $(JAVA_HOME)/include/")
31 c_linker_option("-L $(JNI_LIB_PATH) -ljvm")
32 new_annotation extra_java_files
33 end
34
35 import jvm
36
37 redef class Sys
38 private var jvm_cache: nullable JavaVM = null
39 private var jni_env_cache: nullable JniEnv = null
40
41 # Default Java Virtual Machine to use (will be instantiated using
42 # `create_default_jvm` if not already set)
43 fun jvm: JavaVM
44 do
45 if jvm_cache == null then create_default_jvm
46 return jvm_cache.as(not null)
47 end
48
49 # Sets the current default Java Virtual Machine (use with `jni_env=`)
50 fun jvm=(jvm: JavaVM) do jvm_cache = jvm
51
52 # Current main `JniEnv`
53 fun jni_env: JniEnv
54 do
55 if jni_env_cache == null then create_default_jvm
56 return jni_env_cache.as(not null)
57 end
58
59 # Sets the current default JNI env (use with `jvm=`)
60 fun jni_env=(jni_env: JniEnv) do jni_env_cache = jni_env
61
62 # Called by `jvm` and `jni_env` to instantiate a Java Virtual Machine.
63 # Used mostly for the FFI with Java.
64 protected fun create_default_jvm
65 do
66 var builder = new JavaVMBuilder
67
68 # By default, look for Java classes in a jar file the same directory as the executable
69 builder.options.add "-Djava.class.path={sys.program_name}.jar"
70
71 var jvm = builder.create_jvm
72 assert jvm != null else print "JVM creation failed"
73
74 self.jvm = jvm
75 self.jni_env = builder.jni_env.as(not null)
76 end
77
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);
81
82 // retrieve 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);
87 exit(1);
88 }
89
90 return java_class;
91 `}
92 end
93
94 # A standard Java string `java.lang.String`
95 #
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 `}
99 super JavaObject
100
101 redef type SELF: JavaString
102
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);
107
108 // Get the data from Java
109 const jbyte *java_cstr = (char*)(*env)->GetStringUTFChars(env, recv, NULL);
110 jsize len = (*env)->GetStringUTFLength(env, recv);
111
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';
116
117 // Free JNI ref and return
118 (*env)->ReleaseStringUTFChars(env, recv, java_cstr);
119 return nit_cstr;
120 `}
121
122 redef fun to_s do return to_cstring.to_s
123 end
124
125 redef class NativeString
126 # Get a Java string from this C string
127 #
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);
134 `}
135 end
136
137 redef class Text
138 # Get `self` as a `JavaString`
139 fun to_java_string: JavaString do return to_cstring.to_java_string
140 end
141
142 redef extern class JavaObject
143 type SELF: JavaObject
144
145 # Returns a global reference to the Java object behind this reference
146 #
147 # You must use a global reference when keeping a Java object
148 # across execution of Java code, per JNI specification.
149 fun new_global_ref: SELF import sys, Sys.jni_env `{
150 Sys sys = JavaObject_sys(recv);
151 JNIEnv *env = Sys_jni_env(sys);
152 return (*env)->NewGlobalRef(env, recv);
153 `}
154
155 # Delete this global reference
156 fun delete_global_ref import sys, Sys.jni_env `{
157 Sys sys = JavaObject_sys(recv);
158 JNIEnv *env = Sys_jni_env(sys);
159 (*env)->DeleteGlobalRef(env, recv);
160 `}
161
162 # Delete this local reference
163 fun delete_local_ref import sys, Sys.jni_env `{
164 Sys sys = JavaObject_sys(recv);
165 JNIEnv *env = Sys_jni_env(sys);
166 (*env)->DeleteLocalRef(env, recv);
167 `}
168
169 # Pops the current local reference frame and return a valid reference to self
170 #
171 # Similiar to `JavaVM::pop_local_frame` but returns a value.
172 fun pop_from_local_frame: SELF
173 do
174 var jni_env = sys.jni_env
175 return pop_from_local_frame_with_env(jni_env)
176 end
177
178 private fun pop_from_local_frame_with_env(jni_env: JniEnv): SELF `{
179 return (*jni_env)->PopLocalFrame(jni_env, recv);
180 `}
181
182 # Is `self` null in Java?
183 #
184 # Since Java type system doesn't have the same `nullable` concept as Nit's,
185 # the two systems are not directly compatible. Any Nit instances of
186 # `JavaObject` may hold a Java null.
187 #
188 # To benefit from the safer type system of Nit, it is recommended to check
189 # the return of all extern methods implemented in Java to ensure the value
190 # is not a Java null. In case it is, you should replace it by a normal Nit
191 # `null`.
192 fun is_java_null: Bool in "Java" `{ return recv == null; `}
193
194 # `JavaString` representation of `self` using Java's `toString`
195 fun to_java_string: JavaString in "Java" `{ return recv.toString(); `}
196
197 # Use Java's `toString` for any `JavaObject`
198 redef fun to_s
199 do
200 if is_java_null then return super
201 return to_java_string.to_s
202 end
203 end