vpp/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py
Marek Gradzki 81c7dfc1bb VPP-120: add custom types support to jvpp
Generates java classes based on typeonly definitions
(hashcode, equals and toString methods are also included).

Adds JNI handling for request and reply messages
(also arrays of custom types).

Change-Id: I16f1cea17899704426aa083fad1cb800a8d115df
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
2016-10-31 21:42:40 +00:00

342 lines
13 KiB
Python

#!/usr/bin/env python
#
# Copyright (c) 2016 Cisco and/or its affiliates.
# 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
# l
# 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.
#
import os, util
from string import Template
import jni_gen
def is_manually_generated(f_name, plugin_name):
return f_name in {'control_ping_reply'}
class_reference_template = Template("""jclass ${ref_name}Class;
""")
find_class_invocation_template = Template("""
${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${class_name}"));
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
return JNI_ERR;
}""")
find_class_template = Template("""
${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "${class_name}"));
if ((*env)->ExceptionCheck(env)) {
(*env)->ExceptionDescribe(env);
return JNI_ERR;
}""")
delete_class_invocation_template = Template("""
if (${ref_name}Class) {
(*env)->DeleteGlobalRef(env, ${ref_name}Class);
}""")
class_cache_template = Template("""
$class_references
static int cache_class_references(JNIEnv* env) {
$find_class_invocations
return 0;
}
static void delete_class_references(JNIEnv* env) {
$delete_class_invocations
}""")
def generate_class_cache(func_list, plugin_name):
class_references = []
find_class_invocations = []
delete_class_invocations = []
for f in func_list:
c_name = f['name']
class_name = util.underscore_to_camelcase_upper(c_name)
ref_name = util.underscore_to_camelcase(c_name)
if util.is_ignored(c_name) or util.is_control_ping(class_name):
continue
if util.is_reply(class_name):
class_references.append(class_reference_template.substitute(
ref_name=ref_name))
find_class_invocations.append(find_class_invocation_template.substitute(
plugin_name=plugin_name,
ref_name=ref_name,
class_name=class_name))
delete_class_invocations.append(delete_class_invocation_template.substitute(ref_name=ref_name))
elif util.is_notification(c_name):
class_references.append(class_reference_template.substitute(
ref_name=util.add_notification_suffix(ref_name)))
find_class_invocations.append(find_class_invocation_template.substitute(
plugin_name=plugin_name,
ref_name=util.add_notification_suffix(ref_name),
class_name=util.add_notification_suffix(class_name)))
delete_class_invocations.append(delete_class_invocation_template.substitute(
ref_name=util.add_notification_suffix(ref_name)))
# add exception class to class cache
ref_name = 'callbackException'
class_name = 'io/fd/vpp/jvpp/VppCallbackException'
class_references.append(class_reference_template.substitute(
ref_name=ref_name))
find_class_invocations.append(find_class_template.substitute(
ref_name=ref_name,
class_name=class_name))
delete_class_invocations.append(delete_class_invocation_template.substitute(ref_name=ref_name))
return class_cache_template.substitute(
class_references="".join(class_references), find_class_invocations="".join(find_class_invocations),
delete_class_invocations="".join(delete_class_invocations))
# TODO: cache method and field identifiers to achieve better performance
# https://jira.fd.io/browse/HONEYCOMB-42
request_class_template = Template("""
jclass requestClass = (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${java_name_upper}");""")
request_field_identifier_template = Template("""
jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${object_name}Class, "${field_name}", "${jni_signature}");
${jni_type} ${field_reference_name} = (*env)->Get${jni_getter}(env, ${object_name}, ${field_reference_name}FieldId);
""")
jni_impl_template = Template("""
/**
* JNI binding for sending ${c_name} message.
* Generated based on $inputfile preparsed data:
$api_data
*/
JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${java_plugin_name}Impl_${field_name}0
(JNIEnv * env, jclass clazz$args) {
${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
vl_api_${c_name}_t * mp;
u32 my_context_id = vppjni_get_context_id (&jvpp_main);
$request_class
// create message:
mp = vl_msg_api_alloc(sizeof(*mp));
memset (mp, 0, sizeof (*mp));
mp->_vl_msg_id = ntohs (VL_API_${c_name_uppercase} + plugin_main->msg_id_base);
mp->client_index = plugin_main->my_client_index;
mp->context = clib_host_to_net_u32 (my_context_id);
$msg_initialization
// send message:
vl_msg_api_send_shmem (plugin_main->vl_input_queue, (u8 *)&mp);
if ((*env)->ExceptionCheck(env)) {
return JNI_ERR;
}
return my_context_id;
}""")
def generate_jni_impl(func_list, plugin_name, inputfile):
jni_impl = []
for f in func_list:
f_name = f['name']
camel_case_function_name = util.underscore_to_camelcase(f_name)
if is_manually_generated(f_name, plugin_name) or util.is_reply(camel_case_function_name) \
or util.is_ignored(f_name) or util.is_just_notification(f_name):
continue
arguments = ''
request_class = ''
msg_initialization = ''
f_name_uppercase = f_name.upper()
if f['args']:
arguments = ', jobject request'
camel_case_function_name_upper = util.underscore_to_camelcase_upper(f_name)
request_class = request_class_template.substitute(
java_name_upper=camel_case_function_name_upper,
plugin_name=plugin_name)
for t in zip(f['types'], f['args'], f['lengths']):
field_name = util.underscore_to_camelcase(t[1])
msg_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=t[1],
field_reference_name=field_name,
field_name=field_name,
field_length=t[2][0],
is_variable_len_array=t[2][1])
jni_impl.append(jni_impl_template.substitute(
inputfile=inputfile,
api_data=util.api_message_to_javadoc(f),
field_reference_name=camel_case_function_name,
field_name=camel_case_function_name,
c_name_uppercase=f_name_uppercase,
c_name=f_name,
plugin_name=plugin_name,
java_plugin_name=plugin_name.title(),
request_class=request_class,
msg_initialization=msg_initialization,
args=arguments))
return "\n".join(jni_impl)
# code fragment for checking result of the operation before sending request reply
callback_err_handler_template = Template("""
// for negative result don't send callback message but send error callback
if (mp->retval<0) {
call_on_error("${handler_name}", mp->context, mp->retval, plugin_main->callbackClass, plugin_main->callbackObject, callbackExceptionClass);
return;
}
if (mp->retval == VNET_API_ERROR_IN_PROGRESS) {
clib_warning("Result in progress");
return;
}
""")
msg_handler_template = Template("""
/**
* Handler for ${handler_name} message.
* Generated based on $inputfile preparsed data:
$api_data
*/
static void vl_api_${handler_name}_t_handler (vl_api_${handler_name}_t * mp)
{
${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
JNIEnv *env = jvpp_main.jenv;
$err_handler
jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "<init>", "()V");
jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lio/fd/vpp/jvpp/${plugin_name}/dto/${dto_name};)V");
jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor);
$dto_setters
(*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto);
}""")
def generate_msg_handlers(func_list, plugin_name, inputfile):
handlers = []
for f in func_list:
handler_name = f['name']
dto_name = util.underscore_to_camelcase_upper(handler_name)
ref_name = util.underscore_to_camelcase(handler_name)
if is_manually_generated(handler_name, plugin_name) or util.is_ignored(handler_name):
continue
if not util.is_reply(dto_name) and not util.is_notification(handler_name):
continue
if util.is_notification(handler_name):
dto_name = util.add_notification_suffix(dto_name)
ref_name = util.add_notification_suffix(ref_name)
dto_setters = ''
err_handler = ''
# dto setters
for t in zip(f['types'], f['args'], f['lengths']):
c_name = t[1]
java_name = util.underscore_to_camelcase(c_name)
field_length = t[2][0]
is_variable_len_array = t[2][1]
length_field_type = None
if is_variable_len_array:
length_field_type = f['types'][f['args'].index(field_length)]
dto_setters += jni_gen.jni_reply_handler_for_type(handler_name=handler_name, ref_name=ref_name,
field_type=t[0], c_name=t[1],
field_reference_name=java_name,
field_name=java_name, field_length=field_length,
is_variable_len_array=is_variable_len_array,
length_field_type=length_field_type)
# for retval don't generate setters and generate retval check
if util.is_retval_field(c_name):
err_handler = callback_err_handler_template.substitute(
handler_name=handler_name
)
continue
handlers.append(msg_handler_template.substitute(
inputfile=inputfile,
api_data=util.api_message_to_javadoc(f),
handler_name=handler_name,
plugin_name=plugin_name,
dto_name=dto_name,
class_ref_name=ref_name,
dto_setters=dto_setters,
err_handler=err_handler))
return "\n".join(handlers)
handler_registration_template = Template("""_(${upercase_name}, ${name}) \\
""")
def generate_handler_registration(func_list):
handler_registration = ["#define foreach_api_reply_handler \\\n"]
for f in func_list:
name = f['name']
camelcase_name = util.underscore_to_camelcase(f['name'])
if (not util.is_reply(camelcase_name) and not util.is_notification(name)) or util.is_ignored(name) \
or util.is_control_ping(camelcase_name):
continue
handler_registration.append(handler_registration_template.substitute(
name=name,
upercase_name=name.upper()))
return "".join(handler_registration)
jvpp_c_template = Template("""/**
* This file contains JNI bindings for jvpp Java API.
* It was generated by jvpp_c_gen.py based on $inputfile
* (python representation of api file generated by vppapigen).
*/
// JAVA class reference cache
$class_cache
// JNI bindings
$jni_implementations
// Message handlers
$msg_handlers
// Registration of message handlers in vlib
$handler_registration
""")
def generate_jvpp(func_list, plugin_name, inputfile):
""" Generates jvpp C file """
print "Generating jvpp C"
class_cache = generate_class_cache(func_list, plugin_name)
jni_impl = generate_jni_impl(func_list, plugin_name, inputfile)
msg_handlers = generate_msg_handlers(func_list, plugin_name, inputfile)
handler_registration = generate_handler_registration(func_list)
jvpp_c_file = open("jvpp_%s_gen.h" % plugin_name, 'w')
jvpp_c_file.write(jvpp_c_template.substitute(
inputfile=inputfile,
class_cache=class_cache,
jni_implementations=jni_impl,
msg_handlers=msg_handlers,
handler_registration=handler_registration))
jvpp_c_file.flush()
jvpp_c_file.close()