summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarek Gradzki <mgradzki@cisco.com>2018-06-27 10:18:18 +0200
committerDamjan Marion <dmarion@me.com>2018-06-27 12:59:31 +0000
commit5b3d95bb3268457f8e9dc264883252b3dcb27141 (patch)
treec3b2b74466e44d018c3681b4abffd85d3eeae6de
parentbf129f458f6f36d57a4378c72ce134ba44858472 (diff)
jvpp: add support for unions (VPP-1322)
Change-Id: I2456a9b03bcae43793f9ac29eb74eff81269df7b Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
-rwxr-xr-xsrc/vpp-api/java/jvpp/gen/jvpp_gen.py2
-rwxr-xr-xsrc/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py37
-rwxr-xr-xsrc/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py80
-rwxr-xr-xsrc/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py8
-rwxr-xr-xsrc/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py102
-rwxr-xr-xsrc/vpp-api/java/jvpp/gen/jvppgen/unions_gen.py98
6 files changed, 277 insertions, 50 deletions
diff --git a/src/vpp-api/java/jvpp/gen/jvpp_gen.py b/src/vpp-api/java/jvpp/gen/jvpp_gen.py
index 5b8c7fda27c..067a92f86be 100755
--- a/src/vpp-api/java/jvpp/gen/jvpp_gen.py
+++ b/src/vpp-api/java/jvpp/gen/jvpp_gen.py
@@ -20,6 +20,7 @@ import sys
from jvppgen.types_gen import generate_types
from jvppgen.enums_gen import generate_enums
+from jvppgen.unions_gen import generate_unions
from jvppgen.dto_gen import generate_dtos
from jvppgen.jvpp_ifc_gen import generate_java_ifc
from jvppgen.jvpp_impl_gen import generate_java_impl
@@ -35,6 +36,7 @@ def generate_jvpp(root_dir, model, logger):
base_dir = "%s/target/%s" % (root_dir, model.plugin_package.replace(".", "/"))
generate_types(_work_dir(base_dir, "types"), model, logger)
generate_enums(_work_dir(base_dir, "types"), model, logger)
+ generate_unions(_work_dir(base_dir, "types"), model, logger)
generate_dtos(_work_dir(base_dir, "dto"), model, logger)
generate_java_ifc(_work_dir(base_dir), model, logger)
generate_java_impl(_work_dir(base_dir), model, logger)
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py
index 742c1736e22..057427dfcfc 100755
--- a/src/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py
+++ b/src/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py
@@ -15,7 +15,7 @@
#
from string import Template
-from jvpp_model import is_array, is_retval, Class, Enum
+from jvpp_model import is_array, is_retval, Class, Enum, Union
def generate_j2c_identifiers(element, class_ref_name, object_ref_name):
@@ -42,18 +42,21 @@ _REQUEST_FIELD_IDENTIFIER_TEMPLATE = Template("""
def generate_j2c_swap(element, struct_ref_name):
initialization = []
for field in element.fields:
- if is_array(field):
- initialization.append(_generate_j2c_array_swap(field, struct_ref_name))
- else:
- initialization.append(_generate_j2c_scalar_swap(field, struct_ref_name))
-
+ initialization.append(generate_j2c_field_swap(field, struct_ref_name))
return "\n".join(initialization)
+def generate_j2c_field_swap(field, struct_ref_name):
+ if is_array(field):
+ return _generate_j2c_array_swap(field, struct_ref_name)
+ else:
+ return _generate_j2c_scalar_swap(field, struct_ref_name)
+
+
def _generate_j2c_array_swap(field, struct_ref_name):
# TODO(VPP-1186): move the logic to JNI generators
base_type = field.type.base_type
- if isinstance(base_type, Class) or isinstance(base_type, Enum):
+ if isinstance(base_type, Class) or isinstance(base_type, Enum) or isinstance(base_type, Union):
return _generate_j2c_object_array_swap(field, struct_ref_name)
elif base_type.is_swap_needed:
return _generate_j2c_primitive_type_array_swap(field, struct_ref_name)
@@ -181,8 +184,8 @@ def generate_c2j_swap(element, object_ref_name, struct_ref_name):
def _generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
# TODO(VPP-1186): move the logic to JNI generators
base_type = field.type.base_type
- if isinstance(base_type, Class):
- return _generate_c2j_class_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+ if isinstance(base_type, Class) or isinstance(base_type, Union):
+ return _generate_c2j_object_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
elif isinstance(base_type, Enum):
return _generate_c2j_enum_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
elif base_type.is_swap_needed:
@@ -191,9 +194,9 @@ def _generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_n
return _generate_c2j_primitive_type_array_no_swap(msg_java_name, field, object_ref_name, struct_ref_name)
-def _generate_c2j_class_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+def _generate_c2j_object_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
field_type = field.type
- return _C2J_CLASS_ARRAY_SWAP_TEMPLATE.substitute(
+ return _C2J_OBJECT_ARRAY_SWAP_TEMPLATE.substitute(
field_reference_name=field.java_name,
class_ref_name=msg_java_name,
jni_signature=field_type.jni_signature,
@@ -205,7 +208,7 @@ def _generate_c2j_class_array_swap(msg_java_name, field, object_ref_name, struct
c_name=field.name
)
-_C2J_CLASS_ARRAY_SWAP_TEMPLATE = Template("""
+_C2J_OBJECT_ARRAY_SWAP_TEMPLATE = Template("""
jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
{
jclass ${field_reference_name}Class = (*env)->FindClass(env, "${jni_name}");
@@ -329,8 +332,8 @@ def _generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_
field_type = field.type
if field_type.is_swap_needed:
# TODO(VPP-1186): move the logic to JNI generators
- if isinstance(field_type, Class):
- return _generate_c2j_class_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+ if isinstance(field_type, Class) or isinstance(field_type, Union):
+ return _generate_c2j_object_swap(msg_java_name, field, object_ref_name, struct_ref_name)
elif isinstance(field_type, Enum):
return _generate_c2j_enum_swap(msg_java_name, field, object_ref_name, struct_ref_name)
else:
@@ -339,9 +342,9 @@ def _generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_
return _generate_c2j_primitive_type_no_swap(msg_java_name, field, object_ref_name, struct_ref_name)
-def _generate_c2j_class_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+def _generate_c2j_object_swap(msg_java_name, field, object_ref_name, struct_ref_name):
field_type = field.type
- return _C2J_CLASS_SWAP_TEMPLATE.substitute(
+ return _C2J_OBJECT_SWAP_TEMPLATE.substitute(
java_name=field.java_name,
class_ref_name=msg_java_name,
jni_signature=field_type.jni_signature,
@@ -352,7 +355,7 @@ def _generate_c2j_class_swap(msg_java_name, field, object_ref_name, struct_ref_n
net_to_host_function=field_type.net_to_host_function,
c_name=field.name)
-_C2J_CLASS_SWAP_TEMPLATE = Template("""
+_C2J_OBJECT_SWAP_TEMPLATE = Template("""
jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
jclass ${java_name}Class = (*env)->FindClass(env, "${jni_name}");
jmethodID ${java_name}Constructor = (*env)->GetMethodID(env, ${java_name}Class, "<init>", "()V");
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py
index 31642e53a73..48566f6234b 100755
--- a/src/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py
+++ b/src/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py
@@ -15,8 +15,8 @@
#
from string import Template
-from jni_common_gen import generate_j2c_swap, generate_j2c_identifiers, generate_c2j_swap
-from jvpp_model import Class, Enum
+from jni_common_gen import generate_j2c_swap, generate_j2c_field_swap, generate_j2c_identifiers, generate_c2j_swap
+from jvpp_model import Class, Enum, Union
def generate_type_handlers(model, logger):
@@ -32,6 +32,8 @@ def generate_type_handlers(model, logger):
_generate_class(model, t, type_handlers)
elif isinstance(t, Enum):
_generate_enum(model, t, type_handlers)
+ elif isinstance(t, Union):
+ _generate_union(model, t, type_handlers)
else:
logger.debug("Skipping custom JNI type handler generation for %s", t)
@@ -148,4 +150,76 @@ def _generate_scalar_net_to_host_swap(field):
if field_type.is_swap_needed:
return "%s((%s) _net);" % (field_type.net_to_host_function, field_type.name)
else:
- return "_net" \ No newline at end of file
+ return "_net"
+
+
+def _generate_union(model, t, type_handlers):
+ type_handlers.append(_generate_union_host_to_net(model, t))
+ type_handlers.append(_generate_union_net_to_host(model, t))
+
+
+def _generate_union_host_to_net(model, t):
+ swap = []
+ for i, field in enumerate(t.fields):
+ field_type = field.type
+ swap.append(_UNION_FIELD_HOST_TO_NET_TEMPLATE.substitute(
+ field_index=i,
+ java_name=field.java_name,
+ jni_signature=field_type.jni_signature,
+ jni_type=field_type.jni_type,
+ jni_accessor=field_type.jni_accessor,
+ swap=generate_j2c_field_swap(field, struct_ref_name="_net")
+ ))
+
+ return _UNION_HOST_TO_NET_TEMPLATE.substitute(
+ c_name=t.name,
+ json_filename=model.json_api_files,
+ json_definition=t.doc,
+ class_FQN=t.jni_name,
+ swap="".join(swap)
+ )
+
+_UNION_FIELD_HOST_TO_NET_TEMPLATE = Template("""
+ if (_activeMember == ${field_index}) {
+ jfieldID fieldId = (*env)->GetFieldID(env, _class, "${java_name}", "${jni_signature}");
+ ${jni_type} ${java_name} = (*env)->Get${jni_accessor}Field(env, _host, fieldId);
+ ${swap}
+ }""")
+
+_UNION_HOST_TO_NET_TEMPLATE = Template("""
+/**
+ * Host to network byte order conversion for ${c_name} union.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static inline void _host_to_net_${c_name}(JNIEnv * env, jobject _host, vl_api_${c_name}_t * _net)
+{
+ jclass _class = (*env)->FindClass(env, "${class_FQN}");
+
+ jfieldID _activeMemberFieldId = (*env)->GetFieldID(env, _class, "_activeMember", "I");
+ jint _activeMember = (*env)->GetIntField(env, _host, _activeMemberFieldId);
+$swap
+}""")
+
+
+def _generate_union_net_to_host(model, t):
+ return _UNION_NET_TO_HOST_TEMPLATE.substitute(
+ c_name=t.name,
+ json_filename=model.json_api_files,
+ json_definition=t.doc,
+ type_reference_name=t.java_name_lower,
+ class_FQN=t.jni_name,
+ swap=generate_c2j_swap(t, object_ref_name="_host", struct_ref_name="_net")
+ )
+
+_UNION_NET_TO_HOST_TEMPLATE = Template("""
+/**
+ * Network to host byte order conversion for ${c_name} union.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static inline void _net_to_host_${c_name}(JNIEnv * env, vl_api_${c_name}_t * _net, jobject _host)
+{
+ jclass ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
+$swap
+}""")
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py
index 73615ca7178..83226ea78ac 100755
--- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py
+++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py
@@ -17,10 +17,12 @@ from string import Template
from jvpp_model import is_array
-def generate_fields(fields):
- return "\n".join(_FIELD_TEMPLATE.substitute(type=f.type.java_name_fqn, name=f.java_name) for f in fields)
+def generate_fields(fields, access_modifier="public"):
+ return "\n".join(_FIELD_TEMPLATE
+ .substitute(access_modifier=access_modifier, type=f.type.java_name_fqn, name=f.java_name)
+ for f in fields)
-_FIELD_TEMPLATE = Template(""" public $type $name;""")
+_FIELD_TEMPLATE = Template(""" ${access_modifier} ${type} ${name};""")
def generate_hash_code(fields):
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py
index 024e8ffd7d8..299796b908b 100755
--- a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py
+++ b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py
@@ -166,10 +166,39 @@ class Class(Type):
return "_host_to_net_%s(env, %s, &(%s))" % (self.name, host_ref_name, net_ref_name)
+class Union(Type):
+ def __init__(self, name, crc, fields, definition, plugin_name):
+ _java_name = _underscore_to_camelcase_upper(name)
+
+ super(Union, self).__init__(
+ name=name,
+ java_name=_java_name,
+ java_name_fqn="io.fd.vpp.jvpp.%s.types.%s" % (plugin_name, _java_name),
+ jni_signature="Lio/fd/vpp/jvpp/%s/types/%s;" % (plugin_name, _java_name),
+ jni_type="jobject",
+ jni_accessor="Object",
+ host_to_net_function="_host_to_net_%s" % name,
+ net_to_host_function="_net_to_host_%s" % name
+ )
+
+ self.crc = crc
+ self.fields = fields
+ self.doc = _message_to_javadoc(definition)
+ self.java_name_lower = _underscore_to_camelcase_lower(name)
+ self.vpp_name = "%s%s%s" % (_VPP_TYPE_PREFIX, name, _VPP_TYPE_SUFFIX)
+ # Fully qualified class name used by FindClass function, see:
+ # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#FindClass
+ self.jni_name = "io/fd/vpp/jvpp/%s/types/%s" % (plugin_name, _java_name)
+
+ def get_host_to_net_function(self, host_ref_name, net_ref_name):
+ return "_host_to_net_%s(env, %s, &(%s))" % (self.name, host_ref_name, net_ref_name)
+
+
class Field(object):
def __init__(self, name, field_type, array_len=None, array_len_field=None):
self.name = name
self.java_name = _underscore_to_camelcase_lower(name)
+ self.java_name_upper = _underscore_to_camelcase_upper(name)
self.type = field_type
self.array_len = array_len
self.array_len_field = array_len_field
@@ -283,25 +312,60 @@ class JVppModel(object):
self.plugin_name = plugin_name
self.plugin_java_name = _underscore_to_camelcase_upper(plugin_name)
self._load_json_files(json_api_files)
- self._parse_simple_types()
- self._parse_enums()
- self._parse_types()
self._parse_services()
self._parse_messages()
self._validate_messages()
def _load_json_files(self, json_api_files):
- self._enums = []
- self._types = []
+ types = {}
self._messages = []
self._services = {}
for file_name in json_api_files:
with open(file_name) as f:
j = json.load(f)
- self._enums.extend(j['enums'])
- self._types.extend(j['types'])
+ types.update({d[0]: {'type': 'enum', 'data': d} for d in j['enums']})
+ types.update({d[0]: {'type': 'type', 'data': d} for d in j['types']})
+ types.update({d[0]: {'type': 'union', 'data': d} for d in j['unions']})
self._messages.extend(j['messages'])
self._services.update(j['services'])
+ self._parse_types(types)
+
+ def _parse_types(self, types):
+ self._parse_simple_types()
+ i = 0
+ while True:
+ unresolved = {}
+ for name, value in types.items():
+ if name in self._types_by_name:
+ continue
+
+ type = value['type']
+ data = value['data'][1:]
+ try:
+ if type == 'enum':
+ type = self._parse_enum(name, data)
+ elif type == 'union':
+ type = self._parse_union(name, data)
+ elif type == 'type':
+ type = self._parse_type(name, data)
+ else:
+ self.logger.warning("Unsupported type %s. Ignoring...", type)
+ continue
+
+ self._types_by_name[name] = type
+ self._types_by_name[name + _ARRAY_SUFFIX] = Array(type)
+ except ParseException as e:
+ self.logger.debug("Failed to parse %s type in iteration %s: %s.", name, i, e)
+ unresolved[name] = value
+ if len(unresolved) == 0:
+ break
+ if i > 3:
+ raise ParseException('Unresolved type definitions {}'
+ .format(unresolved))
+ types = unresolved
+ i += 1
+
+ self.types = self._types_by_name.values()
def _parse_simple_types(self):
# Mapping according to:
@@ -339,14 +403,6 @@ class JVppModel(object):
for n, t in self._types_by_name.items():
self._types_by_name[n + _ARRAY_SUFFIX] = Array(t)
- def _parse_enums(self):
- for json_type in self._enums:
- name = json_type[0]
- definition = json_type[1:]
- _type = self._parse_enum(name, definition)
- self._types_by_name[name] = _type
- self._types_by_name[name + _ARRAY_SUFFIX] = Array(_type)
-
def _parse_enum(self, name, definition):
self.logger.debug("Parsing enum %s: %s", name, definition)
constants = []
@@ -360,18 +416,10 @@ class JVppModel(object):
raise ParseException("'enumtype' was not defined for %s" % definition)
return Enum(name, Field('value', self._types_by_name[type_name]), constants, definition, self.plugin_name)
- def _parse_types(self):
- for json_type in self._types:
- try:
- name = json_type[0]
- definition = json_type[1:]
- _type = self._parse_type(name, definition)
- self._types_by_name[name] = _type
- self._types_by_name[name + _ARRAY_SUFFIX] = Array(_type)
- except ParseException as e:
- self.logger.warning("Failed to parse %s type: %s. Skipping type definition.", name, e)
-
- self.types = self._types_by_name.values()
+ def _parse_union(self, name, definition):
+ self.logger.debug("Parsing union %s: %s", name, definition)
+ crc, fields = self._parse_fields(definition)
+ return Union(name, crc, fields, definition, self.plugin_name)
def _parse_type(self, name, definition):
self.logger.debug("Parsing type %s: %s", name, definition)
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/unions_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/unions_gen.py
new file mode 100755
index 00000000000..f67704f2426
--- /dev/null
+++ b/src/vpp-api/java/jvpp/gen/jvppgen/unions_gen.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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
+
+from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields
+from jvpp_model import Union
+
+
+def generate_unions(work_dir, model, logger):
+ logger.debug("Generating unions for %s " % model.json_api_files)
+
+ for t in model.types:
+ if not isinstance(t, Union):
+ continue
+ logger.debug("Generating DTO for union %s", t)
+ java_union_name = t.java_name
+ fields = t.fields
+ type_class = _UNION_TEMPLATE.substitute(
+ plugin_package=model.plugin_package,
+ c_type_name=t.name,
+ json_filename=model.json_api_files,
+ json_definition=t.doc,
+ java_union_name=java_union_name,
+ fields=generate_fields(fields, access_modifier="private"),
+ constructors=_generate_constructors(java_union_name, fields),
+ getters=_generate_getters(fields),
+ hash_code=generate_hash_code(fields),
+ equals=generate_equals(java_union_name, fields),
+ to_string=generate_to_string(java_union_name, fields)
+ )
+ with open("%s/%s.java" % (work_dir, java_union_name), "w") as f:
+ f.write(type_class)
+
+_UNION_TEMPLATE = Template("""
+package ${plugin_package}.types;
+
+/**
+ * <p>This class represents ${c_type_name} union definition.
+ * <br>It was generated by unions_gen.py based on ${json_filename}:
+ * <pre>
+${json_definition}
+ * </pre>
+ */
+public class ${java_union_name} {
+ private final int _activeMember;
+${fields}
+ private ${java_union_name}() {
+ // Constructor for JNI usage. All members can be read.
+ _activeMember = -1;
+ }
+${constructors}
+${getters}
+${hash_code}
+${equals}
+${to_string}
+}
+""")
+
+
+def _generate_constructors(union_name, fields):
+ return "".join(
+ _CONSTRUCTOR_TEMPLATE
+ .substitute(union_name=union_name,
+ field_type=f.type.java_name_fqn,
+ field_name=f.java_name,
+ field_index=i) for i, f in enumerate(fields))
+
+_CONSTRUCTOR_TEMPLATE = Template("""
+ public ${union_name}(${field_type} ${field_name}) {
+ this.${field_name} = java.util.Objects.requireNonNull(${field_name}, "${field_name} should not be null");
+ _activeMember = $field_index;
+ }""")
+
+
+def _generate_getters(fields):
+ return "".join(_GETTER_TEMPLATE.substitute(
+ type=f.type.java_name_fqn,
+ getter_name=f.java_name_upper,
+ field_name=f.java_name
+ ) for f in fields)
+
+_GETTER_TEMPLATE = Template("""
+ public ${type} get${getter_name}() {
+ return ${field_name};
+ }""")