1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4# Copyright (c) 2024 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17from __future__ import absolute_import
18
19import os
20import file_parser
21import make_file_base
22
23
24# pylint:disable=variable-type-changed
25# pylint:disable=huawei-redefined-outer-name
26
27def make_cpptoc_impl_proto(name, func, parts, flag):
28    if isinstance(func, file_parser.obj_function_virtual):
29        proto = parts['retval'] + ' ARK_WEB_CALLBACK'
30    elif flag:
31        proto = 'ARK_WEB_EXPORT ' + parts['retval']
32    else:
33        proto = parts['retval']
34
35    proto += ' ' + name + '(' + ', '.join(parts['args']) + ')'
36    return proto
37
38
39def verify_cpptoc_func_args(func, retval_default, macro_retval_default):
40    result = ''
41    if isinstance(func, file_parser.obj_function_virtual):
42        result += '\n  ARK_WEB_CPPTOC_DV_LOG(\"capi struct is %{public}ld\", (long)self);\n' + \
43                  '\n  ARK_WEB_CPPTOC_CHECK_PARAM(self, ' + macro_retval_default + ');'
44
45    args = func.get_arguments()
46    for arg in args:
47        arg_type = arg.get_arg_type()
48        arg_name = arg.get_type().get_name()
49        comment = '\n  // Verify param: ' + arg_name + '; type: ' + arg_type
50
51        if arg_type == 'bool_byref' or arg_type == 'bool_byref_const' or arg_type == 'simple_byref' or \
52                arg_type == 'simple_byref_const' or arg_type == 'struct_byref' or arg_type == 'struct_byref_const' or \
53                arg_type == 'refptr_diff_byref':
54            result += '\n  ARK_WEB_CPPTOC_CHECK_PARAM(' + arg_name + ', ' + macro_retval_default + ');'
55            if arg_type == 'struct_byref_const' or arg_type == 'struct_byref':
56                result += '\n  if (!template_util::has_valid_size(' + arg_name + ')) {' + \
57                          '\n    return' + retval_default + ';' + \
58                          '\n  }'
59
60        # check index params
61        index_params = arg.parent.get_attrib_list('index_param')
62        if not index_params is None and arg_name in index_params:
63            result += comment + \
64                      '\n  if (' + arg_name + ' < 0) {' + \
65                      '\n    return' + retval_default + ';' + \
66                      '\n  }'
67    return result
68
69
70def restore_cpptoc_func_args(func):
71    result = ''
72    args = func.get_arguments()
73    for arg in args:
74        arg_type = arg.get_arg_type()
75        arg_name = arg.get_type().get_name()
76        comment = '\n  // Restore param: ' + arg_name + '; type: ' + arg_type
77
78        if arg_type == 'struct_byref':
79            result += comment + \
80                      '\n  if (' + arg_name + ') {' + \
81                      '\n    ' + arg_name + 'Obj.DetachTo(*' + arg_name + ');' + \
82                      '\n  }'
83        elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref':
84            ptr_class = arg.get_type().get_ptr_type()
85            if arg_type == 'refptr_same_byref':
86                assign = ptr_class + 'CppToC::Invert(' + arg_name + 'Ptr)'
87            else:
88                assign = ptr_class + 'CToCpp::Revert(' + arg_name + 'Ptr)'
89            result += comment + \
90                      '\n  if (' + arg_name + ') {' + \
91                      '\n    if (' + arg_name + 'Ptr.get()) {' + \
92                      '\n      if (' + arg_name + 'Ptr.get() != ' + arg_name + 'Orig) {' + \
93                      '\n        *' + arg_name + ' = ' + assign + ';' + \
94                      '\n      }' + \
95                      '\n    } else {' + \
96                      '\n      *' + arg_name + ' = nullptr;' + \
97                      '\n    }' + \
98                      '\n  }'
99    return result;
100
101
102def translate_cpptoc_func_args(func):
103    result = ''
104    params = []
105    args = func.get_arguments()
106    for arg in args:
107        arg_type = arg.get_arg_type()
108        arg_name = arg.get_type().get_name()
109        comment = '  // Translate param: ' + arg_name + '; type: ' + arg_type
110
111        if arg_type == 'simple_byval' or arg_type == 'simple_byaddr':
112            if arg_name[0] == '*':
113                params.append(arg_name[1:])
114            else:
115                pos = arg_name.find('[')
116                if pos == -1:
117                    params.append(arg_name)
118                else:
119                    params.append(arg_name[0:pos])
120        elif arg_type == 'simple_byref' or arg_type == 'simple_byref_const':
121            params.append('*' + arg_name)
122        elif arg_type == 'bool_byval':
123            params.append(arg_name)
124        elif arg_type == 'bool_byref' or arg_type == 'bool_byaddr':
125            params.append('*' + arg_name)
126        elif arg_type == 'struct_byref_const':
127            struct_type = arg.get_type().get_type()
128            result += comment + \
129                      '\n  ' + struct_type + ' ' + arg_name + 'Obj;' + \
130                      '\n  if (' + arg_name + ') {' + \
131                      '\n    ' + arg_name + 'Obj.Set(*' + arg_name + ', false);' + \
132                      '\n  }'
133            params.append(arg_name + 'Obj')
134        elif arg_type == 'struct_byref':
135            struct_type = arg.get_type().get_type()
136            result += comment + \
137                      '\n  ' + struct_type + ' ' + arg_name + 'Obj;' + \
138                      '\n  if (' + arg_name + ') {' + \
139                      '\n    ' + arg_name + 'Obj.AttachTo(*' + arg_name + ');' + \
140                      '\n  }'
141            params.append(arg_name + 'Obj')
142        elif arg_type == 'refptr_same' or arg_type == 'refptr_diff':
143            ptr_class = arg.get_type().get_ptr_type()
144            if arg_type == 'refptr_same':
145                params.append(ptr_class + 'CppToC::Revert(' + arg_name + ')')
146            else:
147                params.append(ptr_class + 'CToCpp::Invert(' + arg_name + ')')
148        elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref':
149            ptr_class = arg.get_type().get_ptr_type()
150            if arg_type == 'refptr_same_byref':
151                assign = ptr_class + 'CppToC::Revert(*' + arg_name + ')'
152            else:
153                assign = ptr_class + 'CToCpp::Invert(*' + arg_name + ')'
154            result += comment + \
155                      '\n  ArkWebRefPtr<' + ptr_class + '> ' + arg_name + 'Ptr;' + \
156                      '\n  if (' + arg_name + ' && *' + arg_name + ') {' + \
157                      '\n    ' + arg_name + 'Ptr = ' + assign + ';' + \
158                      '\n  }' + \
159                      '\n  ' + ptr_class + '* ' + arg_name + 'Orig = ' + arg_name + 'Ptr.get();'
160            params.append(arg_name + 'Ptr')
161        else:
162            raise Exception('Unsupported argument type %s for parameter %s in %s' %
163                            (arg_type, arg_name, name))
164    return result, params
165
166
167def make_cpptoc_function_impl(cls, name, func, defined_names):
168    # retrieve the C API prototype parts
169    parts = func.get_capi_parts(defined_names, True)
170    result = make_cpptoc_impl_proto(name, func, parts, False) + ' {'
171
172    invalid = make_file_base.get_func_invalid_info(name, func)
173    if len(invalid) > 0:
174        return result + invalid
175
176    retval = func.get_retval()
177    retval_default = retval.get_retval_default(True)
178    if len(retval_default) > 0:
179        retval_default = ' ' + retval_default
180        macro_retval_default = retval_default
181    else:
182        macro_retval_default = 'ARK_WEB_RETURN_VOID'
183    result_len = len(result)
184
185    # parameter verification
186    result += verify_cpptoc_func_args(func, retval_default, macro_retval_default)
187    if len(result) != result_len:
188        result += '\n'
189        result_len = len(result)
190
191    # parameter translation
192    trans, params = translate_cpptoc_func_args(func)
193    if len(trans) != 0:
194        result += trans + '\n'
195
196    # execution
197    result += '\n  // Execute\n  '
198
199    retval_type = retval.get_retval_type()
200    if retval_type != 'none':
201        # has a return value
202        if retval_type == 'simple' or retval_type == 'bool' or retval_type == 'void*' or \
203                retval_type == 'uint8_t*' or retval_type == 'uint32_t*' or retval_type == 'char*' or \
204                file_parser.check_arg_type_is_struct(retval_type):
205            result += 'return '
206        else:
207            result += retval.get_type().get_type() + ' _retval = '
208
209    if isinstance(func.parent, file_parser.obj_class):
210        parent_clsname = func.parent.get_name()
211        if isinstance(func, file_parser.obj_function_virtual):
212            if cls.get_name() == parent_clsname:
213                result += parent_clsname + 'CppToC::Get(self)->'
214            else:
215                result += cls.get_name() + 'CppToC::Get(reinterpret_cast<' + cls.get_capi_name() + '*>(self))->'
216        else:
217            result += parent_clsname + '::'
218    result += func.get_name() + '('
219
220    if len(params) > 0:
221        result += '\n      ' + ',\n      '.join(params)
222    result += ');\n'
223    result_len = len(result)
224
225    # parameter restoration
226    result += restore_cpptoc_func_args(func)
227    if len(result) != result_len:
228        result += '\n'
229        result_len = len(result)
230
231    if retval_type == 'refptr_same':
232        result += '\n  // Return type: ' + retval_type + \
233                  '\n  return ' + retval.get_type().get_ptr_type() + 'CppToC::Invert(_retval);'
234    elif retval_type == 'refptr_diff':
235        result += '\n  // Return type: ' + retval_type + \
236                  '\n  return ' + retval.get_type().get_ptr_type() + 'CToCpp::Revert(_retval);'
237
238    if len(result) != result_len:
239        result += '\n'
240
241    result += '}\n\n'
242    return result
243
244
245def make_cpptoc_function_body(cls, funcs, prefix, defined_names):
246    new_list = []
247    old_list = make_file_base.get_func_name_list(funcs)
248
249    impl = ''
250    for func in funcs:
251        suffix = ''
252        new_list = make_file_base.get_func_name_count(func.get_capi_name(), old_list, new_list)
253        if new_list.count(func.get_capi_name()) != 0:
254            suffix = str(new_list.count(func.get_capi_name()))
255
256        name, _ = make_file_base.get_func_pointer_name(cls, func, prefix, suffix)
257        impl += make_cpptoc_function_impl(cls, name, func, defined_names)
258    return impl
259
260
261def cpptoc_make_function_assign(cls, prefix, header):
262    funcs = make_file_base.get_class_func_list(cls, header);
263    new_list = []
264    old_list = make_file_base.get_func_name_list(funcs)
265
266    insert = ''
267    assign = ''
268    for func in funcs:
269        suffix = ''
270        new_list = make_file_base.get_func_name_count(func.get_capi_name(), old_list, new_list)
271        if new_list.count(func.get_capi_name()) != 0:
272            suffix = str(new_list.count(func.get_capi_name()))
273
274        hash_name = make_file_base.get_func_hash_name(func, prefix)
275        var_name = make_file_base.get_func_variable_name(func, suffix)
276        func_name, _ = make_file_base.get_func_pointer_name(cls, func, prefix, suffix)
277        insert += '  funcMemberMap[\"' + hash_name + '\"] = reinterpret_cast<void*>(' + func_name + ');\n  '
278        assign += '  GetStruct()->' + var_name + ' = ' + func_name + ';\n'
279    return assign, insert
280
281
282def make_cpptoc_static_function_impl(cls, funcs, defined_names):
283    new_list = []
284    old_list = make_file_base.get_func_name_list(funcs)
285
286    impl = '#ifdef __cplusplus\nextern "C" {\n#endif // __cplusplus\n\n'
287    for func in funcs:
288        suffix = ''
289        new_list = make_file_base.get_func_name_count(func.get_capi_name(), old_list, new_list)
290        if new_list.count(func.get_capi_name()) != 0:
291            suffix = str(new_list.count(func.get_capi_name()))
292        parts = func.get_capi_parts(defined_names, True)
293        func_name, _ = make_file_base.get_func_pointer_name(cls, func, '', suffix)
294        impl += make_cpptoc_impl_proto(func_name + '_static', func, parts, True) + ' {\n' + \
295                '  ARK_WEB_CPPTOC_DV_LOG();\n\n'
296
297        retval = func.get_retval()
298        retval_type = retval.get_retval_type()
299        if retval_type != 'none':
300            impl += '  return '
301        impl += 'OHOS::ArkWeb::' + func_name + '('
302
303        params = []
304        args = func.get_arguments()
305        for arg in args:
306            arg_name = arg.get_type().get_name()
307            params.append(arg_name)
308
309        if len(params) > 0:
310            impl += '\n      ' + ',\n      '.join(params)
311        impl += ');\n}\n\n'
312
313    impl += '#ifdef __cplusplus\n}\n#endif // __cplusplus'
314    return impl
315
316
317def cpptoc_make_unwrap_derived(cls, header, clsname):
318    impl = ''
319    derived_classes = make_file_base.get_derived_classes(cls, header)
320    for clsname in derived_classes:
321        impl += '  if (type == ' + file_parser.get_wrapper_type_enum(clsname) + ') {\n' + \
322                '    return ' + clsname + 'CppToC::Revert(reinterpret_cast<' + \
323                file_parser.get_capi_name(clsname, True) + '*>(s));\n' + \
324                '  }\n'
325    return impl
326
327
328def cpptoc_make_include_file(cls, body, header, dir_name):
329    result = file_parser.format_translation_includes(header, dir_name, body)
330    result += '#include "base/cpptoc/ark_web_cpptoc_macros.h"\n'
331    if dir_name == 'ohos_nweb':
332        if cls.is_webview_side():
333            result += '#include "ohos_nweb/bridge/ark_web_nweb_webview_bridge_helper.h"\n'
334        else:
335            result += '#include "ohos_nweb/bridge/ark_web_nweb_webcore_bridge_helper.h"\n'
336    else:
337        if cls.is_webview_side():
338            result += '#include "ohos_adapter/bridge/ark_web_adapter_webview_bridge_helper.h"\n'
339        else:
340            result += '#include "ohos_adapter/bridge/ark_web_adapter_webcore_bridge_helper.h"\n'
341    return result
342
343
344def cpptoc_make_class_function(cls, prefix, header, dir_name):
345    assign, insert = cpptoc_make_function_assign(cls, prefix, header)
346    result = cls.get_name() + 'CppToC::' + cls.get_name() + 'CppToC() {' + \
347             '\n' + assign + \
348             '\n  static std::once_flag flag;' + \
349             '\n  std::call_once(flag, [] {' + \
350             '\n    std::map<std::string, void*> funcMemberMap;' + \
351             '\n  ' + insert
352    if dir_name == 'ohos_nweb':
353        if cls.is_webview_side():
354            result += '  ArkWebNWebWebviewBridgeHelper::GetInstance().RegisterFuncMember('
355        else:
356            result += '  ArkWebNWebWebcoreBridgeHelper::GetInstance().RegisterFuncMember('
357    else:
358        if cls.is_webview_side():
359            result += '  ArkWebAdapterWebviewBridgeHelper::GetInstance().RegisterFuncMember('
360        else:
361            result += '  ArkWebAdapterWebcoreBridgeHelper::GetInstance().RegisterFuncMember('
362    result += file_parser.get_wrapper_type_enum(cls.get_name()) + ', funcMemberMap);\n  });\n}\n' + \
363              '\n' + cls.get_name() + 'CppToC::~' + cls.get_name() + 'CppToC() {\n}\n\n'
364    return result
365
366
367def cpptoc_make_c_function_body(cls, header, dir_name, defined_names):
368    funcs = make_file_base.get_class_func_list(cls, header)
369    prefix = file_parser.get_capi_name(cls.get_name(), False)
370    result = make_cpptoc_function_body(cls, funcs, prefix, defined_names)
371    if len(result) > 0:
372        result = make_cpptoc_function_body(cls, cls.get_static_funcs(), '', defined_names) + \
373                 'namespace {\n\n' + result + '}  // namespace\n\n' + \
374                 cpptoc_make_class_function(cls, prefix, header, dir_name)
375    else:
376        result = make_cpptoc_function_body(cls, cls.get_static_funcs(), '', defined_names) + \
377                 cpptoc_make_class_function(cls, prefix, header, dir_name)
378    return result
379
380
381def make_cpptoc_impl_file(header, dir_path, dir_name, clsname):
382    defined_names = header.get_defined_structs()
383    cls = header.get_class(clsname, defined_names)
384    if cls is None:
385        raise Exception('Class does not exist: ' + clsname)
386
387    defined_names.append(cls.get_capi_name())
388    function_body = cpptoc_make_c_function_body(cls, header, dir_name, defined_names)
389
390    unwrap_derived = cpptoc_make_unwrap_derived(cls, header, clsname)
391    include_file = cpptoc_make_include_file(cls, function_body + unwrap_derived, header, dir_name)
392
393    content = make_file_base.get_copyright() + '\n' + include_file + '\n' + \
394              'namespace OHOS::ArkWeb {\n\n' + \
395              function_body + '\n' + \
396              '\n' + make_file_base.make_wrapper_type(cls, 'CppToC') + \
397              '\n\n} // namespace OHOS::ArkWeb\n\n'
398
399    if len(cls.get_static_funcs()) > 0:
400        content += make_cpptoc_static_function_impl(cls, cls.get_static_funcs(), defined_names)
401
402    absolute_dir = os.path.join(os.path.join(dir_path, dir_name), 'cpptoc')
403    absolute_path = os.path.join(absolute_dir, file_parser.get_capi_name(clsname, False) + '_cpptoc.cpp')
404    return (content, absolute_path)
405