From f2aedb0dfeb69bbe3d0bb1abe8df376db1ae8fbc Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alexis=20Laferri=C3=A8re?= Date: Sun, 17 Feb 2019 13:11:17 -0500 Subject: [PATCH] Java FFI: intro NitObject for references to Nit objects from Java MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Alexis Laferrière --- lib/java/NitObject.java | 27 ++++++++ lib/java/ffi_support.nit | 1 + src/ffi/java.nit | 155 ++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 165 insertions(+), 18 deletions(-) create mode 100644 lib/java/NitObject.java diff --git a/lib/java/NitObject.java b/lib/java/NitObject.java new file mode 100644 index 0000000..a09a9aa --- /dev/null +++ b/lib/java/NitObject.java @@ -0,0 +1,27 @@ +/* This file is part of NIT ( http://www.nitlanguage.org ). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package nit.app; + +// General class for all references to Nit objects from Java in the FFI +public class NitObject { + + // Address to the object in Nit memory + private long pointer; + + protected NitObject(long pointer) { + this.pointer = pointer; + } +} diff --git a/lib/java/ffi_support.nit b/lib/java/ffi_support.nit index 5fafd18..c6a7e2d 100644 --- a/lib/java/ffi_support.nit +++ b/lib/java/ffi_support.nit @@ -21,6 +21,7 @@ module ffi_support is cflags "-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/" ldflags "-L $(JNI_LIB_PATH) -ljvm" new_annotation extra_java_files + extra_java_files "nit.app.NitObject" end import jvm diff --git a/src/ffi/java.nit b/src/ffi/java.nit index 4894846..fe8bc19 100644 --- a/src/ffi/java.nit +++ b/src/ffi/java.nit @@ -105,7 +105,7 @@ class JavaLanguage jni_signature_alt = mclass_type.jni_signature_alt return_type = mclass_type else - params.add "self" + params.add to_java_call_context.cast_to(mclass_type, "self") if signature.return_mtype != null then var ret_mtype = signature.return_mtype ret_mtype = ret_mtype.resolve_for(mclass_type, mclass_type, mmodule, true) @@ -177,6 +177,64 @@ class JavaLanguage # Enable linking C callbacks to java native methods mmodule.ensure_linking_callback_methods(ffi_ccu) + # Function to build instances to the Java class NitObject + var callbacks = mmodule.callbacks_used_from_java.callbacks + if callbacks.not_empty then + var cf = new CFunction("jobject nit_ffi_with_java_new_nit_object(JNIEnv *env, void *data)") + cf.exprs.add """ + // retrieve the current JVM + Sys sys = Pointer_sys(NULL); + + jclass java_class = Sys_load_jclass(sys, "nit/app/NitObject"); + if (java_class == NULL) { + PRINT_ERROR("Nit FFI with Java error: failed to load class NitObject.\\n"); + (*env)->ExceptionDescribe(env); + exit(1); + } + + jmethodID java_init = (*env)->GetMethodID(env, java_class, "", "(J)V"); + if (java_init == NULL) { + PRINT_ERROR("Nit FFI with Java error: NitObject constructor not found.\\n"); + (*env)->ExceptionDescribe(env); + exit(1); + } + + jobject nit_object = (*env)->NewObject(env, java_class, java_init, (jlong)data); + if (nit_object == NULL) { + PRINT_ERROR("Nit FFI with Java error: NitObject construction failed.\\n"); + (*env)->ExceptionDescribe(env); + exit(1); + } + + return nit_object; + """ + ffi_ccu.add_local_function cf + + # Function to extract the pointer held by instances of the Java class NitObject + cf = new CFunction("void *nit_ffi_with_java_nit_object_data(JNIEnv *env, jobject nit_object)") + cf.exprs.add """ + Sys sys = Pointer_sys(NULL); + jclass java_class = Sys_load_jclass(sys, "nit/app/NitObject"); + if (java_class == NULL) { + PRINT_ERROR("Nit FFI with Java error: failed to load class NitObject.\\n"); + (*env)->ExceptionDescribe(env); + exit(1); + } + + jfieldID java_field = (*env)->GetFieldID(env, java_class, "pointer", "J"); + if (java_field == NULL) { + PRINT_ERROR("Nit FFI with Java error: NitObject field not found.\\n"); + (*env)->ExceptionDescribe(env); + exit(1); + } + + jlong data = (*env)->GetLongField(env, nit_object, java_field); + + return (void*)data; + """ + ffi_ccu.add_local_function cf + end + # Java implementation code var java_file = mmodule.java_file assert java_file != null @@ -386,8 +444,24 @@ end private class ToJavaCallContext super CallContext - redef fun cast_to(mtype, name) do return "({mtype.jni_type})({name})" - redef fun cast_from(mtype, name) do return "({mtype.cname})({name})" + redef fun cast_to(mtype, name) + do + if mtype.java_is_nit_object then + return "nit_ffi_with_java_new_nit_object(nit_ffi_jni_env, {name})" + else + return "({mtype.jni_type})({name})" + end + end + + redef fun cast_from(mtype, name) + do + if mtype.java_is_nit_object then + return "({mtype.cname})nit_ffi_with_java_nit_object_data(nit_ffi_jni_env, {name})" + else + return "({mtype.cname})({name})" + end + end + redef fun name_mtype(mtype) do return mtype.jni_type end @@ -395,8 +469,24 @@ end private class FromJavaCallContext super CallContext - redef fun cast_to(mtype, name) do return "({mtype.cname})({name})" - redef fun cast_from(mtype, name) do return "({mtype.jni_type})({name})" + redef fun cast_to(mtype, name) + do + if mtype.java_is_nit_object then + return "({mtype.cname})nit_ffi_with_java_nit_object_data(nit_ffi_jni_env, {name})" + else + return "({mtype.cname})({name})" + end + end + + redef fun cast_from(mtype, name) + do + if mtype.java_is_nit_object then + return "nit_ffi_with_java_new_nit_object(nit_ffi_jni_env, {name})" + else + return "({mtype.jni_type})({name})" + end + end + redef fun name_mtype(mtype) do return mtype.jni_type end @@ -458,21 +548,23 @@ redef class MType # # * Primitives common to both languages use their Java primitive type # * Nit extern Java classes are represented by their full Java type - # * Other Nit objects are represented by `int` in Java. It holds the - # pointer to the underlying C structure. - # TODO create static Java types to store and hide the pointer - private fun java_type: String do return "int" + # * Other Nit objects are represented by `NitObject` in Java, a class + # encapsulating the pointer to the underlying C structure. + private fun java_type: String do return "nit.app.NitObject" + + # Is this type opaque in Java? As so it is represented by `nit.app.NitObject`. + private fun java_is_nit_object: Bool do return true # JNI type name (in C) # # So this is a C type, usually defined in `jni.h` - private fun jni_type: String do return "long" + private fun jni_type: String do return "jobject" # JNI short type name (for signatures) # # Is used by `MMethod::build_jni_format` to pass a Java method signature # to the JNI function `GetStaticMetodId`. - private fun jni_format: String do return "I" + private fun jni_format: String do return "Lnit/app/NitObject;" # Type name appearing within JNI function names. # @@ -482,20 +574,22 @@ redef class MType redef fun compile_callback_to_java(mmodule, mainmodule, ccu) do + if self isa MClassType and mclass.ftype isa ForeignJavaType then return + var java_file = mmodule.java_file - if java_file == null then return + if java_file == null or mmodule.callbacks_used_from_java.callbacks.is_empty then return for variation in ["incr", "decr"] do var friendly_name = "{mangled_cname}_{variation}_ref" # C - var csignature = "void {mmodule.impl_java_class_name}_{friendly_name}(JNIEnv *env, jclass clazz, jint object)" + var csignature = "void {mmodule.impl_java_class_name}_{friendly_name}(JNIEnv *nit_ffi_jni_env, jclass clazz, jobject object)" var cf = new CFunction("JNIEXPORT {csignature}") - cf.exprs.add "\tnitni_global_ref_{variation}((void*)(long)object);" + cf.exprs.add "\tnitni_global_ref_{variation}(nit_ffi_with_java_nit_object_data(nit_ffi_jni_env, object));" ccu.add_non_static_local_function cf # Java - java_file.class_content.add "private native static void {friendly_name}(int object);\n" + java_file.class_content.add "private native static void {friendly_name}(nit.app.NitObject object);\n" end end @@ -504,7 +598,7 @@ redef class MType var arr = new Array[String] for variation in ["incr", "decr"] do var friendly_name = "{mangled_cname}_{variation}_ref" - var jni_format = "(I)V" + var jni_format = "(Lnit/app/NitObject;)V" var cname = "{from_mmodule.impl_java_class_name}_{friendly_name}" arr.add """{"{{{friendly_name}}}", "{{{jni_format}}}", {{{cname}}}}""" end @@ -532,6 +626,16 @@ redef class MClassType return super end + redef fun java_is_nit_object + do + var ftype = mclass.ftype + if ftype isa ForeignJavaType then return false + + var java_primitives = once new HashSet[String].from( + ["Bool", "Char", "Int", "Float", "Byte", "Int8", "Int16", "UInt16", "Int32", "UInt32"]) + return not java_primitives.has(mclass.name) + end + redef fun jni_type do var ftype = mclass.ftype @@ -631,6 +735,22 @@ redef class MClassType if mclass.name == "UInt32" then return "Int" return super end + + redef fun compile_callback_to_java(mmodule, mainmodule, ccu) + do + # Don't generate functions for reference counters on extern classes + if mclass.ftype != null then return + + super + end + + redef fun jni_methods_declaration(from_mmodule) + do + # Don't generate functions for reference counters on extern classes + if mclass.ftype != null then return new Array[String] + + return super + end end redef class MMethod @@ -696,8 +816,7 @@ redef class MMethod var cparams = new List[String] - # This is different - cparams.add "JNIEnv *env" + cparams.add "JNIEnv *nit_ffi_jni_env" cparams.add "jclass clazz" if not self.is_init then -- 1.7.9.5