jvpp: add support for enums (VPP-1153)

Change-Id: I2a1b946a71419e1fb3c5d70567c54a6a7d841f10
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
This commit is contained in:
Marek Gradzki
2018-06-20 14:49:39 +02:00
committed by Ole Trøan
parent 9679c81b64
commit 3e2d57d877
5 changed files with 244 additions and 4 deletions

View File

@ -19,6 +19,7 @@ import os
import sys
from jvppgen.types_gen import generate_types
from jvppgen.enums_gen import generate_enums
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
@ -33,6 +34,7 @@ from jvppgen.jvpp_model import JVppModel
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_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)

View File

@ -0,0 +1,74 @@
#!/usr/bin/env python2
#
# Copyright (c) 2016,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_model import Enum
def generate_enums(work_dir, model, logger):
logger.debug("Generating enums for %s " % model.json_api_files)
for t in model.types:
if not isinstance(t, Enum):
continue
logger.debug("Generating DTO for enum %s", t)
type_class_name = t.java_name
type_class = _ENUM_TEMPLATE.substitute(
plugin_package=model.plugin_package,
c_type_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
java_enum_name=type_class_name,
constants=_generate_constants(t.constants),
value_type=t.value.type.java_name
)
with open("%s/%s.java" % (work_dir, type_class_name), "w") as f:
f.write(type_class)
_ENUM_TEMPLATE = Template("""
package $plugin_package.types;
/**
* <p>This class represents $c_type_name enum definition.
* <br>It was generated by enums_gen.py based on $json_filename:
* <pre>
$json_definition
* </pre>
*/
public enum $java_enum_name {
$constants;
public final $value_type value;
$java_enum_name(final $value_type value) {
this.value = value;
}
public static $java_enum_name forValue(final $value_type value) {
for ($java_enum_name enumeration : $java_enum_name.values()) {
if (value == enumeration.value) {
return enumeration;
}
}
return null;
}
}
""")
def _generate_constants(constants):
return ",\n".join(_CONSTANT_TEMPLATE.substitute(name=c['name'], value=c['value']) for c in constants)
_CONSTANT_TEMPLATE = Template(""" $name($value)""")

View File

@ -15,7 +15,7 @@
#
from string import Template
from jvpp_model import is_array, is_retval, Class
from jvpp_model import is_array, is_retval, Class, Enum
def generate_j2c_identifiers(element, class_ref_name, object_ref_name):
@ -53,7 +53,7 @@ def generate_j2c_swap(element, 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):
if isinstance(base_type, Class) or isinstance(base_type, Enum):
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)
@ -183,6 +183,8 @@ def _generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_n
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)
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:
return _generate_c2j_primitive_type_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
else:
@ -222,6 +224,40 @@ _C2J_CLASS_ARRAY_SWAP_TEMPLATE = Template("""
""")
def _generate_c2j_enum_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
field_type = field.type
base_type = field_type.base_type
return _C2J_ENUM_ARRAY_SWAP_TEMPLATE.substitute(
field_reference_name=field.java_name,
class_ref_name=msg_java_name,
jni_signature=field_type.jni_signature,
jni_name=base_type.jni_name,
field_length=_generate_array_length(field, struct_ref_name),
net_to_host_function=field_type.net_to_host_function,
jni_signature_enum_value=base_type.value.type.jni_signature,
struct_ref_name=struct_ref_name,
object_ref_name=object_ref_name,
c_name=field.name
)
_C2J_ENUM_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}");
jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0);
jmethodID ${field_reference_name}Constructor = (*env)->GetStaticMethodID(env, ${field_reference_name}Class, "forValue", "(${jni_signature_enum_value})${jni_signature}");
unsigned int _i;
for (_i = 0; _i < ${field_length}; _i++) {
jobject ${field_reference_name}ArrayElement = (*env)->CallStaticObjectMethod(env, ${field_reference_name}Class, ${field_reference_name}Constructor, ${net_to_host_function}(${struct_ref_name}->${c_name}[_i]));
(*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement);
(*env)->DeleteLocalRef(env, ${field_reference_name}ArrayElement);
}
(*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
(*env)->DeleteLocalRef(env, ${field_reference_name});
}
""")
def _generate_c2j_primitive_type_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
field_type = field.type
return _C2J_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE.substitute(
@ -295,6 +331,8 @@ def _generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_
# 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)
elif isinstance(field_type, Enum):
return _generate_c2j_enum_swap(msg_java_name, field, object_ref_name, struct_ref_name)
else:
return _generate_c2j_primitive_type_swap(msg_java_name, field, object_ref_name, struct_ref_name)
else:
@ -325,6 +363,30 @@ _C2J_CLASS_SWAP_TEMPLATE = Template("""
""")
def _generate_c2j_enum_swap(msg_java_name, field, object_ref_name, struct_ref_name):
field_type = field.type
return _C2J_ENUM_SWAP_TEMPLATE.substitute(
java_name=field.java_name,
class_ref_name=msg_java_name,
jni_signature=field_type.jni_signature,
jni_signature_enum_value=field_type.value.type.jni_signature,
jni_name=field_type.jni_name,
jni_accessor=field_type.jni_accessor,
object_ref_name=object_ref_name,
struct_ref_name=struct_ref_name,
net_to_host_function=field_type.net_to_host_function,
c_name=field.name)
_C2J_ENUM_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)->GetStaticMethodID(env, ${java_name}Class, "forValue", "(${jni_signature_enum_value})${jni_signature}");
jobject ${java_name} = (*env)->CallStaticObjectMethod(env, ${java_name}Class, ${java_name}Constructor, ${net_to_host_function}(${struct_ref_name}->${c_name}));
(*env)->SetObjectField(env, ${object_ref_name}, ${java_name}FieldId, ${java_name});
(*env)->DeleteLocalRef(env, ${java_name});
""")
def _generate_c2j_primitive_type_swap(msg_java_name, field, object_ref_name, struct_ref_name):
field_type = field.type
return _C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE.substitute(

View File

@ -16,7 +16,7 @@
from string import Template
from jni_common_gen import generate_j2c_swap, generate_j2c_identifiers, generate_c2j_swap
from jvpp_model import Class
from jvpp_model import Class, Enum
def generate_type_handlers(model, logger):
@ -30,6 +30,8 @@ def generate_type_handlers(model, logger):
#TODO(VPP-1186): move the logic to JNI generators
if isinstance(t, Class):
_generate_class(model, t, type_handlers)
elif isinstance(t, Enum):
_generate_enum(model, t, type_handlers)
else:
logger.debug("Skipping custom JNI type handler generation for %s", t)
@ -85,6 +87,54 @@ $type_swap
}""")
def _generate_enum(model, t, type_handlers):
value_type = t.value.type
type_handlers.append(_ENUM_NET_TO_HOST_TEMPLATE.substitute(
c_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
class_FQN=t.jni_name,
jni_signature=value_type.jni_signature,
jni_type=value_type.jni_type,
jni_accessor=value_type.jni_accessor,
swap=_generate_scalar_host_to_net_swap(t.value)
))
type_handlers.append(_ENUM_HOST_TO_NET_TEMPLATE.substitute(
c_name=t.name,
json_filename=model.json_api_files,
json_definition=t.doc,
class_FQN=t.jni_name,
jni_type=value_type.jni_type,
type_swap=_generate_scalar_net_to_host_swap(t.value)
))
_ENUM_NET_TO_HOST_TEMPLATE = Template("""
/**
* Host to network byte order conversion for ${c_name} enum.
* 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 enumClass = (*env)->FindClass(env, "${class_FQN}");
jfieldID valueFieldId = (*env)->GetStaticFieldID(env, enumClass, "value", "${jni_signature}");
${jni_type} value = (*env)->GetStatic${jni_accessor}Field(env, enumClass, valueFieldId);
${swap};
}""")
_ENUM_HOST_TO_NET_TEMPLATE = Template("""
/**
* Network to host byte order conversion for ${c_name} type.
* Generated based on $json_filename:
$json_definition
*/
static inline ${jni_type} _net_to_host_${c_name}(vl_api_${c_name}_t _net)
{
return (${jni_type}) $type_swap
}""")
def _generate_scalar_host_to_net_swap(field):
field_type = field.type
if field_type.is_swap_needed:

View File

@ -110,6 +110,34 @@ class Array(Type):
return "Array{name:%s, java_name:%s}" % (self.name, self.java_name)
class Enum(Type):
def __init__(self, name, value, constants, definition, plugin_name):
_java_name = _underscore_to_camelcase_upper(name)
super(Enum, 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.value = value
self.constants = constants
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 Class(Type):
def __init__(self, name, crc, fields, definition, plugin_name):
_java_name = _underscore_to_camelcase_upper(name)
@ -255,6 +283,8 @@ 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()
@ -273,7 +303,7 @@ class JVppModel(object):
self._messages.extend(j['messages'])
self._services.update(j['services'])
def _parse_types(self):
def _parse_simple_types(self):
# Mapping according to:
# http://docs.oracle.com/javase/7/do+'[]'cs/technotes/guides/jni/spec/types.html
# and
@ -309,6 +339,28 @@ 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 = []
type_name = None
for item in definition:
if type(item) is dict and 'enumtype' in item:
type_name = item['enumtype']
continue
constants.append({'name': item[0], 'value': item[1]})
if not type_name:
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]