3 # Copyright (c) 2016 Cisco and/or its affiliates.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
18 from string import Template
23 def is_manually_generated(f_name, plugin_name):
24 return f_name in {'control_ping_reply'}
27 class_reference_template = Template("""jclass ${ref_name}Class;
30 find_class_invocation_template = Template("""
31 ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${class_name}"));
32 if ((*env)->ExceptionCheck(env)) {
33 (*env)->ExceptionDescribe(env);
37 find_class_template = Template("""
38 ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "${class_name}"));
39 if ((*env)->ExceptionCheck(env)) {
40 (*env)->ExceptionDescribe(env);
44 delete_class_invocation_template = Template("""
45 if (${ref_name}Class) {
46 (*env)->DeleteGlobalRef(env, ${ref_name}Class);
49 class_cache_template = Template("""
51 static int cache_class_references(JNIEnv* env) {
52 $find_class_invocations
56 static void delete_class_references(JNIEnv* env) {
57 $delete_class_invocations
61 def generate_class_cache(func_list, plugin_name):
63 find_class_invocations = []
64 delete_class_invocations = []
67 class_name = util.underscore_to_camelcase_upper(c_name)
68 ref_name = util.underscore_to_camelcase(c_name)
70 if util.is_ignored(c_name) or util.is_control_ping(class_name):
73 class_references.append(class_reference_template.substitute(
75 find_class_invocations.append(find_class_invocation_template.substitute(
76 plugin_name=plugin_name,
78 class_name=class_name))
79 delete_class_invocations.append(delete_class_invocation_template.substitute(ref_name=ref_name))
81 # add exception class to class cache
82 ref_name = 'callbackException'
83 class_name = 'io/fd/vpp/jvpp/VppCallbackException'
84 class_references.append(class_reference_template.substitute(
86 find_class_invocations.append(find_class_template.substitute(
88 class_name=class_name))
89 delete_class_invocations.append(delete_class_invocation_template.substitute(ref_name=ref_name))
91 return class_cache_template.substitute(
92 class_references="".join(class_references), find_class_invocations="".join(find_class_invocations),
93 delete_class_invocations="".join(delete_class_invocations))
96 # TODO: cache method and field identifiers to achieve better performance
97 # https://jira.fd.io/browse/HONEYCOMB-42
98 request_class_template = Template("""
99 jclass requestClass = (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${java_name_upper}");""")
101 request_field_identifier_template = Template("""
102 jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${object_name}Class, "${field_name}", "${jni_signature}");
103 ${jni_type} ${field_reference_name} = (*env)->Get${jni_getter}(env, ${object_name}, ${field_reference_name}FieldId);
106 jni_msg_size_template = Template(""" + ${array_length}*sizeof(${element_type})""")
108 jni_impl_template = Template("""
110 * JNI binding for sending ${c_name} message.
111 * Generated based on $inputfile preparsed data:
114 JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${java_plugin_name}Impl_${field_name}0
115 (JNIEnv * env, jclass clazz$args) {
116 ${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
117 vl_api_${c_name}_t * mp;
118 u32 my_context_id = vppjni_get_context_id (&jvpp_main);
124 mp = vl_msg_api_alloc(${msg_size});
125 memset (mp, 0, ${msg_size});
126 mp->_vl_msg_id = ntohs (get_message_id(env, "${c_name}_${crc}"));
127 mp->client_index = plugin_main->my_client_index;
128 mp->context = clib_host_to_net_u32 (my_context_id);
134 clib_warning ("Sending ${field_name} event message");
135 vl_msg_api_send_shmem (plugin_main->vl_input_queue, (u8 *)&mp);
136 if ((*env)->ExceptionCheck(env)) {
139 return my_context_id;
142 def generate_jni_impl(func_list, plugin_name, inputfile):
146 camel_case_function_name = util.underscore_to_camelcase(f_name)
147 if is_manually_generated(f_name, plugin_name) or util.is_reply(camel_case_function_name) \
148 or util.is_ignored(f_name) or util.is_just_notification(f_name):
154 msg_initialization = ''
155 f_name_uppercase = f_name.upper()
156 msg_size = 'sizeof(*mp)'
159 arguments = ', jobject request'
160 camel_case_function_name_upper = util.underscore_to_camelcase_upper(f_name)
162 request_class = request_class_template.substitute(
163 java_name_upper=camel_case_function_name_upper,
164 plugin_name=plugin_name)
166 for t in zip(f['types'], f['args'], f['lengths'], f['arg_types']):
167 field_name = util.underscore_to_camelcase(t[1])
168 is_variable_len_array = t[2][1]
169 if is_variable_len_array:
170 msg_size += jni_msg_size_template.substitute(array_length=util.underscore_to_camelcase(t[2][0]),
172 jni_identifiers += jni_gen.jni_request_identifiers_for_type(field_type=t[0],
173 field_reference_name=field_name,
174 field_name=field_name)
175 msg_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=t[1],
176 field_reference_name=field_name,
177 field_length=t[2][0],
178 is_variable_len_array=is_variable_len_array)
180 jni_impl.append(jni_impl_template.substitute(
182 api_data=util.api_message_to_javadoc(f),
183 field_reference_name=camel_case_function_name,
184 field_name=camel_case_function_name,
185 c_name_uppercase=f_name_uppercase,
188 plugin_name=plugin_name,
189 java_plugin_name=plugin_name.title(),
190 request_class=request_class,
191 jni_identifiers=jni_identifiers,
193 msg_initialization=msg_initialization,
196 return "\n".join(jni_impl)
198 # code fragment for checking result of the operation before sending request reply
199 callback_err_handler_template = Template("""
200 // for negative result don't send callback message but send error callback
202 call_on_error("${handler_name}", mp->context, mp->retval, plugin_main->callbackClass, plugin_main->callbackObject, callbackExceptionClass);
205 if (mp->retval == VNET_API_ERROR_IN_PROGRESS) {
206 clib_warning("Result in progress");
211 msg_handler_template = Template("""
213 * Handler for ${handler_name} message.
214 * Generated based on $inputfile preparsed data:
217 static void vl_api_${handler_name}_t_handler (vl_api_${handler_name}_t * mp)
219 ${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
220 JNIEnv *env = jvpp_main.jenv;
225 clib_warning ("Received ${handler_name} event message");
227 jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "<init>", "()V");
229 // User does not have to provide callbacks for all VPP messages.
230 // We are ignoring messages that are not supported by user.
231 (*env)->ExceptionClear(env); // just in case exception occurred in different place and was not properly cleared
232 jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lio/fd/vpp/jvpp/${plugin_name}/dto/${dto_name};)V");
233 exc = (*env)->ExceptionOccurred(env);
235 clib_warning("Unable to extract on${dto_name} method reference from ${plugin_name} plugin's callbackClass. Ignoring message.\\n");
236 (*env)->ExceptionDescribe(env);
237 (*env)->ExceptionClear(env);
241 jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor);
244 (*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto);
245 // free DTO as per http://stackoverflow.com/questions/1340938/memory-leak-when-calling-java-code-from-c-using-jni
246 (*env)->DeleteLocalRef(env, dto);
250 def generate_msg_handlers(func_list, plugin_name, inputfile):
253 handler_name = f['name']
254 dto_name = util.underscore_to_camelcase_upper(handler_name)
255 ref_name = util.underscore_to_camelcase(handler_name)
257 if is_manually_generated(handler_name, plugin_name) or util.is_ignored(handler_name):
260 if not util.is_reply(dto_name) and not util.is_notification(handler_name):
266 for t in zip(f['types'], f['args'], f['lengths']):
268 java_name = util.underscore_to_camelcase(c_name)
269 field_length = t[2][0]
270 is_variable_len_array = t[2][1]
271 length_field_type = None
272 if is_variable_len_array:
273 length_field_type = f['types'][f['args'].index(field_length)]
274 dto_setters += jni_gen.jni_reply_handler_for_type(handler_name=handler_name, ref_name=ref_name,
275 field_type=t[0], c_name=t[1],
276 field_reference_name=java_name,
277 field_name=java_name, field_length=field_length,
278 is_variable_len_array=is_variable_len_array,
279 length_field_type=length_field_type)
281 # for retval don't generate setters and generate retval check
282 if util.is_retval_field(c_name):
283 err_handler = callback_err_handler_template.substitute(
284 handler_name=handler_name
288 handlers.append(msg_handler_template.substitute(
290 api_data=util.api_message_to_javadoc(f),
291 handler_name=handler_name,
292 plugin_name=plugin_name,
294 class_ref_name=ref_name,
295 dto_setters=dto_setters,
296 err_handler=err_handler))
298 return "\n".join(handlers)
301 handler_registration_template = Template("""_(${name}_${crc}, ${name}) \\
305 def generate_handler_registration(func_list):
306 handler_registration = ["#define foreach_api_reply_handler \\\n"]
309 camelcase_name = util.underscore_to_camelcase(f['name'])
311 if (not util.is_reply(camelcase_name) and not util.is_notification(name)) or util.is_ignored(name) \
312 or util.is_control_ping(camelcase_name):
315 handler_registration.append(handler_registration_template.substitute(
319 return "".join(handler_registration)
322 api_verification_template = Template("""_(${name}_${crc}) \\
326 def generate_api_verification(func_list):
327 api_verification = ["#define foreach_supported_api_message \\\n"]
331 if util.is_ignored(name):
334 api_verification.append(api_verification_template.substitute(
338 return "".join(api_verification)
341 jvpp_c_template = Template("""/**
342 * This file contains JNI bindings for jvpp Java API.
343 * It was generated by jvpp_c_gen.py based on $inputfile
344 * (python representation of api file generated by vppapigen).
347 // JAVA class reference cache
350 // List of supported API messages used for verification
359 // Registration of message handlers in vlib
360 $handler_registration
363 def generate_jvpp(func_list, plugin_name, inputfile, path):
364 """ Generates jvpp C file """
365 print "Generating jvpp C"
367 class_cache = generate_class_cache(func_list, plugin_name)
368 jni_impl = generate_jni_impl(func_list, plugin_name, inputfile)
369 msg_handlers = generate_msg_handlers(func_list, plugin_name, inputfile)
370 handler_registration = generate_handler_registration(func_list)
371 api_verification = generate_api_verification(func_list)
373 jvpp_c_file = open("%s/jvpp_%s_gen.h" % (path, plugin_name), 'w')
374 jvpp_c_file.write(jvpp_c_template.substitute(
376 class_cache=class_cache,
377 api_verification=api_verification,
378 jni_implementations=jni_impl,
379 msg_handlers=msg_handlers,
380 handler_registration=handler_registration))