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