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>
This commit is contained in:
Marek Gradzki
2016-09-29 13:22:35 +02:00
committed by Damjan Marion
parent c967a8239d
commit 81c7dfc1bb
10 changed files with 779 additions and 342 deletions

View File

@ -92,11 +92,13 @@ jvpp-core/io_fd_vpp_jvpp_core_JVppCoreImpl.h: jvpp-registry/io_fd_vpp_jvpp_VppJN
cp -rf @srcdir@/jvpp-core/* -t jvpp-core/
mkdir -p jvpp-core/target
cd jvpp-core \
&& mkdir -p dto future callfacade callback notification \
&& mkdir -p types dto future callfacade callback notification \
&& @srcdir@/jvpp/gen/jvpp_gen.py -i defs_vpp_papi.py --plugin_name core \
&& cp -rf dto future callfacade callback notification *.java -t $(packagedir_jvpp_core) \
&& rm -rf dto future callfacade callback notification *.java
&& cp -rf types dto future callfacade callback notification *.java -t $(packagedir_jvpp_core) \
&& rm -rf types dto future callfacade callback notification *.java
$(JAVAC) -classpath jvpp-registry/target -d jvpp-core/target jvpp-core/$(packagedir_jvpp_core)/*.java \
jvpp-core/$(packagedir_jvpp_core)/types/*.java \
jvpp-core/$(packagedir_jvpp_core)/dto/*.java \
jvpp-core/$(packagedir_jvpp_core)/callback/*.java \
jvpp-core/$(packagedir_jvpp_core)/notification/*.java \

View File

@ -0,0 +1,124 @@
/*
* 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
*
* 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 io.fd.vpp.jvpp.core.test;
import io.fd.vpp.jvpp.JVppRegistry;
import io.fd.vpp.jvpp.JVppRegistryImpl;
import io.fd.vpp.jvpp.core.JVppCoreImpl;
import io.fd.vpp.jvpp.core.dto.LispAddDelAdjacency;
import io.fd.vpp.jvpp.core.dto.LispAddDelLocalEid;
import io.fd.vpp.jvpp.core.dto.LispAddDelLocatorSet;
import io.fd.vpp.jvpp.core.dto.LispAddDelRemoteMapping;
import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGet;
import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGetReply;
import io.fd.vpp.jvpp.core.dto.LispEnableDisable;
import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutionException;
import java.util.logging.Logger;
/**
* Tests lisp adjacency creation and read (custom vpe.api type support showcase).
*/
public class LispAdjacencyTest {
private static final Logger LOG = Logger.getLogger(LispAdjacencyTest.class.getName());
private static void enableLisp(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
final LispEnableDisable request = new LispEnableDisable();
request.isEn = 1;
jvpp.lispEnableDisable(request).toCompletableFuture().get();
LOG.info("Lisp enabled successfully");
}
private static void addLocatorSet(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
final LispAddDelLocatorSet request = new LispAddDelLocatorSet();
request.isAdd = 1;
request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8);
jvpp.lispAddDelLocatorSet(request).toCompletableFuture().get();
LOG.info("Locator set created successfully:" + request.toString());
}
private static void addLocalEid(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
final LispAddDelLocalEid request = new LispAddDelLocalEid();
request.isAdd = 1;
request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8);
request.eid = new byte[] {1, 2, 1, 10};
request.eidType = 0; // ip4
request.vni = 0;
request.prefixLen = 32;
jvpp.lispAddDelLocalEid(request).toCompletableFuture().get();
LOG.info("Local EID created successfully:" + request.toString());
}
private static void addRemoteMapping(final FutureJVppCoreFacade jvpp)
throws ExecutionException, InterruptedException {
final LispAddDelRemoteMapping request = new LispAddDelRemoteMapping();
request.isAdd = 1;
request.vni = 0;
request.eid = new byte[] {1, 2, 1, 20};
request.eidLen = 32;
request.rlocNum = 1;
request.rlocs = new byte[] {1, 1, 1, 1, 2, 1, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
jvpp.lispAddDelRemoteMapping(request).toCompletableFuture().get();
LOG.info("Remote mapping created successfully:" + request.toString());
}
private static void addAdjacency(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
final LispAddDelAdjacency request = new LispAddDelAdjacency();
request.isAdd = 1;
request.leid = new byte[] {1, 2, 1, 10};
request.leidLen = 32;
request.reid = new byte[] {1, 2, 1, 20};
request.reidLen = 32;
request.eidType = 0; // ip4
request.vni = 0;
jvpp.lispAddDelAdjacency(request).toCompletableFuture().get();
LOG.info("Lisp adjacency created successfully:" + request.toString());
}
private static void showAdjacencies(final FutureJVppCoreFacade jvpp)
throws ExecutionException, InterruptedException {
final LispAdjacenciesGetReply reply =
jvpp.lispAdjacenciesGet(new LispAdjacenciesGet()).toCompletableFuture().get();
LOG.info("Lisp adjacency received successfully:" + reply.toString());
}
private static void testAdjacency(final FutureJVppCoreFacade jvpp) throws Exception {
enableLisp(jvpp);
addLocatorSet(jvpp);
addLocalEid(jvpp);
addRemoteMapping(jvpp);
addAdjacency(jvpp);
showAdjacencies(jvpp);
}
private static void testFutureApi() throws Exception {
LOG.info("Create lisp adjacency test");
try (final JVppRegistry registry = new JVppRegistryImpl("LispAdjacencyTest");
final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
LOG.info("Successfully connected to VPP");
testAdjacency(jvppFacade);
LOG.info("Disconnecting...");
}
}
public static void main(String[] args) throws Exception {
testFutureApi();
}
}

View File

@ -14,3 +14,4 @@ CreateSubInterfaceTest - Tests sub-interface creation
FutureApiNotificationTest - Tests interface notifications using Future based JVpp facade
FutureApiTest - Execution of more complex calls using Future based JVpp facade
L2AclTest - Tests L2 ACL creation
LispAdjacencyTest - Tests lisp adjacency creation and read (custom vpe.api type support showcase)

View File

@ -71,8 +71,8 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_core_JVppCoreImpl_init0
vl_msg_api_set_handlers(VL_API_##N, #n, \
vl_api_##n##_t_handler, \
vl_noop_handler, \
vl_api_##n##_t_endian, \
vl_api_##n##_t_print, \
vl_noop_handler, \
vl_noop_handler, \
sizeof(vl_api_##n##_t), 1);
foreach_api_reply_handler;
#undef _

View File

@ -19,6 +19,7 @@ import importlib
import sys
import os
from jvppgen import types_gen
from jvppgen import callback_gen
from jvppgen import notification_gen
from jvppgen import dto_gen
@ -37,7 +38,6 @@ from jvppgen import util
#
# where
# defs_api_vpp_papi.py - vpe.api in python format (generated by vppapigen)
from jvppgen.util import vpp_2_jni_type_mapping
parser = argparse.ArgumentParser(description='VPP Java API generator')
parser.add_argument('-i', action="store", dest="inputfile")
@ -47,7 +47,7 @@ args = parser.parse_args()
sys.path.append(".")
print "args.inputfile %s" % args.inputfile
print "Generating Java API for %s" % args.inputfile
importdir = os.path.dirname(args.inputfile)
print "importdir %s" % importdir
inputfile = os.path.basename(args.inputfile)
@ -60,13 +60,6 @@ print "control_ping_class %s" % control_ping_class
sys.path.append(importdir)
cfg = importlib.import_module(inputfile, package=None)
# FIXME: functions unsupported due to problems with vpe.api
def is_supported(f_name):
return f_name not in {'vnet_ip4_fib_counters', 'vnet_ip6_fib_counters',
'lisp_adjacencies_get_reply', 'lisp_adjacencies_get'}
def is_request_field(field_name):
return field_name not in {'_vl_msg_id', 'client_index', 'context'}
@ -86,60 +79,52 @@ def get_args(t, filter):
def get_types(t, filter):
types_list = []
c_types_list = []
lengths_list = []
for i in t:
if not filter(i[1]):
continue
if len(i) is 3: # array type
types_list.append(vpp_2_jni_type_mapping[i[0]] + 'Array')
c_types_list.append(i[0] + '[]')
types_list.append(i[0] + '[]')
lengths_list.append((i[2], False))
elif len(i) is 4: # variable length array type
types_list.append(vpp_2_jni_type_mapping[i[0]] + 'Array')
c_types_list.append(i[0] + '[]')
types_list.append(i[0] + '[]')
lengths_list.append((i[3], True))
else: # primitive type
types_list.append(vpp_2_jni_type_mapping[i[0]])
c_types_list.append(i[0])
types_list.append(i[0])
lengths_list.append((0, False))
return types_list, c_types_list, lengths_list
return types_list, lengths_list
def get_definitions():
def get_definitions(defs):
# Pass 1
func_list = []
func_name = {}
for a in cfg.messages:
if not is_supported(a[0]):
continue
for a in defs:
java_name = util.underscore_to_camelcase(a[0])
# For replies include all the arguments except message_id
if util.is_reply(java_name):
types, c_types, lengths = get_types(a[1:], is_response_field)
types, lengths = get_types(a[1:], is_response_field)
func_name[a[0]] = dict(
[('name', a[0]), ('java_name', java_name),
('args', get_args(a[1:], is_response_field)), ('full_args', get_args(a[1:], lambda x: True)),
('types', types), ('c_types', c_types), ('lengths', lengths)])
('types', types), ('lengths', lengths)])
# For requests skip message_id, client_id and context
else:
types, c_types, lengths = get_types(a[1:], is_request_field)
types, lengths = get_types(a[1:], is_request_field)
func_name[a[0]] = dict(
[('name', a[0]), ('java_name', java_name),
('args', get_args(a[1:], is_request_field)), ('full_args', get_args(a[1:], lambda x: True)),
('types', types), ('c_types', c_types), ('lengths', lengths)])
('types', types), ('lengths', lengths)])
# Indexed by name
func_list.append(func_name[a[0]])
return func_list, func_name
func_list, func_name = get_definitions()
base_package = 'io.fd.vpp.jvpp'
plugin_package = base_package + '.' + plugin_name
types_package = 'types'
dto_package = 'dto'
callback_package = 'callback'
notification_package = 'notification'
@ -148,6 +133,11 @@ future_package = 'future'
callback_facade_package = 'callfacade'
control_ping_class_fqn = "%s.%s.%s" % (plugin_package, dto_package, control_ping_class)
types_list, types_name = get_definitions(cfg.types)
types_gen.generate_types(types_list, plugin_package, types_package, inputfile)
func_list, func_name = get_definitions(cfg.messages)
dto_gen.generate_dtos(func_list, base_package, plugin_package, plugin_name.title(), dto_package, args.inputfile)
jvpp_impl_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name, control_ping_class_fqn, dto_package, args.inputfile)
callback_gen.generate_callbacks(func_list, base_package, plugin_package, plugin_name.title(), callback_package, dto_package, args.inputfile)
@ -155,3 +145,5 @@ notification_gen.generate_notification_registry(func_list, base_package, plugin_
jvpp_c_gen.generate_jvpp(func_list, plugin_name, args.inputfile)
jvpp_future_facade_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name.title(), dto_package, callback_package, notification_package, future_package, args.inputfile)
jvpp_callback_facade_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name.title(), dto_package, callback_package, notification_package, callback_facade_package, args.inputfile)
print "Java API for %s generated successfully" % args.inputfile

View File

@ -280,7 +280,7 @@ def generate_dump_reply_dto(request_dto_name, base_package, plugin_package, dto_
cls_name = camel_case_dto_name + dump_dto_suffix
# using artificial type for fields, just to bypass the is_array check in base methods generators
# the type is not really used
artificial_type = 'jstring'
artificial_type = 'u8'
# In case of already existing artificial reply dump DTO, just update it
# Used for sub-dump dtos

View File

@ -0,0 +1,291 @@
#!/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
#
# 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.
from string import Template
import util
variable_length_array_value_template = Template("""mp->${length_var_name}""")
variable_length_array_template = Template("""clib_net_to_host_${length_field_type}(${value})""")
dto_field_id_template = Template("""
jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_name}", "${jni_signature}");""")
default_dto_field_setter_template = Template("""
(*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, mp->${c_name});
""")
variable_length_array_value_template = Template("""mp->${length_var_name}""")
variable_length_array_template = Template("""clib_net_to_host_${length_field_type}(${value})""")
u16_dto_field_setter_template = Template("""
(*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u16(mp->${c_name}));
""")
u32_dto_field_setter_template = Template("""
(*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u32(mp->${c_name}));
""")
u64_dto_field_setter_template = Template("""
(*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u64(mp->${c_name}));
""")
u8_array_dto_field_setter_template = Template("""
jbyteArray ${field_reference_name} = (*env)->NewByteArray(env, ${field_length});
(*env)->SetByteArrayRegion(env, ${field_reference_name}, 0, ${field_length}, (const jbyte*)mp->${c_name});
(*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name});
""")
u16_array_dto_field_setter_template = Template("""
{
jshortArray ${field_reference_name} = (*env)->NewShortArray(env, ${field_length});
jshort * ${field_reference_name}ArrayElements = (*env)->GetShortArrayElements(env, ${field_reference_name}, NULL);
unsigned int _i;
for (_i = 0; _i < ${field_length}; _i++) {
${field_reference_name}ArrayElements[_i] = clib_net_to_host_u16(mp->${c_name}[_i]);
}
(*env)->ReleaseShortArrayElements(env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
(*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name});
}
""")
u32_array_dto_field_setter_template = Template("""
{
jintArray ${field_reference_name} = (*env)->NewIntArray(env, ${field_length});
jint * ${field_reference_name}ArrayElements = (*env)->GetIntArrayElements(env, ${field_reference_name}, NULL);
unsigned int _i;
for (_i = 0; _i < ${field_length}; _i++) {
${field_reference_name}ArrayElements[_i] = clib_net_to_host_u32(mp->${c_name}[_i]);
}
(*env)->ReleaseIntArrayElements(env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
(*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name});
}
""")
# For each u64 array we get its elements. Then we convert values to host byte order.
# All changes to jlong* buffer are written to jlongArray (isCopy is set to NULL)
u64_array_dto_field_setter_template = Template("""
{
jlongArray ${field_reference_name} = (*env)->NewLongArray(env, ${field_length});
jlong * ${field_reference_name}ArrayElements = (*env)->GetLongArrayElements(env, ${field_reference_name}, NULL);
unsigned int _i;
for (_i = 0; _i < ${field_length}; _i++) {
${field_reference_name}ArrayElements[_i] = clib_net_to_host_u64(mp->${c_name}[_i]);
}
(*env)->ReleaseLongArrayElements(env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
(*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name});
}
""")
dto_field_setter_templates = {'u8': default_dto_field_setter_template,
'u16': u16_dto_field_setter_template,
'u32': u32_dto_field_setter_template,
'i32': u32_dto_field_setter_template,
'u64': u64_dto_field_setter_template,
'f64': default_dto_field_setter_template, # fixme
'u8[]': u8_array_dto_field_setter_template,
'u16[]': u16_array_dto_field_setter_template,
'u32[]': u32_array_dto_field_setter_template,
'u64[]': u64_array_dto_field_setter_template
}
def jni_reply_handler_for_type(handler_name, ref_name, field_type, c_name, field_reference_name,
field_name, field_length, is_variable_len_array, length_field_type,
object_name="dto"):
"""
Generates jni code that initializes a field of java object (dto or custom type).
To be used in reply message handlers.
:param field_type: type of the field to be initialized (as defined in vpe.api)
:param c_name: name of the message struct member that stores initialization value
:param field_reference_name: name of the field reference in generated code
:param field_name: name of the field (camelcase)
:param field_length: integer or name of variable that stores field length
:param object_name: name of the object to be initialized
"""
# todo move validation to vppapigen
if field_type.endswith('[]') and field_length == '0':
raise Exception('Variable array \'%s\' defined in \'%s\' '
'should have defined length (e.g. \'%s[%s_length]\''
% (c_name, handler_name, c_name, c_name))
if is_variable_len_array:
length_var_name = field_length
field_length = variable_length_array_value_template.substitute(length_var_name=length_var_name)
if length_field_type != 'u8': # we need net to host conversion:
field_length = variable_length_array_template.substitute(
length_field_type=length_field_type, value=field_length)
# for retval don't generate setters
if util.is_retval_field(c_name):
return ""
jni_signature = util.jni_2_signature_mapping[field_type]
jni_setter = util.jni_field_accessors[field_type]
result = dto_field_id_template.substitute(
field_reference_name=field_reference_name,
field_name=field_name,
class_ref_name=ref_name,
jni_signature=jni_signature)
dto_setter_template = dto_field_setter_templates[field_type]
result += dto_setter_template.substitute(
jni_signature=jni_signature,
object_name=object_name,
field_reference_name=field_reference_name,
c_name=c_name,
jni_setter=jni_setter,
field_length=field_length)
return result
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);
""")
array_length_enforcement_template = Template("""
size_t max_size = ${field_length};
if (cnt > max_size) cnt = max_size;""")
u8_struct_setter_template = Template("""
mp->${c_name} = ${field_reference_name};""")
u16_struct_setter_template = Template("""
mp->${c_name} = clib_host_to_net_u16(${field_reference_name});""")
u32_struct_setter_template = Template("""
mp->${c_name} = clib_host_to_net_u32(${field_reference_name});""")
i32_struct_setter_template = Template("""
mp->${c_name} = clib_host_to_net_i32(${field_reference_name});!""")
u64_struct_setter_template = Template("""
mp->${c_name} = clib_host_to_net_u64(${field_reference_name});""")
array_length_enforcement_template = Template("""
size_t max_size = ${field_length};
if (cnt > max_size) cnt = max_size;""")
u8_array_struct_setter_template = Template("""
if (${field_reference_name}) {
jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
${field_length_check}
(*env)->GetByteArrayRegion(env, ${field_reference_name}, 0, cnt, (jbyte *)mp->${c_name});
}
""")
u16_array_struct_setter_template = Template("""
jshort * ${field_reference_name}ArrayElements = (*env)->GetShortArrayElements(env, ${field_reference_name}, NULL);
if (${field_reference_name}) {
size_t _i;
jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
${field_length_check}
for (_i = 0; _i < cnt; _i++) {
mp->${c_name}[_i] = clib_host_to_net_u16(${field_reference_name}ArrayElements[_i]);
}
}
(*env)->ReleaseShortArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
""")
u32_array_struct_setter_template = Template("""
jint * ${field_reference_name}ArrayElements = (*env)->GetIntArrayElements(env, ${field_reference_name}, NULL);
if (${field_reference_name}) {
size_t _i;
jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
${field_length_check}
for (_i = 0; _i < cnt; _i++) {
mp->${c_name}[_i] = clib_host_to_net_u32(${field_reference_name}ArrayElements[_i]);
}
}
(*env)->ReleaseIntArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
""")
u64_array_struct_setter_template = Template("""
jlong * ${field_reference_name}ArrayElements = (*env)->GetLongArrayElements(env, ${field_reference_name}, NULL);
if (${field_reference_name}) {
size_t _i;
jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
${field_length_check}
for (_i = 0; _i < cnt; _i++) {
mp->${c_name}[_i] = clib_host_to_net_u64(${field_reference_name}ArrayElements[_i]);
}
}
(*env)->ReleaseLongArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
""")
struct_setter_templates = {'u8': u8_struct_setter_template,
'u16': u16_struct_setter_template,
'u32': u32_struct_setter_template,
'i32': u32_struct_setter_template,
'u64': u64_struct_setter_template,
'u8[]': u8_array_struct_setter_template,
'u16[]': u16_array_struct_setter_template,
'u32[]': u32_array_struct_setter_template,
'u64[]': u64_array_struct_setter_template
}
def jni_request_binding_for_type(field_type, c_name, field_reference_name, field_name, field_length,
is_variable_len_array, object_name="request"):
"""
Generates jni code that initializes C structure that corresponds to a field of java object
(dto or custom type). To be used in request message handlers.
:param field_type: type of the field to be initialized (as defined in vpe.api)
:param c_name: name of the message struct member to be initialized
:param field_reference_name: name of the field reference in generated code
:param field_name: name of the field (camelcase)
:param field_length: integer or name of variable that stores field length
:param object_name: name of the object to be initialized
"""
# field identifiers
jni_type = util.vpp_2_jni_type_mapping[field_type]
jni_signature = util.jni_2_signature_mapping[field_type]
jni_getter = util.jni_field_accessors[field_type]
# field identifier
msg_initialization = request_field_identifier_template.substitute(
jni_type=jni_type,
field_reference_name=field_reference_name,
field_name=field_name,
jni_signature=jni_signature,
jni_getter=jni_getter,
object_name=object_name)
# field setter
field_length_check = ""
# check if we are processing variable length array:
if is_variable_len_array:
field_length = util.underscore_to_camelcase(field_length)
# enforce max length if array has fixed length or uses variable length syntax
if str(field_length) != "0":
field_length_check = array_length_enforcement_template.substitute(field_length=field_length)
struct_setter_template = struct_setter_templates[field_type]
msg_initialization += struct_setter_template.substitute(
c_name=c_name,
field_reference_name=field_reference_name,
field_length_check=field_length_check)
return msg_initialization

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,227 @@
#!/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
#
# 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
from string import Template
import util
import jni_gen
import dto_gen
type_template = Template("""
package $plugin_package.$type_package;
/**
* <p>This class represents $c_type_name type definition.
* <br>It was generated by types_gen.py based on $inputfile preparsed data:
* <pre>
$docs
* </pre>
*/
public final class $java_type_name {
$fields
$methods
}
""")
field_template = Template(""" public $type $name;\n""")
def generate_type_fields(type_definition):
"""
Generates fields for class representing typeonly definition
:param type_definition: python representation of typeonly definition
:return: string representing class fields
"""
fields = ""
for t in zip(type_definition['types'], type_definition['args']):
field_name = util.underscore_to_camelcase(t[1])
fields += field_template.substitute(type=util.jni_2_java_type_mapping[t[0]],
name=field_name)
return fields
object_struct_setter_template = Template("""
{
jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
memset (&(mp->${c_name}), 0, sizeof (mp->${c_name}));
${struct_initialization}
}
""")
object_array_struct_setter_template = Template("""
{
jclass ${field_reference_name}ArrayElementClass = (*env)->FindClass(env, "${class_FQN}");
if (${field_reference_name}) {
size_t _i;
jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
${field_length_check}
for (_i = 0; _i < cnt; _i++) {
jobject ${field_reference_name}ArrayElement = (*env)->GetObjectArrayElement(env, ${field_reference_name}, _i);
memset (&(mp->${c_name}[_i]), 0, sizeof (mp->${c_name}[_i]));
${struct_initialization}
}
}
}
""")
object_dto_field_setter_template = Template("""
{
jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V");
jobject ${field_reference_name} = (*env)->NewObject(env, ${field_reference_name}Class, ${field_reference_name}Constructor);
${type_initialization}
(*env)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${field_reference_name});
}
""")
object_array_dto_field_setter_template = Template("""
{
jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0);
unsigned int _i;
for (_i = 0; _i < ${field_length}; _i++) {
jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V");
jobject ${field_reference_name}ArrayElement = (*env)->NewObject(env, ${field_reference_name}Class, ${field_reference_name}Constructor);
${type_initialization}
(*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement);
}
(*env)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${field_reference_name});
}
""")
def generate_struct_initialization(type_def, c_name_prefix, object_name, indent):
struct_initialization = ""
# field identifiers
for t in zip(type_def['types'], type_def['args'], type_def['lengths']):
field_reference_name = "${c_name}" + util.underscore_to_camelcase_upper(t[1])
field_name = util.underscore_to_camelcase(t[1])
struct_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=c_name_prefix + t[1],
field_reference_name=field_reference_name,
field_name=field_name,
field_length=t[2][0],
is_variable_len_array=t[2][1],
object_name=object_name)
return indent + struct_initialization.replace('\n', '\n' + indent)
def generate_type_setter(handler_name, type_def, c_name_prefix, object_name, indent):
type_initialization = ""
for t in zip(type_def['types'], type_def['args'], type_def['lengths']):
field_length = t[2][0]
is_variable_len_array = t[2][1]
length_field_type = None
if is_variable_len_array:
length_field_type = type_def['types'][type_def['args'].index(field_length)]
type_initialization += jni_gen.jni_reply_handler_for_type(handler_name=handler_name,
ref_name="${field_reference_name}",
field_type=t[0], c_name=c_name_prefix + t[1],
field_reference_name="${c_name}" + util.underscore_to_camelcase_upper(t[1]),
field_name=util.underscore_to_camelcase(t[1]),
field_length=field_length,
is_variable_len_array=is_variable_len_array,
length_field_type=length_field_type,
object_name=object_name)
return indent + type_initialization.replace('\n', '\n' + indent)
def generate_types(types_list, plugin_package, types_package, inputfile):
"""
Generates Java representation of custom types defined in api file.
"""
#
if not types_list:
print "Skipping custom types generation (%s does not define custom types)." % inputfile
return
print "Generating custom types"
if not os.path.exists(types_package):
raise Exception("%s folder is missing" % types_package)
for type in types_list:
c_type_name = type['name']
java_type_name = util.underscore_to_camelcase_upper(type['name'])
dto_path = os.path.join(types_package, java_type_name + ".java")
fields = generate_type_fields(type)
dto_file = open(dto_path, 'w')
dto_file.write(type_template.substitute(plugin_package=plugin_package,
type_package=types_package,
c_type_name=c_type_name,
inputfile=inputfile,
docs=util.api_message_to_javadoc(type),
java_type_name=java_type_name,
fields=fields,
methods=dto_gen.generate_dto_base_methods(java_type_name, type)
))
# update type mappings:
# todo fix vpe.api to use type_name instead of vl_api_type_name_t
type_name = "vl_api_" + c_type_name + "_t"
java_fqn = "%s.%s.%s" % (plugin_package, types_package, java_type_name)
util.vpp_2_jni_type_mapping[type_name] = "jobject"
util.vpp_2_jni_type_mapping[type_name + "[]"] = "jobjectArray"
util.jni_2_java_type_mapping[type_name] = java_fqn
util.jni_2_java_type_mapping[type_name + "[]"] = java_fqn + "[]"
jni_name = java_fqn.replace('.', "/")
jni_signature = "L" + jni_name + ";"
util.jni_2_signature_mapping[type_name] = "L" + jni_name + ";"
util.jni_2_signature_mapping[type_name + "[]"] = "[" + jni_signature
util.jni_field_accessors[type_name] = "ObjectField"
util.jni_field_accessors[type_name + "[]"] = "ObjectField"
jni_gen.struct_setter_templates[type_name] = Template(
object_struct_setter_template.substitute(
c_name="${c_name}",
field_reference_name="${field_reference_name}",
class_FQN=jni_name,
struct_initialization=generate_struct_initialization(type, "${c_name}.",
"${field_reference_name}", ' ' * 4))
)
jni_gen.struct_setter_templates[type_name+ "[]"] = Template(
object_array_struct_setter_template.substitute(
c_name="${c_name}",
field_reference_name="${field_reference_name}",
field_length_check="${field_length_check}",
class_FQN=jni_name,
struct_initialization=generate_struct_initialization(type, "${c_name}[_i].",
"${field_reference_name}ArrayElement", ' ' * 8))
)
jni_gen.dto_field_setter_templates[type_name] = Template(
object_dto_field_setter_template.substitute(
field_reference_name="${field_reference_name}",
field_length="${field_length}",
class_FQN=jni_name,
type_initialization=generate_type_setter(c_type_name, type, "${c_name}.",
"${field_reference_name}", ' ' * 4))
)
jni_gen.dto_field_setter_templates[type_name + "[]"] = Template(
object_array_dto_field_setter_template.substitute(
field_reference_name="${field_reference_name}",
field_length="${field_length}",
class_FQN=jni_name,
type_initialization=generate_type_setter(c_type_name, type, "${c_name}[_i].",
"${field_reference_name}ArrayElement", ' ' * 8))
)
dto_file.flush()
dto_file.close()

View File

@ -65,78 +65,94 @@ def get_reply_suffix(name):
else:
return reply_suffix
# http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html
jni_2_java_type_mapping = {'jbyte': 'byte',
'jbyteArray': 'byte[]',
'jchar': 'char',
'jcharArray': 'char[]',
'jshort': 'short',
'jshortArray': 'short[]',
'jint': 'int',
'jintArray': 'int[]',
'jlong': 'long',
'jlongArray': 'long[]',
'jdouble': 'double',
'jdoubleArray': 'double[]',
'jfloat': 'float',
'jfloatArray': 'float[]',
'void': 'void',
'jstring': 'java.lang.String',
'jobject': 'java.lang.Object',
'jobjectArray': 'java.lang.Object[]'
}
# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#type_signatures
jni_2_signature_mapping = {'jbyte': 'B',
'jbyteArray': '[B',
'jchar': 'C',
'jcharArray': '[C',
'jshort': 'S',
'jshortArray': '[S',
'jint': 'I',
'jintArray': '[I',
'jlong': 'J',
'jlongArray': '[J',
'jdouble': 'D',
'jdoubleArray': '[D',
'jfloat': 'F',
'jfloatArray': '[F'
}
# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Get_type_Field_routines
jni_field_accessors = {
'jbyte': 'ByteField',
'jbyteArray': 'ObjectField',
'jchar': 'CharField',
'jcharArray': 'ObjectField',
'jshort': 'ShortField',
'jshortArray': 'ObjectField',
'jint': 'IntField',
'jintArray': 'ObjectField',
'jlong': 'LongField',
'jlongArray': 'ObjectField',
'jdouble': 'DoubleField',
'jdoubleArray': 'ObjectField',
'jfloat': 'FloatField',
'jfloatArray': 'ObjectField'
}
# Mapping according to:
# http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html
#
# Unsigned types are converted to signed java types that have the same size.
# It is the API user responsibility to interpret them correctly.
vpp_2_jni_type_mapping = {'u8': 'jbyte',
'i8': 'jbyte',
'u16': 'jshort',
'i16': 'jshort',
'u32': 'jint',
'i32': 'jint',
'u64': 'jlong',
'i64': 'jlong',
'f64': 'jdouble'
jni_2_java_type_mapping = {'u8': 'byte',
'u8[]': 'byte[]',
'i8': 'byte',
'i8[]': 'byte[]',
'u16': 'short',
'u16[]': 'short[]',
'i16': 'short',
'i16[]': 'short[]',
'u32': 'int',
'u32[]': 'int[]',
'i32': 'int',
'i32[]': 'int[]',
'u64': 'long',
'u64[]': 'long[]',
'i64': 'long',
'i64[]': 'long[]',
'f64': 'double',
'f64[]': 'double[]'
}
vpp_2_jni_type_mapping = {'u8': 'jbyte',
'u8[]': 'jbyteArray',
'i8': 'jbyte',
'u8[]': 'jbyteArray',
'u16': 'jshort',
'u16[]': 'jshortArray',
'i16': 'jshort',
'i16[]': 'jshortArray',
'u32': 'jint',
'u32[]': 'jintArray',
'i32': 'jint',
'i32[]': 'jintArray',
'u64': 'jlong',
'u64[]': 'longArray',
'i64': 'jlong',
'u64[]': 'longArray',
'f64': 'jdouble',
'f64[]': 'jdoubleArray'
}
# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#type_signatures
jni_2_signature_mapping = {'u8': 'B',
'u8[]': '[B',
'i8': 'B',
'i8[]': '[B',
'u16': 'S',
'u16[]': '[S',
'i16': 'S',
'i16[]': '[S',
'u32': 'I',
'u32[]': '[I',
'i32': 'I',
'i32[]': '[I',
'u64': 'J',
'u64[]': '[J',
'i64': 'J',
'i64[]': '[J',
'f64': 'D',
'f64[]': '[D'
}
# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Get_type_Field_routines
jni_field_accessors = {'u8': 'ByteField',
'u8[]': 'ObjectField',
'i8': 'ByteField',
'i8[]': 'ObjectField',
'u16': 'ShortField',
'u16[]': 'ObjectField',
'i16': 'ShortField',
'i16[]': 'ObjectField',
'u32': 'IntField',
'u32[]': 'ObjectField',
'i32': 'IntField',
'i32[]': 'ObjectField',
'u64': 'LongField',
'u64[]': 'ObjectField',
'i64': 'LongField',
'i64[]': 'ObjectField',
'f64': 'DoubleField',
'f64[]': 'ObjectField'
}
# vpe.api calls that do not follow naming conventions and have to be handled exceptionally when finding reply -> request mapping
# FIXME in vpe.api
unconventional_naming_rep_req = {