Update japi to support type aliases 69/16369/2
authorMichal Cmarada <mcmarada@cisco.com>
Thu, 6 Dec 2018 09:17:39 +0000 (10:17 +0100)
committerOle Trøan <otroan@employees.org>
Thu, 6 Dec 2018 14:04:44 +0000 (14:04 +0000)
Change-Id: I6d6068d641d4c91e5c5b52eefb898affc5c0d2c0
Signed-off-by: Michal Cmarada <mcmarada@cisco.com>
extras/japi/java/jvpp/gen/jvppgen/jni_common_gen.py
extras/japi/java/jvpp/gen/jvppgen/jni_impl_gen.py
extras/japi/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py
extras/japi/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py
extras/japi/java/jvpp/gen/jvppgen/jvpp_model.py

index 397b92b..708cc1c 100755 (executable)
@@ -39,21 +39,21 @@ _REQUEST_FIELD_IDENTIFIER_TEMPLATE = Template("""
 
 
 # TODO(VPP-1187): do not inline JNI object creation inside message handlers to reduce number of special cases
-def generate_j2c_swap(element, struct_ref_name):
+def generate_j2c_swap(element, struct_ref_name, is_alias):
     initialization = []
     for field in element.fields:
-        initialization.append(generate_j2c_field_swap(field, struct_ref_name))
+        initialization.append(generate_j2c_field_swap(field, struct_ref_name, is_alias))
     return "\n".join(initialization)
 
 
-def generate_j2c_field_swap(field, struct_ref_name):
+def generate_j2c_field_swap(field, struct_ref_name, is_alias):
     if is_array(field):
-        return _generate_j2c_array_swap(field, struct_ref_name)
+        return _generate_j2c_array_swap(field, struct_ref_name, is_alias)
     else:
-        return _generate_j2c_scalar_swap(field, struct_ref_name)
+        return _generate_j2c_scalar_swap(field, struct_ref_name, is_alias)
 
 
-def _generate_j2c_array_swap(field, struct_ref_name):
+def _generate_j2c_array_swap(field, struct_ref_name, is_alias):
     # TODO(VPP-1186): move the logic to JNI generators
     base_type = field.type.base_type
     if isinstance(base_type, (Class, Enum, Union)):
@@ -61,7 +61,7 @@ def _generate_j2c_array_swap(field, struct_ref_name):
     elif base_type.is_swap_needed:
         return _generate_j2c_primitive_type_array_swap(field, struct_ref_name)
     else:
-        return _generate_j2c_primitive_type_array_no_swap(field, struct_ref_name)
+        return _generate_j2c_primitive_type_array_no_swap(field, struct_ref_name, is_alias)
 
 
 def _generate_j2c_object_array_swap(field, struct_ref_name):
@@ -119,9 +119,14 @@ _J2C_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE = Template("""
     """)
 
 
-def _generate_j2c_primitive_type_array_no_swap(field, struct_ref_name):
+def _generate_j2c_primitive_type_array_no_swap(field, struct_ref_name, is_alias):
     field_type = field.type
-    return _J2C_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE.substitute(
+    if not is_alias:
+        template = _J2C_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE
+    else:
+        template = _J2C_ALIAS_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE
+
+    return template.substitute(
         field_reference_name=field.java_name,
         field_length_check=_generate_field_length_check(field),
         base_type=field_type.base_type.jni_accessor,
@@ -139,6 +144,15 @@ _J2C_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE = Template("""
 """)
 
 
+_J2C_ALIAS_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE = Template("""
+    if (${field_reference_name}) {
+        jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
+        ${field_length_check}
+        (*env)->Get${base_type}ArrayRegion(env, ${field_reference_name}, 0, cnt, (${jni_base_type} *)${struct_reference_name});
+    }
+""")
+
+
 def _generate_field_length_check(field):
     # Enforce max length if array has fixed length or uses variable length syntax
     field_length = str(field.array_len)
@@ -157,17 +171,21 @@ _FIELD_LENGTH_CHECK = Template("""
         if (cnt > max_size) cnt = max_size;""")
 
 
-def _generate_j2c_scalar_swap(field, struct_ref_name):
+def _generate_j2c_scalar_swap(field, struct_ref_name, is_alias):
     field_type = field.type
     if field_type.is_swap_needed:
         host = field.java_name
-        net = "%s->%s" % (struct_ref_name, field.name)
-        return "    %s;" % field_type.get_host_to_net_function(host, net)
+        if not is_alias:
+            net = "%s->%s" % (struct_ref_name, field.name)
+            return "    %s;" % field_type.get_host_to_net_function(host, net)
+        else:
+            net = "%s" % (struct_ref_name)
+            return "    *%s;" % field_type.get_host_to_net_function(host, net)
     else:
         return "    %s->%s = %s;" % (struct_ref_name, field.name, field.java_name)
 
 
-def generate_c2j_swap(element, object_ref_name, struct_ref_name):
+def generate_c2j_swap(element, object_ref_name, struct_ref_name, is_alias):
     msg_java_name = element.java_name_lower
     initialization = []
     for field in element.fields:
@@ -175,13 +193,13 @@ def generate_c2j_swap(element, object_ref_name, struct_ref_name):
             # For retval don't generate setters and generate retval check
             continue
         elif is_array(field):
-            initialization.append(_generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_name))
+            initialization.append(_generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_name, is_alias))
         else:
-            initialization.append(_generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_name))
+            initialization.append(_generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_name, is_alias))
     return "".join(initialization)
 
 
-def _generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+def _generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_name, is_alias):
     # TODO(VPP-1186): move the logic to JNI generators
     base_type = field.type.base_type
     if isinstance(base_type, (Class, Union)):
@@ -191,7 +209,7 @@ def _generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_n
     elif base_type.is_swap_needed:
         return _generate_c2j_primitive_type_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
     else:
-        return _generate_c2j_primitive_type_array_no_swap(msg_java_name, field, object_ref_name, struct_ref_name)
+        return _generate_c2j_primitive_type_array_no_swap(msg_java_name, field, object_ref_name, struct_ref_name, is_alias)
 
 
 def _generate_c2j_object_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
@@ -294,9 +312,13 @@ _C2J_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE = Template("""
 """)
 
 
-def _generate_c2j_primitive_type_array_no_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+def _generate_c2j_primitive_type_array_no_swap(msg_java_name, field, object_ref_name, struct_ref_name, is_alias):
     field_type = field.type
-    return _C2J_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE.substitute(
+    if not is_alias:
+        template = _C2J_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE
+    else:
+        template = _C2J_ALIAS_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE
+    return template.substitute(
         field_reference_name=field.java_name,
         class_ref_name=msg_java_name,
         jni_signature=field_type.jni_signature,
@@ -318,6 +340,15 @@ _C2J_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE = Template("""
 """)
 
 
+_C2J_ALIAS_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE = Template("""
+    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
+    ${jni_type} ${field_reference_name} = (*env)->New${base_type}Array(env, ${field_length});
+    (*env)->Set${base_type}ArrayRegion(env, ${field_reference_name}, 0, ${field_length}, (const ${jni_base_type}*)${struct_ref_name});
+    (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
+    (*env)->DeleteLocalRef(env, ${field_reference_name});
+""")
+
+
 def _generate_array_length(field, struct_ref_name):
     if field.array_len_field:
         len_field = field.array_len_field
@@ -328,7 +359,7 @@ def _generate_array_length(field, struct_ref_name):
     return field.array_len
 
 
-def _generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+def _generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_name, is_alias):
     field_type = field.type
     if field_type.is_swap_needed:
         # TODO(VPP-1186): move the logic to JNI generators
@@ -337,7 +368,7 @@ def _generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_
         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)
+            return _generate_c2j_primitive_type_swap(msg_java_name, field, object_ref_name, struct_ref_name, is_alias)
     else:
         return _generate_c2j_primitive_type_no_swap(msg_java_name, field, object_ref_name, struct_ref_name)
 
@@ -390,9 +421,13 @@ _C2J_ENUM_SWAP_TEMPLATE = Template("""
 """)
 
 
-def _generate_c2j_primitive_type_swap(msg_java_name, field, object_ref_name, struct_ref_name):
+def _generate_c2j_primitive_type_swap(msg_java_name, field, object_ref_name, struct_ref_name, is_alias):
     field_type = field.type
-    return _C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE.substitute(
+    if not is_alias:
+        template = _C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE
+    else:
+        template = _C2J_ALIAS_PRIMITIVE_TYPE_SWAP_TEMPLATE
+    return template.substitute(
         java_name=field.java_name,
         class_ref_name=msg_java_name,
         jni_signature=field_type.jni_signature,
@@ -409,6 +444,12 @@ _C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE = Template("""
 """)
 
 
+_C2J_ALIAS_PRIMITIVE_TYPE_SWAP_TEMPLATE = Template("""
+    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
+    (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${net_to_host_function}(*${struct_ref_name}));
+""")
+
+
 def _generate_c2j_primitive_type_no_swap(msg_java_name, field, object_ref_name, struct_ref_name):
     field_type = field.type
     return _C2J_PRIMITIVE_TYPE_NO_SWAP_TEMPLATE.substitute(
index bf75236..717a42c 100755 (executable)
@@ -43,7 +43,7 @@ def generate_jni_impl(model):
                 java_dto_name=msg.java_name_upper
             )
             jni_identifiers = generate_j2c_identifiers(msg, class_ref_name="requestClass", object_ref_name="request")
-            msg_initialization = generate_j2c_swap(msg, struct_ref_name="mp")
+            msg_initialization = generate_j2c_swap(msg, struct_ref_name="mp", is_alias=False)
 
         jni_impl.append(_JNI_IMPL_TEMPLATE.substitute(
             c_name=msg.name,
index 8f6410f..5f274a3 100755 (executable)
@@ -41,7 +41,7 @@ def generate_jni_handlers(model):
             err_handler=_generate_error_handler(msg),
             class_ref_name=msg.java_name_lower,
             dto_name=msg.java_name_upper,
-            dto_setters=generate_c2j_swap(msg, object_ref_name="dto", struct_ref_name="mp")
+            dto_setters=generate_c2j_swap(msg, object_ref_name="dto", struct_ref_name="mp", is_alias=False)
         ))
     return "".join(jni_impl)
 
index 2447974..59aaf95 100755 (executable)
@@ -42,6 +42,11 @@ def generate_type_handlers(model, logger):
 
 def _generate_class(model, t, type_handlers):
     ref_name = t.java_name_lower
+    if t.name in model._aliases:
+        is_alias = True
+    else:
+        is_alias = False
+
     type_handlers.append(_TYPE_HOST_TO_NET_TEMPLATE.substitute(
         c_name=t.name,
         json_filename=model.json_api_files,
@@ -49,7 +54,7 @@ def _generate_class(model, t, type_handlers):
         type_reference_name=ref_name,
         class_FQN=t.jni_name,
         jni_identifiers=generate_j2c_identifiers(t, class_ref_name="%sClass" % ref_name, object_ref_name="_host"),
-        type_swap=generate_j2c_swap(t, struct_ref_name="_net")
+        type_swap=generate_j2c_swap(t, struct_ref_name="_net", is_alias=is_alias)
     ))
     type_handlers.append(_TYPE_NET_TO_HOST_TEMPLATE.substitute(
         c_name=t.name,
@@ -57,7 +62,7 @@ def _generate_class(model, t, type_handlers):
         json_definition=t.doc,
         type_reference_name=ref_name,
         class_FQN=t.jni_name,
-        type_swap=generate_c2j_swap(t, object_ref_name="_host", struct_ref_name="_net")
+        type_swap=generate_c2j_swap(t, object_ref_name="_host", struct_ref_name="_net", is_alias=is_alias)
     ))
 
 _TYPE_HOST_TO_NET_TEMPLATE = Template("""
@@ -159,13 +164,17 @@ def _generate_union_host_to_net(model, t):
     swap = []
     for i, field in enumerate(t.fields):
         field_type = field.type
+        if t.name in model._aliases:
+            is_alias = True
+        else:
+            is_alias = False
         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")
+            swap=generate_j2c_field_swap(field, struct_ref_name="_net", is_alias=is_alias)
         ))
 
     return _UNION_HOST_TO_NET_TEMPLATE.substitute(
@@ -200,13 +209,17 @@ $swap
 
 
 def _generate_union_net_to_host(model, t):
+    if t.name in model._aliases:
+        is_alias = True
+    else:
+        is_alias = False
     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")
+        swap=generate_c2j_swap(t, object_ref_name="_host", struct_ref_name="_net", is_alias=is_alias)
     )
 
 _UNION_NET_TO_HOST_TEMPLATE = Template("""
index 3c2db15..1609968 100755 (executable)
@@ -17,6 +17,8 @@ import json
 import pprint
 from collections import OrderedDict
 
+import binascii
+
 BASE_PACKAGE = "io.fd.vpp.jvpp"
 
 
@@ -303,6 +305,11 @@ def is_control_ping_reply(msg):
     return msg.name == u'control_ping_reply'
 
 
+def crc(block):
+    s = str(block).encode()
+    return binascii.crc32(s) & 0xffffffff
+
+
 class JVppModel(object):
     def __init__(self, logger, json_api_files, plugin_name):
         self.logger = logger
@@ -333,8 +340,43 @@ class JVppModel(object):
 
         self._parse_types(types)
 
+    def _parse_aliases(self, types):
+        for alias_name in self._aliases:
+            alias = self._aliases[alias_name]
+            alias_type = {"type": "type"}
+            java_name_lower = _underscore_to_camelcase_lower(alias_name)
+            vpp_type = alias["type"]
+            crc_value = '0x%08x' % crc(alias_name)
+            if "length" in alias:
+                length = alias["length"]
+                alias_type["data"] = [
+                    alias_name,
+                    [
+                        vpp_type,
+                        java_name_lower,
+                        length
+                    ],
+                    {
+                        "crc": crc_value
+                    }
+                ]
+            else:
+                alias_type["data"] = [
+                    alias_name,
+                    [
+                        vpp_type,
+                        java_name_lower
+                    ],
+                    {
+                        "crc": crc_value
+                    }
+                ]
+
+            types[alias_name] = alias_type
+
     def _parse_types(self, types):
         self._parse_simple_types()
+        self._parse_aliases(types)
         i = 0
         while True:
             unresolved = {}
@@ -484,9 +526,6 @@ class JVppModel(object):
 
     def _parse_field(self, field, fields):
         type_name = _extract_type_name(field[0])
-        if type_name in self._aliases and type_name not in self._types_by_name:
-            aliased_type = self._types_by_name.get(self._aliases.get(type_name).get("type"))
-            self._types_by_name[type_name] = aliased_type
 
         if type_name in self._types_by_name:
             if len(field) > 2:
@@ -524,6 +563,7 @@ class JVppModel(object):
 
         self.messages = self._messages_by_name.values()
 
+
 _ARRAY_SUFFIX = '[]'