From 6c8034768d3f75fd0e07a4cfc76123b3b6b71576 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 11 Oct 2017 12:53:04 +0200 Subject: [PATCH] More work on JNI layer for Java wrapper for listeners --- wrappers/java/genwrapper.py | 134 +++++++++++++++++++++++++++++++----- wrappers/java/jni.mustache | 67 +++++++++++++++++- 2 files changed, 184 insertions(+), 17 deletions(-) diff --git a/wrappers/java/genwrapper.py b/wrappers/java/genwrapper.py index 18422d5b3..d2cd01213 100644 --- a/wrappers/java/genwrapper.py +++ b/wrappers/java/genwrapper.py @@ -84,6 +84,61 @@ class JavaTranslator(object): def translate_argument_name(self, _argName): return _argName.to_snake_case() + def translate_java_jni_enum_name(self, _enum): + name = _enum.name.to_camel_case() + if name in ENUMS_LIST: + className = ENUMS_LIST[name] + if name.startswith(className): + name = name[len(className):] + name = className + '$' + name + return name + + def translate_java_jni_base_type_name(self, _type): + if _type == 'string': + return 'Ljava/lang/String;' + elif _type == 'integer': + return 'I' + elif _type == 'boolean': + return 'Z' + elif _type == 'floatant': + return 'F' + elif _type == 'size': + return 'I' + elif _type == 'time': + return 'I' + elif _type == 'status': + return 'I' + elif _type == 'string_array': + return '[Ljava/lang/String;' + elif _type == 'character': + return 'C' + elif _type == 'void': + return 'V' + return _type + + def translate_as_c_base_type(self, _type): + if _type == 'string': + return 'const char *' + elif _type == 'integer': + return 'int' + elif _type == 'boolean': + return 'bool_t' + elif _type == 'floatant': + return 'float' + elif _type == 'size': + return 'size_t' + elif _type == 'time': + return 'time_t' + elif _type == 'status': + return 'int' + elif _type == 'string_array': + return 'const char **' + elif _type == 'character': + return 'char' + elif _type == 'void': + return 'void *' + return _type + def translate_type(self, _type, native=False, jni=False, isReturn=False): if type(_type) is AbsApi.ListType: if jni: @@ -163,11 +218,10 @@ class JavaTranslator(object): return 'jchar' return 'char' elif _type.name == 'void': + if isReturn: + return 'void' if jni: - if isReturn: - return 'void' - else: - return 'jobject' + return 'jobject' return 'Object' return _type.name @@ -334,6 +388,57 @@ class JavaTranslator(object): return classDict + def translate_jni_interface(self, className, _method): + methodDict = {} + listenerName = 'Linphone' + className.to_camel_case() + methodDict['classCName'] = listenerName[:-8] #Remove Listener at the end + methodDict['className'] = className.to_camel_case() + methodDict['classImplName'] = className.to_camel_case() + 'Impl' + methodDict['jniPath'] = self.jni_path + methodDict['cPrefix'] = 'linphone_' + className.to_snake_case() + methodDict['callbackName'] = methodDict['cPrefix'] + '_' + _method.name.to_snake_case() + methodDict['jname'] = _method.name.to_camel_case(lower=True) + methodDict['return'] = self.translate_type(_method.returnType, jni=True, isReturn=True) + + methodDict['jobjects'] = [] + methodDict['jenums'] = [] + methodDict['jstrings'] = [] + methodDict['params'] = '' + methodDict['jparams'] = '(' + methodDict['params_impl'] = '' + for arg in _method.args: + argname = self.translate_argument_name(arg.name) + if arg is not _method.args[0]: + methodDict['params'] += ', ' + methodDict['params_impl'] += ', ' + + if type(arg.type) is AbsApi.ClassType: + methodDict['params'] += 'Linphone' + arg.type.desc.name.to_camel_case() + ' *' + (argname if arg is not _method.args[0] else 'cptr') + methodDict['jparams'] += 'L' + self.jni_path + arg.type.desc.name.to_camel_case() + ';' + methodDict['params_impl'] += 'j_' + argname + methodDict['jobjects'].append({'objectName': argname, 'className': arg.type.desc.name.to_camel_case(),}) + elif type(arg.type) is AbsApi.BaseType: + methodDict['params'] += self.translate_as_c_base_type(arg.type.name) + ' ' + argname + methodDict['jparams'] += self.translate_java_jni_base_type_name(arg.type.name) + if arg.type.name == 'string': + methodDict['params_impl'] += 'j_' + argname + methodDict['jstrings'].append({'stringName': argname,}) + else: + methodDict['params_impl'] += argname + elif type(arg.type) is AbsApi.EnumType: + methodDict['params'] += 'Linphone' + arg.type.desc.name.to_camel_case() + ' ' + argname + methodDict['jparams'] += 'L' + self.jni_path + self.translate_java_jni_enum_name(arg.type.desc) + ';' + methodDict['params_impl'] += 'j_' + argname + methodDict['jenums'].append({'enumName': argname, 'cEnumPrefix': 'linphone_' + arg.type.desc.name.to_snake_case()}) + + methodDict['jparams'] += ')' + if (methodDict['return'] == 'void'): + methodDict['jparams'] += 'V' + else: + pass #TODO + + return methodDict + def translate_interface(self, _class): interfaceDict = { 'methods': [], @@ -344,6 +449,7 @@ class JavaTranslator(object): for method in _class.methods: interfaceDict['methods'].append(self.translate_method(method)) + interfaceDict['jniMethods'].append(self.translate_jni_interface(_class.name, method)) return interfaceDict @@ -392,15 +498,7 @@ class JavaEnum(object): self.filename = self.className + ".java" self.values = self._class['values'] self.doc = self._class['doc'] - self.jniMethods = self._class['jniMethods'] - - name = _enum.name.to_camel_case() - if name in ENUMS_LIST: - className = ENUMS_LIST[name] - if name.startswith(className): - name = name[len(className):] - name = className + '$' + name - self.jniName = name + self.jniName = translator.translate_java_jni_enum_name(_enum) class JavaInterface(object): def __init__(self, package, _interface, translator): @@ -446,8 +544,9 @@ class JavaClass(object): class Jni(object): def __init__(self, package): - self.objects = [] self.enums = [] + self.interfaces = [] + self.objects = [] self.methods = [] self.jni_package = '' self.jni_path = '' @@ -479,6 +578,10 @@ class Jni(object): } self.objects.append(obj) + def add_interfaces(self, name, interfaces): + for interface in interfaces: + self.interfaces.append(interface) + def add_methods(self, name, methods): for method in methods: self.methods.append(method) @@ -560,7 +663,6 @@ class GenWrapper(object): self.enums[javaenum.className] = javaenum except AbsApi.Error as e: print('Could not translate {0}: {1}'.format(_class.name.to_camel_case(fullName=True), e.args[0])) - #self.jni.add_methods(javaenum.className, javaenum.jniMethods) def render_java_interface(self, _class): if _class is not None: @@ -571,7 +673,7 @@ class GenWrapper(object): self.interfaces[javaInterfaceStub.classNameStub] = javaInterfaceStub except AbsApi.Error as e: print('Could not translate {0}: {1}'.format(_class.name.to_camel_case(fullName=True), e.args[0])) - #self.jni.add_methods(javainterface.className, javainterface.jniMethods) + self.jni.add_interfaces(javainterface.className, javainterface.jniMethods) def render_java_class(self, _class): if _class is not None: diff --git a/wrappers/java/jni.mustache b/wrappers/java/jni.mustache index a43ccc4cc..00bcbf0ba 100644 --- a/wrappers/java/jni.mustache +++ b/wrappers/java/jni.mustache @@ -66,6 +66,8 @@ static jlong GetObjectNativePtr(JNIEnv *env, jobject object) { return nativePtr; } +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + class LinphoneJavaBindings { public: LinphoneJavaBindings(JNIEnv *env) { @@ -86,7 +88,7 @@ public: {{#objects}} env->DeleteGlobalRef({{cPrefix}}_class); {{/objects}} - + {{#enums}} env->DeleteGlobalRef({{cPrefix}}_class); {{/enums}} @@ -103,6 +105,8 @@ public: {{/enums}} }; +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + {{#objects}} jobject get{{className}}(JNIEnv *env, {{classCName}} *ptr) { jobject jobj = 0; @@ -143,6 +147,67 @@ void Java_{{jniPrefix}}{{classImplName}}_unref(JNIEnv* env, jobject thiz, jlong } {{/objects}} +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +static inline void handle_possible_java_exception(JNIEnv *env, jobject listener) +{ + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + ms_error("Listener %p raised an exception",listener); + } +} + +{{#interfaces}} +static {{return}} {{callbackName}}({{params}}) { + JNIEnv *env = 0; + jint result = jvm->AttachCurrentThread(&env,NULL); + LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get()); + if (result != 0) { + ms_error("cannot attach VM"); + return; + } + + {{classCName}}Cbs *cbs = {{cPrefix}}_get_callbacks(cptr); + jobject jlistener = (jobject) {{cPrefix}}_cbs_get_user_data(cbs); + + if (jlistener == NULL) { + ms_warning("{{name}}() notification without listener"); + return ; + } + + jclass jlistenerClass = (jclass) env->GetObjectClass(jlistener); + jmethodID jcallback = env->GetMethodID(jlistenerClass, "{{jname}}", "{{jparams}}"); + env->DeleteLocalRef(jlistenerClass); + + {{#jobjects}} + jobject j_{{objectName}} = get{{className}}(env, {{objectName}}); + {{/jobjects}} + {{#jenums}} + jobject j_{{enumName}} = env->CallStaticObjectMethod(ljb->{{cEnumPrefix}}_class, ljb->{{cEnumPrefix}}_class_constructor_from_int, (jint){{enumName}}); + {{/jenums}} + {{#jstrings}} + jstring j_{{stringName}} = {{stringName}} ? env->NewStringUTF({{stringName}}) : NULL; + {{/jstrings}} + + env->CallVoidMethod(jlistener, jcallback, {{params_impl}}); + + {{#jobjects}} + if (j_{{objectName}}) { + env->DeleteLocalRef(j_{{objectName}}); + } + {{/jobjects}} + {{#jstrings}} + if (j_{{stringName}}) { + env->DeleteLocalRef(j_{{stringName}}); + } + {{/jstrings}} + + handle_possible_java_exception(env, jlistener); +} + +{{/interfaces}} +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// {{#methods}} {{return}} {{name}}({{params}}) {