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 ctocpp_make_impl_proto(cls, func):
28    proto = 'ARK_WEB_NO_SANITIZE\n'
29    parts = func.get_cpp_parts(True)
30    if cls.get_name() is None:
31        proto += 'ARK_WEB_GLOBAL ' + parts['retval'] + ' ' + func.get_name() + '(' + ', '.join(parts['args']) + ') {'
32    else:
33        const = ''
34        proto += parts['retval'] + ' ' + cls.get_name()
35        if isinstance(func, file_parser.obj_function_virtual):
36            proto += 'CToCpp'
37            if func.is_const():
38                const = ' const'
39        proto += '::' + func.get_name() + '(' + ', '.join(parts['args']) + ')' + const + ' {'
40    return proto
41
42
43def verify_ctocpp_func_args(func, retval_default):
44    result = ''
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_byaddr':
52            result += comment + \
53                      '\n  if (!' + arg_name + ') {' + \
54                      '\n    return' + retval_default + ';' + \
55                      '\n  }'
56
57        # check index params
58        index_params = arg.parent.get_attrib_list('index_param')
59        if not index_params is None and arg_name in index_params:
60            result += comment + \
61                      '\n  if (' + arg_name + ' < 0) {' + \
62                      '\n    return' + retval_default + ';' + \
63                      '\n  }'
64    return result
65
66
67def restore_ctocpp_func_args(func):
68    result = ''
69    args = func.get_arguments()
70    for arg in args:
71        arg_type = arg.get_arg_type()
72        arg_name = arg.get_type().get_name()
73        comment = '\n  // Restore param:' + arg_name + '; type: ' + arg_type
74
75        if arg_type == 'bool_byaddr':
76            result += comment + \
77                      '\n  if (' + arg_name + ') {' + \
78                      '\n    *' + arg_name + ' = ' + arg_name + 'Int ? true : false;' + \
79                      '\n  }'
80        elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref':
81            ptr_class = arg.get_type().get_ptr_type()
82            if arg_type == 'refptr_same_byref':
83                assign = ptr_class + 'CToCpp::Invert(' + arg_name + 'Struct)'
84            else:
85                assign = ptr_class + 'CppToC::Revert(' + arg_name + 'Struct)'
86            result += comment + \
87                      '\n  if (' + arg_name + 'Struct) {' + \
88                      '\n    if (' + arg_name + 'Struct != ' + arg_name + 'Orig) {' + \
89                      '\n      ' + arg_name + ' = ' + assign + ';' + \
90                      '\n    }' + \
91                      '\n  } else {' + \
92                      '\n    ' + arg_name + ' = nullptr;' + \
93                      '\n  }'
94    return result;
95
96
97def translate_ctocpp_func_args(func):
98    params = []
99    if isinstance(func, file_parser.obj_function_virtual):
100        params.append('_struct')
101
102    result = ''
103    args = func.get_arguments()
104    for arg in args:
105        arg_type = arg.get_arg_type()
106        arg_name = arg.get_type().get_name()
107        comment = '\n  // Translate param: ' + arg_name + '; type: ' + arg_type
108
109        if arg_type == 'simple_byval' or arg_type == 'simple_byaddr' or arg_type == 'bool_byval':
110            if arg_name[0] == '*':
111                params.append(arg_name[1:])
112            else:
113                pos = arg_name.find('[')
114                if pos == -1:
115                    params.append(arg_name)
116                else:
117                    params.append(arg_name[0:pos])
118        elif arg_type == 'simple_byref' or arg_type == 'simple_byref_const' or \
119                arg_type == 'struct_byref_const' or arg_type == 'struct_byref':
120            params.append('&' + arg_name)
121        elif arg_type == 'bool_byref':
122            params.append('&' + arg_name)
123        elif arg_type == 'bool_byaddr':
124            result += comment + '\n  int ' + arg_name + 'Int = ' + arg_name + '?*' + arg_name + ':0;'
125            params.append('&' + arg_name + 'Int')
126        elif arg_type == 'refptr_same':
127            ptr_class = arg.get_type().get_ptr_type()
128            params.append(ptr_class + 'CToCpp::Revert(' + arg_name + ')')
129        elif arg_type == 'refptr_diff':
130            ptr_class = arg.get_type().get_ptr_type()
131            params.append(ptr_class + 'CppToC::Invert(' + arg_name + ')')
132        elif arg_type == 'refptr_same_byref' or arg_type == 'refptr_diff_byref':
133            ptr_class = arg.get_type().get_ptr_type()
134            ptr_struct = arg.get_type().get_result_ptr_type_root()
135            if arg_type == 'refptr_same_byref':
136                assign = ptr_class + 'CToCpp::Revert(' + arg_name + ')'
137            else:
138                assign = ptr_class + 'CppToC::Invert(' + arg_name + ')'
139            result += comment + \
140                      '\n  ' + ptr_struct + '* ' + arg_name + 'Struct = NULL;' + \
141                      '\n  if (' + arg_name + '.get()) {' + \
142                      '\n    ' + arg_name + 'Struct = ' + assign + ';' + \
143                      '\n  }' + \
144                      '\n  ' + ptr_struct + '* ' + arg_name + 'Orig = ' + arg_name + 'Struct;'
145            params.append('&' + arg_name + 'Struct')
146        else:
147            raise Exception('Unsupported argument type %s for parameter %s in %s' %
148                            (arg_type, arg_name, name))
149    return result, params
150
151
152def ctocpp_make_static_param(cls, funcs, prefix):
153    new_list = []
154    old_list = make_file_base.get_func_name_list(funcs)
155
156    impl = ''
157    for func in funcs:
158        new_list = make_file_base.get_func_name_count(func.get_capi_name(), old_list, new_list)
159
160        suffix = ''
161        if new_list.count(func.get_capi_name()) > 0:
162            suffix = str(new_list.count(func.get_capi_name()))
163
164        parts = func.get_capi_parts()
165        func_name, func_type = make_file_base.get_func_pointer_name(cls, func, prefix, suffix)
166        impl += 'using ' + func_type + ' = ' + parts['retval'] + ' (*)(' + ', '.join(parts['args']) + ');\n' + \
167                'static ' + func_type + ' ' + func_name + ' = reinterpret_cast<' + func_type + '>(ARK_WEB_INIT_ADDR);\n\n'
168    return impl
169
170
171def ctocpp_make_static_params(cls, header):
172    prefix = file_parser.get_capi_name(cls.get_name(), False)
173    result = ctocpp_make_static_param(cls, cls.get_static_funcs(), '')
174    result += ctocpp_make_static_param(cls, make_file_base.get_class_func_list(cls, header), prefix)
175    return result
176
177
178def ctocpp_get_static_funcion(cls, func, suffix, dir_name, retval_default):
179    func_name, func_type = make_file_base.get_func_pointer_name(cls, func, '', suffix)
180    result = '\n  ARK_WEB_CTOCPP_DV_LOG();\n' + \
181             '\n  void* func_pointer = reinterpret_cast<void*>(' + func_name + ');' + \
182             '\n  if (func_pointer == ARK_WEB_INIT_ADDR) {' + \
183             '\n    static const char* funcName = \"' + func_name + '_static\";' + \
184             '\n    ' + func_name + ' = reinterpret_cast<' + func_type + '>('
185    if dir_name == 'ohos_nweb':
186        if cls.is_webview_side():
187            result += 'ArkWebNWebWebcoreBridgeHelper::GetInstance().LoadFuncSymbol(funcName));'
188        else:
189            result += 'ArkWebNWebWebviewBridgeHelper::GetInstance().LoadFuncSymbol(funcName));'
190    else:
191        if cls.is_webview_side():
192            result += 'ArkWebAdapterWebcoreBridgeHelper::GetInstance().LoadFuncSymbol(funcName));'
193        else:
194            result += 'ArkWebAdapterWebviewBridgeHelper::GetInstance().LoadFuncSymbol(funcName));'
195    result += '\n  }\n' + \
196              '\n  if (!' + func_name + ') {' + \
197              '\n    ARK_WEB_CTOCPP_WARN_LOG("failed to load func ' + func_name + '_static");' + \
198              '\n    return ' + retval_default + ';' + \
199              '\n  }\n'
200    return result, func_name
201
202
203def ctocpp_get_virtual_funcion(cls, func, suffix, dir_name, macro_retval_default):
204    result = '\n  ARK_WEB_CTOCPP_DV_LOG(\"capi struct is %{public}ld\", (long)this);\n'
205
206    # determine how the struct should be referenced
207    if cls.get_name() == func.parent.get_name():
208        result += '\n  ' + file_parser.get_capi_name(cls.get_name(), True) + '* _struct = GetStruct();'
209    else:
210        result += '\n  ' + func.parent.get_capi_name() + '* _struct = reinterpret_cast<' + \
211                  func.parent.get_capi_name() + '*>(GetStruct());'
212
213    prefix = file_parser.get_capi_name(cls.get_name(), False)
214    hash_name = make_file_base.get_func_hash_name(func, prefix)
215    var_name = make_file_base.get_func_variable_name(func, suffix)
216    func_name, func_type = make_file_base.get_func_pointer_name(cls, func, prefix, suffix)
217    if isinstance(func, file_parser.obj_function_virtual):
218        result += '\n  ARK_WEB_CTOCPP_CHECK_PARAM(_struct, ' + macro_retval_default + ');\n' + \
219                  '\n  void* func_pointer = reinterpret_cast<void*>(' + func_name + ');' + \
220                  '\n  if (func_pointer == ARK_WEB_INIT_ADDR) {' + \
221                  '\n    static const std::string funcName = \"' + hash_name + '\";' + \
222                  '\n    func_pointer = '
223        if dir_name == 'ohos_nweb':
224            if cls.is_webview_side():
225                result += 'ArkWebNWebWebcoreBridgeHelper::GetInstance().CheckFuncMemberForCaller('
226            else:
227                result += 'ArkWebNWebWebviewBridgeHelper::GetInstance().CheckFuncMemberForCaller('
228        else:
229            if cls.is_webview_side():
230                result += 'ArkWebAdapterWebcoreBridgeHelper::GetInstance().CheckFuncMemberForCaller('
231            else:
232                result += 'ArkWebAdapterWebviewBridgeHelper::GetInstance().CheckFuncMemberForCaller('
233        result += file_parser.get_wrapper_type_enum(cls.get_name()) + ', funcName);'
234        result += '\n    if (func_pointer == ARK_WEB_INIT_ADDR) {' + \
235                  '\n      ARK_WEB_CTOCPP_DV_LOG(\"failed to find func member ' + func_name + '\");' + \
236                  '\n      if (ARK_WEB_FUNC_MEMBER_MISSING(_struct, ' + var_name + ')) {' + \
237                  '\n        ' + func_name + ' = nullptr;' + \
238                  '\n      } else {' + \
239                  '\n        ' + func_name + ' = _struct->' + var_name + ';' + \
240                  '\n      }' + \
241                  '\n    } else {' + \
242                  '\n      ' + func_name + ' = reinterpret_cast<' + func_type + '>(func_pointer);' + \
243                  '\n    }' + \
244                  '\n  }\n' + \
245                  '\n  ARK_WEB_CTOCPP_CHECK_FUNC_POINTER(' + func_name + ',' + macro_retval_default + ');\n'
246    return result, func_name
247
248
249def ctocpp_make_function_impl(cls, func, suffix, dir_name):
250    # build the C++ prototype  # build the C++ prototype
251    result = ctocpp_make_impl_proto(cls, func)
252
253    invalid = make_file_base.get_func_invalid_info(func.get_name(), func)
254    if len(invalid) > 0:
255        return result + invalid
256
257    retval = func.get_retval()
258    retval_default = retval.get_retval_default(False)
259    if len(retval_default) > 0:
260        macro_retval_default = retval_default
261        retval_default = ' ' + retval_default
262    else:
263        macro_retval_default = 'ARK_WEB_RETURN_VOID'
264
265    if isinstance(func, file_parser.obj_function_virtual):
266        func_body, func_name = ctocpp_get_virtual_funcion(cls, func, suffix, dir_name, macro_retval_default)
267    else:
268        func_body, func_name = ctocpp_get_static_funcion(cls, func, suffix, dir_name, retval_default)
269
270    result += func_body
271    result_len = len(result)
272
273    # parameter verification
274    result += verify_ctocpp_func_args(func, retval_default)
275    if len(result) != result_len:
276        result += '\n'
277        result_len = len(result)
278
279    # parameter translation
280    trans, params = translate_ctocpp_func_args(func)
281    if len(trans) != 0:
282        result += trans + '\n'
283
284    # execution
285    result += '\n  // Execute\n  '
286
287    retval_type = retval.get_retval_type()
288    if retval_type != 'none':
289        # has a return value
290        if retval_type == 'simple' or retval_type == 'bool' or retval_type == 'void*' or retval_type == 'uint8_t*' or \
291                retval_type == 'uint32_t*' or retval_type == 'char*' or file_parser.check_arg_type_is_struct(
292            retval_type):
293            result += 'return '
294        elif retval_type == 'refptr_same' or retval_type == 'refptr_diff':
295            ptr_struct = retval.get_type().get_result_ptr_type_root()
296            result += ptr_struct + '* _retval = '
297        else:
298            raise Exception('Unsupported return type %s in %s' % (retval_type, func.get_name()))
299
300    result += func_name + '('
301
302    if len(params) > 0:
303        if not isinstance(func, file_parser.obj_function_virtual):
304            result += '\n      '
305        result += ',\n      '.join(params)
306
307    result += ');\n'
308    result_len = len(result)
309
310    # parameter restoration
311    result += restore_ctocpp_func_args(func)
312    if len(result) != result_len:
313        result += '\n'
314        result_len = len(result)
315
316    if retval_type == 'refptr_same':
317        result += '\n  // Return type: ' + retval_type + \
318                  '\n  return ' + retval.get_type().get_ptr_type() + 'CToCpp::Invert(_retval);'
319    elif retval_type == 'refptr_diff':
320        result += '\n  // Return type: ' + retval_type + \
321                  '\n  return ' + retval.get_type().get_ptr_type() + 'CppToC::Revert(_retval);'
322
323    if len(result) != result_len:
324        result += '\n'
325
326    result += '}\n\n'
327    return result
328
329
330def ctocpp_make_function_body(cls, funcs, dir_name):
331    new_list = []
332    old_list = make_file_base.get_func_name_list(funcs)
333
334    impl = ''
335    for func in funcs:
336        new_list = make_file_base.get_func_name_count(func.get_capi_name(), old_list, new_list)
337
338        suffix = ''
339        if new_list.count(func.get_capi_name()) > 0:
340            suffix = str(new_list.count(func.get_capi_name()))
341
342        impl += ctocpp_make_function_impl(cls, func, suffix, dir_name)
343    return impl
344
345
346def ctocpp_make_functions_body(cls, header, dir_name):
347    clsname = cls.get_name();
348    result = ctocpp_make_function_body(cls, cls.get_static_funcs(), dir_name)
349    result += ctocpp_make_function_body(cls, make_file_base.get_class_func_list(cls, header), dir_name)
350    result += clsname + 'CToCpp::' + clsname + 'CToCpp() {\n}\n\n' + \
351              clsname + 'CToCpp::~' + clsname + 'CToCpp() {\n}\n\n'
352    return result
353
354
355def ctocpp_make_include_files(cls, body, header, dir_name):
356    result = file_parser.format_translation_includes(header, dir_name, body)
357    result += '#include "base/ctocpp/ark_web_ctocpp_macros.h"\n'
358    if dir_name == 'ohos_nweb':
359        if cls.is_webview_side():
360            result += '#include "ohos_nweb/bridge/ark_web_nweb_webcore_bridge_helper.h"\n'
361        else:
362            result += '#include "ohos_nweb/bridge/ark_web_nweb_webview_bridge_helper.h"\n'
363    else:
364        if cls.is_webview_side():
365            result += '#include "ohos_adapter/bridge/ark_web_adapter_webcore_bridge_helper.h"\n'
366        else:
367            result += '#include "ohos_adapter/bridge/ark_web_adapter_webview_bridge_helper.h"\n'
368    return result
369
370
371def ctocpp_make_unwrap_derived(cls, header, clsname):
372    impl = ''
373    derived_classes = make_file_base.get_derived_classes(cls, header)
374    for clsname in derived_classes:
375        impl += '  if (type == ' + file_parser.get_wrapper_type_enum(clsname) + ') {\n' + \
376                '    return reinterpret_cast<' + file_parser.get_capi_name(clsname, True) + '*>(' + \
377                clsname + 'CToCpp::Revert(reinterpret_cast<' + clsname + '*>(c)));\n' + \
378                '  }\n'
379    return impl
380
381
382def make_ctocpp_impl_file(header, dir_path, dir_name, clsname):
383    cls = header.get_class(clsname)
384    if cls is None:
385        raise Exception('Class does not exist: ' + clsname)
386
387    static_param = ctocpp_make_static_params(cls, header)
388    function_body = ctocpp_make_functions_body(cls, header, dir_name)
389
390    unwrapderived = ctocpp_make_unwrap_derived(cls, header, clsname)
391    includes = ctocpp_make_include_files(cls, function_body + unwrapderived, header, dir_name)
392
393    content = make_file_base.get_copyright() + '\n' + includes + '\n' + \
394              'namespace OHOS::ArkWeb {\n\n' + \
395              static_param + function_body + \
396              make_file_base.make_wrapper_type(cls, 'CToCpp') + \
397              '\n\n} // namespace OHOS::ArkWeb\n'
398
399    absolute_dir = os.path.join(os.path.join(dir_path, dir_name), 'ctocpp')
400    absolute_path = os.path.join(absolute_dir, file_parser.get_capi_name(clsname, False) + '_ctocpp.cpp')
401    return (content, absolute_path)
402