jvpp: add support for enums (VPP-1153)
[vpp.git] / src / vpp-api / java / jvpp / gen / jvppgen / jni_common_gen.py
1 #!/usr/bin/env python2
2 #
3 # Copyright (c) 2018 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:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15 #
16 from string import Template
17
18 from jvpp_model import is_array, is_retval, Class, Enum
19
20
21 def generate_j2c_identifiers(element, class_ref_name, object_ref_name):
22     identifiers = []
23     for field in element.fields:
24         field_type = field.type
25         identifiers.append(_REQUEST_FIELD_IDENTIFIER_TEMPLATE.substitute(
26             java_name=field.java_name,
27             class_ref_name=class_ref_name,
28             jni_signature=field_type.jni_signature,
29             jni_type=field_type.jni_type,
30             jni_accessor=field_type.jni_accessor,
31             object_ref_name=object_ref_name
32         ))
33     return "".join(identifiers)
34
35 _REQUEST_FIELD_IDENTIFIER_TEMPLATE = Template("""
36     jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}, "${java_name}", "${jni_signature}");
37     ${jni_type} ${java_name} = (*env)->Get${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId);
38 """)
39
40
41 # TODO(VPP-1187): do not inline JNI object creation inside message handlers to reduce number of special cases
42 def generate_j2c_swap(element, struct_ref_name):
43     initialization = []
44     for field in element.fields:
45         if is_array(field):
46             initialization.append(_generate_j2c_array_swap(field, struct_ref_name))
47         else:
48             initialization.append(_generate_j2c_scalar_swap(field, struct_ref_name))
49
50     return "\n".join(initialization)
51
52
53 def _generate_j2c_array_swap(field, struct_ref_name):
54     # TODO(VPP-1186): move the logic to JNI generators
55     base_type = field.type.base_type
56     if isinstance(base_type, Class) or isinstance(base_type, Enum):
57         return _generate_j2c_object_array_swap(field, struct_ref_name)
58     elif base_type.is_swap_needed:
59         return _generate_j2c_primitive_type_array_swap(field, struct_ref_name)
60     else:
61         return _generate_j2c_primitive_type_array_no_swap(field, struct_ref_name)
62
63
64 def _generate_j2c_object_array_swap(field, struct_ref_name):
65     field_type = field.type
66     field_reference_name = field.java_name
67     c_name = field.name
68     host = "%sArrayElement" % field_reference_name
69     net = "%s->%s[_i]" % (struct_ref_name, c_name)
70     swap_elements = field_type.get_host_to_net_function(host, net)
71     return _J2C_OBJECT_ARRAY_SWAP_TEMPLATE.substitute(
72         field_reference_name=field_reference_name,
73         field_length_check=_generate_field_length_check(field),
74         swap_elements=swap_elements)
75
76 _J2C_OBJECT_ARRAY_SWAP_TEMPLATE = Template("""
77     {
78         if (${field_reference_name}) {
79             size_t _i;
80             jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
81             ${field_length_check}
82             for (_i = 0; _i < cnt; _i++) {
83                 jobject ${field_reference_name}ArrayElement = (*env)->GetObjectArrayElement(env, ${field_reference_name}, _i);
84                 ${swap_elements};
85             }
86         }
87     }
88 """)
89
90
91 def _generate_j2c_primitive_type_array_swap(field, struct_ref_name):
92     field_reference_name = field.java_name
93     field_type = field.type
94     host = "%sArrayElements[_i]" % field_reference_name
95     net = "%s->%s[_i]" % (struct_ref_name,  field.name)
96     swap_elements = field_type.get_host_to_net_function(host, net)
97     return _J2C_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE.substitute(
98         field_reference_name=field_reference_name,
99         field_length_check=_generate_field_length_check(field),
100         base_type=field_type.base_type.jni_accessor,
101         jni_base_type=field_type.base_type.jni_type,
102         swap_elements=swap_elements
103     )
104
105 _J2C_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE = Template("""
106     if (${field_reference_name}) {
107         ${jni_base_type} * ${field_reference_name}ArrayElements = (*env)->Get${base_type}ArrayElements(env, ${field_reference_name}, NULL);
108         size_t _i;
109         jsize cnt = (*env)->GetArrayLength(env, ${field_reference_name});
110         ${field_length_check}
111         for (_i = 0; _i < cnt; _i++) {
112             ${swap_elements};
113         }
114         (*env)->Release${base_type}ArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
115     }
116     """)
117
118
119 def _generate_j2c_primitive_type_array_no_swap(field, struct_ref_name):
120     field_type = field.type
121     return _J2C_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE.substitute(
122         field_reference_name=field.java_name,
123         field_length_check=_generate_field_length_check(field),
124         base_type=field_type.base_type.jni_accessor,
125         jni_base_type=field_type.base_type.jni_type,
126         struct_reference_name=struct_ref_name,
127         c_name=field.name
128     )
129
130 _J2C_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE = Template("""
131     if (${field_reference_name}) {
132         jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
133         ${field_length_check}
134         (*env)->Get${base_type}ArrayRegion(env, ${field_reference_name}, 0, cnt, (${jni_base_type} *)${struct_reference_name}->${c_name});
135     }
136 """)
137
138
139 def _generate_field_length_check(field):
140     # Enforce max length if array has fixed length or uses variable length syntax
141     field_length = str(field.array_len)
142     if field.array_len_field:
143         field_length = field.array_len_field.java_name
144
145     # TODO: remove when ZLAs without length field are disabled
146     if field_length != "0":
147         return _FIELD_LENGTH_CHECK.substitute(field_length=field_length)
148     else:
149         return ""
150
151 # Make sure we do not write more elements that are expected
152 _FIELD_LENGTH_CHECK = Template("""
153         size_t max_size = ${field_length};
154         if (cnt > max_size) cnt = max_size;""")
155
156
157 def _generate_j2c_scalar_swap(field, struct_ref_name):
158     field_type = field.type
159     if field_type.is_swap_needed:
160         host = field.java_name
161         net = "%s->%s" % (struct_ref_name, field.name)
162         return "    %s;" % field_type.get_host_to_net_function(host, net)
163     else:
164         return "    %s->%s = %s;" % (struct_ref_name, field.name, field.java_name)
165
166
167 def generate_c2j_swap(element, object_ref_name, struct_ref_name):
168     msg_java_name = element.java_name_lower
169     initialization = []
170     for field in element.fields:
171         if is_retval(field):
172             # For retval don't generate setters and generate retval check
173             continue
174         elif is_array(field):
175             initialization.append(_generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_name))
176         else:
177             initialization.append(_generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_name))
178     return "".join(initialization)
179
180
181 def _generate_c2j_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
182     # TODO(VPP-1186): move the logic to JNI generators
183     base_type = field.type.base_type
184     if isinstance(base_type, Class):
185         return _generate_c2j_class_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
186     elif isinstance(base_type, Enum):
187         return _generate_c2j_enum_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
188     elif base_type.is_swap_needed:
189         return _generate_c2j_primitive_type_array_swap(msg_java_name, field, object_ref_name, struct_ref_name)
190     else:
191         return _generate_c2j_primitive_type_array_no_swap(msg_java_name, field, object_ref_name, struct_ref_name)
192
193
194 def _generate_c2j_class_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
195     field_type = field.type
196     return _C2J_CLASS_ARRAY_SWAP_TEMPLATE.substitute(
197         field_reference_name=field.java_name,
198         class_ref_name=msg_java_name,
199         jni_signature=field_type.jni_signature,
200         jni_name=field_type.base_type.jni_name,
201         field_length=_generate_array_length(field, struct_ref_name),
202         net_to_host_function=field_type.net_to_host_function,
203         struct_ref_name=struct_ref_name,
204         object_ref_name=object_ref_name,
205         c_name=field.name
206     )
207
208 _C2J_CLASS_ARRAY_SWAP_TEMPLATE = Template("""
209     jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
210     {
211         jclass ${field_reference_name}Class = (*env)->FindClass(env, "${jni_name}");
212         jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0);
213         jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V");
214         unsigned int _i;
215         for (_i = 0; _i < ${field_length}; _i++) {
216             jobject ${field_reference_name}ArrayElement = (*env)->NewObject(env, ${field_reference_name}Class,  ${field_reference_name}Constructor);
217             ${net_to_host_function}(env, &(${struct_ref_name}->${c_name}[_i]), ${field_reference_name}ArrayElement);
218             (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement);
219             (*env)->DeleteLocalRef(env, ${field_reference_name}ArrayElement);
220         }
221         (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
222         (*env)->DeleteLocalRef(env, ${field_reference_name});
223     }
224 """)
225
226
227 def _generate_c2j_enum_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
228     field_type = field.type
229     base_type = field_type.base_type
230     return _C2J_ENUM_ARRAY_SWAP_TEMPLATE.substitute(
231         field_reference_name=field.java_name,
232         class_ref_name=msg_java_name,
233         jni_signature=field_type.jni_signature,
234         jni_name=base_type.jni_name,
235         field_length=_generate_array_length(field, struct_ref_name),
236         net_to_host_function=field_type.net_to_host_function,
237         jni_signature_enum_value=base_type.value.type.jni_signature,
238         struct_ref_name=struct_ref_name,
239         object_ref_name=object_ref_name,
240         c_name=field.name
241     )
242
243 _C2J_ENUM_ARRAY_SWAP_TEMPLATE = Template("""
244     jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
245     {
246         jclass ${field_reference_name}Class = (*env)->FindClass(env, "${jni_name}");
247         jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0);
248         jmethodID ${field_reference_name}Constructor = (*env)->GetStaticMethodID(env, ${field_reference_name}Class, "forValue", "(${jni_signature_enum_value})${jni_signature}");
249         unsigned int _i;
250         for (_i = 0; _i < ${field_length}; _i++) {
251             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]));
252             (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement);
253             (*env)->DeleteLocalRef(env, ${field_reference_name}ArrayElement);
254         }
255         (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
256         (*env)->DeleteLocalRef(env, ${field_reference_name});
257     }
258 """)
259
260
261 def _generate_c2j_primitive_type_array_swap(msg_java_name, field, object_ref_name, struct_ref_name):
262     field_type = field.type
263     return _C2J_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE.substitute(
264         field_reference_name=field.java_name,
265         class_ref_name=msg_java_name,
266         jni_signature=field_type.jni_signature,
267         jni_type=field_type.jni_type,
268         base_type=field_type.base_type.jni_accessor,
269         field_length=_generate_array_length(field, struct_ref_name),
270         jni_base_type=field_type.base_type.jni_type,
271         object_ref_name=object_ref_name,
272         struct_ref_name=struct_ref_name,
273         net_to_host_function=field_type.net_to_host_function,
274         c_name=field.name
275     )
276
277 _C2J_PRIMITIVE_TYPE_ARRAY_SWAP_TEMPLATE = Template("""
278     jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
279     {
280         ${jni_type} ${field_reference_name} = (*env)->New${base_type}Array(env, ${field_length});
281         ${jni_base_type} * ${field_reference_name}ArrayElements = (*env)->Get${base_type}ArrayElements(env, ${field_reference_name}, NULL);
282         unsigned int _i;
283         for (_i = 0; _i < ${field_length}; _i++) {
284             ${field_reference_name}ArrayElements[_i] = ${net_to_host_function}(${struct_ref_name}->${c_name}[_i]);
285         }
286
287         (*env)->Release${base_type}ArrayElements(env,  ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
288         (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
289         (*env)->DeleteLocalRef(env, ${field_reference_name});
290     }
291 """)
292
293
294 def _generate_c2j_primitive_type_array_no_swap(msg_java_name, field, object_ref_name, struct_ref_name):
295     field_type = field.type
296     return _C2J_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE.substitute(
297         field_reference_name=field.java_name,
298         class_ref_name=msg_java_name,
299         jni_signature=field_type.jni_signature,
300         jni_type=field_type.jni_type,
301         base_type=field_type.base_type.jni_accessor,
302         field_length=_generate_array_length(field, struct_ref_name),
303         jni_base_type=field_type.base_type.jni_type,
304         object_ref_name=object_ref_name,
305         struct_ref_name=struct_ref_name,
306         c_name=field.name
307     )
308
309 _C2J_PRIMITIVE_TYPE_ARRAY_NO_SWAP_TEMPLATE = Template("""
310     jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
311     ${jni_type} ${field_reference_name} = (*env)->New${base_type}Array(env, ${field_length});
312     (*env)->Set${base_type}ArrayRegion(env, ${field_reference_name}, 0, ${field_length}, (const ${jni_base_type}*)${struct_ref_name}->${c_name});
313     (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
314     (*env)->DeleteLocalRef(env, ${field_reference_name});
315 """)
316
317
318 def _generate_array_length(field, struct_ref_name):
319     if field.array_len_field:
320         len_field = field.array_len_field
321         if len_field.type.is_swap_needed:
322             return "%s(%s->%s)" % (len_field.type.host_to_net_function, struct_ref_name, len_field.name)
323         else:
324             return "%s->%s" % (struct_ref_name, len_field.name)
325     return field.array_len
326
327
328 def _generate_c2j_scalar_swap(msg_java_name, field, object_ref_name, struct_ref_name):
329     field_type = field.type
330     if field_type.is_swap_needed:
331         # TODO(VPP-1186): move the logic to JNI generators
332         if isinstance(field_type, Class):
333             return _generate_c2j_class_swap(msg_java_name, field, object_ref_name, struct_ref_name)
334         elif isinstance(field_type, Enum):
335             return _generate_c2j_enum_swap(msg_java_name, field, object_ref_name, struct_ref_name)
336         else:
337             return _generate_c2j_primitive_type_swap(msg_java_name, field, object_ref_name, struct_ref_name)
338     else:
339         return _generate_c2j_primitive_type_no_swap(msg_java_name, field, object_ref_name, struct_ref_name)
340
341
342 def _generate_c2j_class_swap(msg_java_name, field, object_ref_name, struct_ref_name):
343     field_type = field.type
344     return _C2J_CLASS_SWAP_TEMPLATE.substitute(
345         java_name=field.java_name,
346         class_ref_name=msg_java_name,
347         jni_signature=field_type.jni_signature,
348         jni_name=field_type.jni_name,
349         jni_accessor=field_type.jni_accessor,
350         object_ref_name=object_ref_name,
351         struct_ref_name=struct_ref_name,
352         net_to_host_function=field_type.net_to_host_function,
353         c_name=field.name)
354
355 _C2J_CLASS_SWAP_TEMPLATE = Template("""
356     jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
357     jclass ${java_name}Class = (*env)->FindClass(env, "${jni_name}");
358     jmethodID ${java_name}Constructor = (*env)->GetMethodID(env, ${java_name}Class, "<init>", "()V");
359     jobject ${java_name} = (*env)->NewObject(env, ${java_name}Class,  ${java_name}Constructor);
360     ${net_to_host_function}(env, &(${struct_ref_name}->${c_name}), ${java_name});
361     (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${java_name});
362     (*env)->DeleteLocalRef(env, ${java_name});
363 """)
364
365
366 def _generate_c2j_enum_swap(msg_java_name, field, object_ref_name, struct_ref_name):
367     field_type = field.type
368     return _C2J_ENUM_SWAP_TEMPLATE.substitute(
369         java_name=field.java_name,
370         class_ref_name=msg_java_name,
371         jni_signature=field_type.jni_signature,
372         jni_signature_enum_value=field_type.value.type.jni_signature,
373         jni_name=field_type.jni_name,
374         jni_accessor=field_type.jni_accessor,
375         object_ref_name=object_ref_name,
376         struct_ref_name=struct_ref_name,
377         net_to_host_function=field_type.net_to_host_function,
378         c_name=field.name)
379
380 _C2J_ENUM_SWAP_TEMPLATE = Template("""
381     jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
382     jclass ${java_name}Class = (*env)->FindClass(env, "${jni_name}");
383     jmethodID ${java_name}Constructor = (*env)->GetStaticMethodID(env, ${java_name}Class, "forValue", "(${jni_signature_enum_value})${jni_signature}");
384     jobject ${java_name} = (*env)->CallStaticObjectMethod(env, ${java_name}Class, ${java_name}Constructor, ${net_to_host_function}(${struct_ref_name}->${c_name}));
385     (*env)->SetObjectField(env, ${object_ref_name}, ${java_name}FieldId, ${java_name});
386     (*env)->DeleteLocalRef(env, ${java_name});
387 """)
388
389
390 def _generate_c2j_primitive_type_swap(msg_java_name, field, object_ref_name, struct_ref_name):
391     field_type = field.type
392     return _C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE.substitute(
393         java_name=field.java_name,
394         class_ref_name=msg_java_name,
395         jni_signature=field_type.jni_signature,
396         jni_accessor=field_type.jni_accessor,
397         object_ref_name=object_ref_name,
398         net_to_host_function=field_type.net_to_host_function,
399         struct_ref_name=struct_ref_name,
400         c_name=field.name
401     )
402
403 _C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE = Template("""
404     jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
405     (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${net_to_host_function}(${struct_ref_name}->${c_name}));
406 """)
407
408
409 def _generate_c2j_primitive_type_no_swap(msg_java_name, field, object_ref_name, struct_ref_name):
410     field_type = field.type
411     return _C2J_PRIMITIVE_TYPE_NO_SWAP_TEMPLATE.substitute(
412         java_name=field.java_name,
413         class_ref_name=msg_java_name,
414         jni_signature=field_type.jni_signature,
415         jni_accessor=field_type.jni_accessor,
416         object_ref_name=object_ref_name,
417         struct_ref_name=struct_ref_name,
418         c_name=field.name
419     )
420
421 _C2J_PRIMITIVE_TYPE_NO_SWAP_TEMPLATE = Template("""
422     jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
423     (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${struct_ref_name}->${c_name});
424 """)