1a8e1175bSopenharmony_ci"""Generate C wrapper functions.
2a8e1175bSopenharmony_ci"""
3a8e1175bSopenharmony_ci
4a8e1175bSopenharmony_ci# Copyright The Mbed TLS Contributors
5a8e1175bSopenharmony_ci# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
6a8e1175bSopenharmony_ci
7a8e1175bSopenharmony_ci### WARNING: the code in this file has not been extensively reviewed yet.
8a8e1175bSopenharmony_ci### We do not think it is harmful, but it may be below our normal standards
9a8e1175bSopenharmony_ci### for robustness and maintainability.
10a8e1175bSopenharmony_ci
11a8e1175bSopenharmony_ciimport os
12a8e1175bSopenharmony_ciimport re
13a8e1175bSopenharmony_ciimport sys
14a8e1175bSopenharmony_ciimport typing
15a8e1175bSopenharmony_cifrom typing import Dict, List, Optional, Tuple
16a8e1175bSopenharmony_ci
17a8e1175bSopenharmony_cifrom .c_parsing_helper import ArgumentInfo, FunctionInfo
18a8e1175bSopenharmony_cifrom . import typing_util
19a8e1175bSopenharmony_ci
20a8e1175bSopenharmony_ci
21a8e1175bSopenharmony_cidef c_declare(prefix: str, name: str, suffix: str) -> str:
22a8e1175bSopenharmony_ci    """Format a declaration of name with the given type prefix and suffix."""
23a8e1175bSopenharmony_ci    if not prefix.endswith('*'):
24a8e1175bSopenharmony_ci        prefix += ' '
25a8e1175bSopenharmony_ci    return prefix + name + suffix
26a8e1175bSopenharmony_ci
27a8e1175bSopenharmony_ci
28a8e1175bSopenharmony_ciWrapperInfo = typing.NamedTuple('WrapperInfo', [
29a8e1175bSopenharmony_ci    ('argument_names', List[str]),
30a8e1175bSopenharmony_ci    ('guard', Optional[str]),
31a8e1175bSopenharmony_ci    ('wrapper_name', str),
32a8e1175bSopenharmony_ci])
33a8e1175bSopenharmony_ci
34a8e1175bSopenharmony_ci
35a8e1175bSopenharmony_ciclass Base:
36a8e1175bSopenharmony_ci    """Generate a C source file containing wrapper functions."""
37a8e1175bSopenharmony_ci
38a8e1175bSopenharmony_ci    # This class is designed to have many methods potentially overloaded.
39a8e1175bSopenharmony_ci    # Tell pylint not to complain about methods that have unused arguments:
40a8e1175bSopenharmony_ci    # child classes are likely to override those methods and need the
41a8e1175bSopenharmony_ci    # arguments in question.
42a8e1175bSopenharmony_ci    #pylint: disable=no-self-use,unused-argument
43a8e1175bSopenharmony_ci
44a8e1175bSopenharmony_ci    # Prefix prepended to the function's name to form the wrapper name.
45a8e1175bSopenharmony_ci    _WRAPPER_NAME_PREFIX = ''
46a8e1175bSopenharmony_ci    # Suffix appended to the function's name to form the wrapper name.
47a8e1175bSopenharmony_ci    _WRAPPER_NAME_SUFFIX = '_wrap'
48a8e1175bSopenharmony_ci
49a8e1175bSopenharmony_ci    # Functions with one of these qualifiers are skipped.
50a8e1175bSopenharmony_ci    _SKIP_FUNCTION_WITH_QUALIFIERS = frozenset(['inline', 'static'])
51a8e1175bSopenharmony_ci
52a8e1175bSopenharmony_ci    def __init__(self):
53a8e1175bSopenharmony_ci        """Construct a wrapper generator object.
54a8e1175bSopenharmony_ci        """
55a8e1175bSopenharmony_ci        self.program_name = os.path.basename(sys.argv[0])
56a8e1175bSopenharmony_ci        # To be populated in a derived class
57a8e1175bSopenharmony_ci        self.functions = {} #type: Dict[str, FunctionInfo]
58a8e1175bSopenharmony_ci        # Preprocessor symbol used as a guard against multiple inclusion in the
59a8e1175bSopenharmony_ci        # header. Must be set before writing output to a header.
60a8e1175bSopenharmony_ci        # Not used when writing .c output.
61a8e1175bSopenharmony_ci        self.header_guard = None #type: Optional[str]
62a8e1175bSopenharmony_ci
63a8e1175bSopenharmony_ci    def _write_prologue(self, out: typing_util.Writable, header: bool) -> None:
64a8e1175bSopenharmony_ci        """Write the prologue of a C file.
65a8e1175bSopenharmony_ci
66a8e1175bSopenharmony_ci        This includes a description comment and some include directives.
67a8e1175bSopenharmony_ci        """
68a8e1175bSopenharmony_ci        out.write("""/* Automatically generated by {}, do not edit! */
69a8e1175bSopenharmony_ci
70a8e1175bSopenharmony_ci/* Copyright The Mbed TLS Contributors
71a8e1175bSopenharmony_ci * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
72a8e1175bSopenharmony_ci */
73a8e1175bSopenharmony_ci"""
74a8e1175bSopenharmony_ci                  .format(self.program_name))
75a8e1175bSopenharmony_ci        if header:
76a8e1175bSopenharmony_ci            out.write("""
77a8e1175bSopenharmony_ci#ifndef {guard}
78a8e1175bSopenharmony_ci#define {guard}
79a8e1175bSopenharmony_ci
80a8e1175bSopenharmony_ci#ifdef __cplusplus
81a8e1175bSopenharmony_ciextern "C" {{
82a8e1175bSopenharmony_ci#endif
83a8e1175bSopenharmony_ci"""
84a8e1175bSopenharmony_ci                      .format(guard=self.header_guard))
85a8e1175bSopenharmony_ci        out.write("""
86a8e1175bSopenharmony_ci#include <mbedtls/build_info.h>
87a8e1175bSopenharmony_ci""")
88a8e1175bSopenharmony_ci
89a8e1175bSopenharmony_ci    def _write_epilogue(self, out: typing_util.Writable, header: bool) -> None:
90a8e1175bSopenharmony_ci        """Write the epilogue of a C file.
91a8e1175bSopenharmony_ci        """
92a8e1175bSopenharmony_ci        if header:
93a8e1175bSopenharmony_ci            out.write("""
94a8e1175bSopenharmony_ci#ifdef __cplusplus
95a8e1175bSopenharmony_ci}}
96a8e1175bSopenharmony_ci#endif
97a8e1175bSopenharmony_ci
98a8e1175bSopenharmony_ci#endif /* {guard} */
99a8e1175bSopenharmony_ci"""
100a8e1175bSopenharmony_ci                      .format(guard=self.header_guard))
101a8e1175bSopenharmony_ci        out.write("""
102a8e1175bSopenharmony_ci/* End of automatically generated file. */
103a8e1175bSopenharmony_ci""")
104a8e1175bSopenharmony_ci
105a8e1175bSopenharmony_ci    def _wrapper_function_name(self, original_name: str) -> str:
106a8e1175bSopenharmony_ci        """The name of the wrapper function.
107a8e1175bSopenharmony_ci
108a8e1175bSopenharmony_ci        By default, this adds a suffix.
109a8e1175bSopenharmony_ci        """
110a8e1175bSopenharmony_ci        return (self._WRAPPER_NAME_PREFIX +
111a8e1175bSopenharmony_ci                original_name +
112a8e1175bSopenharmony_ci                self._WRAPPER_NAME_SUFFIX)
113a8e1175bSopenharmony_ci
114a8e1175bSopenharmony_ci    def _wrapper_declaration_start(self,
115a8e1175bSopenharmony_ci                                   function: FunctionInfo,
116a8e1175bSopenharmony_ci                                   wrapper_name: str) -> str:
117a8e1175bSopenharmony_ci        """The beginning of the wrapper function declaration.
118a8e1175bSopenharmony_ci
119a8e1175bSopenharmony_ci        This ends just before the opening parenthesis of the argument list.
120a8e1175bSopenharmony_ci
121a8e1175bSopenharmony_ci        This is a string containing at least the return type and the
122a8e1175bSopenharmony_ci        function name. It may start with additional qualifiers or attributes
123a8e1175bSopenharmony_ci        such as `static`, `__attribute__((...))`, etc.
124a8e1175bSopenharmony_ci        """
125a8e1175bSopenharmony_ci        return c_declare(function.return_type, wrapper_name, '')
126a8e1175bSopenharmony_ci
127a8e1175bSopenharmony_ci    def _argument_name(self,
128a8e1175bSopenharmony_ci                       function_name: str,
129a8e1175bSopenharmony_ci                       num: int,
130a8e1175bSopenharmony_ci                       arg: ArgumentInfo) -> str:
131a8e1175bSopenharmony_ci        """Name to use for the given argument in the wrapper function.
132a8e1175bSopenharmony_ci
133a8e1175bSopenharmony_ci        Argument numbers count from 0.
134a8e1175bSopenharmony_ci        """
135a8e1175bSopenharmony_ci        name = 'arg' + str(num)
136a8e1175bSopenharmony_ci        if arg.name:
137a8e1175bSopenharmony_ci            name += '_' + arg.name
138a8e1175bSopenharmony_ci        return name
139a8e1175bSopenharmony_ci
140a8e1175bSopenharmony_ci    def _wrapper_declaration_argument(self,
141a8e1175bSopenharmony_ci                                      function_name: str,
142a8e1175bSopenharmony_ci                                      num: int, name: str,
143a8e1175bSopenharmony_ci                                      arg: ArgumentInfo) -> str:
144a8e1175bSopenharmony_ci        """One argument definition in the wrapper function declaration.
145a8e1175bSopenharmony_ci
146a8e1175bSopenharmony_ci        Argument numbers count from 0.
147a8e1175bSopenharmony_ci        """
148a8e1175bSopenharmony_ci        return c_declare(arg.type, name, arg.suffix)
149a8e1175bSopenharmony_ci
150a8e1175bSopenharmony_ci    def _underlying_function_name(self, function: FunctionInfo) -> str:
151a8e1175bSopenharmony_ci        """The name of the underlying function.
152a8e1175bSopenharmony_ci
153a8e1175bSopenharmony_ci        By default, this is the name of the wrapped function.
154a8e1175bSopenharmony_ci        """
155a8e1175bSopenharmony_ci        return function.name
156a8e1175bSopenharmony_ci
157a8e1175bSopenharmony_ci    def _return_variable_name(self, function: FunctionInfo) -> str:
158a8e1175bSopenharmony_ci        """The name of the variable that will contain the return value."""
159a8e1175bSopenharmony_ci        return 'retval'
160a8e1175bSopenharmony_ci
161a8e1175bSopenharmony_ci    def _write_function_call(self, out: typing_util.Writable,
162a8e1175bSopenharmony_ci                             function: FunctionInfo,
163a8e1175bSopenharmony_ci                             argument_names: List[str]) -> None:
164a8e1175bSopenharmony_ci        """Write the call to the underlying function.
165a8e1175bSopenharmony_ci        """
166a8e1175bSopenharmony_ci        # Note that the function name is in parentheses, to avoid calling
167a8e1175bSopenharmony_ci        # a function-like macro with the same name, since in typical usage
168a8e1175bSopenharmony_ci        # there is a function-like macro with the same name which is the
169a8e1175bSopenharmony_ci        # wrapper.
170a8e1175bSopenharmony_ci        call = '({})({})'.format(self._underlying_function_name(function),
171a8e1175bSopenharmony_ci                                 ', '.join(argument_names))
172a8e1175bSopenharmony_ci        if function.returns_void():
173a8e1175bSopenharmony_ci            out.write('    {};\n'.format(call))
174a8e1175bSopenharmony_ci        else:
175a8e1175bSopenharmony_ci            ret_name = self._return_variable_name(function)
176a8e1175bSopenharmony_ci            ret_decl = c_declare(function.return_type, ret_name, '')
177a8e1175bSopenharmony_ci            out.write('    {} = {};\n'.format(ret_decl, call))
178a8e1175bSopenharmony_ci
179a8e1175bSopenharmony_ci    def _write_function_return(self, out: typing_util.Writable,
180a8e1175bSopenharmony_ci                               function: FunctionInfo,
181a8e1175bSopenharmony_ci                               if_void: bool = False) -> None:
182a8e1175bSopenharmony_ci        """Write a return statement.
183a8e1175bSopenharmony_ci
184a8e1175bSopenharmony_ci        If the function returns void, only write a statement if if_void is true.
185a8e1175bSopenharmony_ci        """
186a8e1175bSopenharmony_ci        if function.returns_void():
187a8e1175bSopenharmony_ci            if if_void:
188a8e1175bSopenharmony_ci                out.write('    return;\n')
189a8e1175bSopenharmony_ci        else:
190a8e1175bSopenharmony_ci            ret_name = self._return_variable_name(function)
191a8e1175bSopenharmony_ci            out.write('    return {};\n'.format(ret_name))
192a8e1175bSopenharmony_ci
193a8e1175bSopenharmony_ci    def _write_function_body(self, out: typing_util.Writable,
194a8e1175bSopenharmony_ci                             function: FunctionInfo,
195a8e1175bSopenharmony_ci                             argument_names: List[str]) -> None:
196a8e1175bSopenharmony_ci        """Write the body of the wrapper code for the specified function.
197a8e1175bSopenharmony_ci        """
198a8e1175bSopenharmony_ci        self._write_function_call(out, function, argument_names)
199a8e1175bSopenharmony_ci        self._write_function_return(out, function)
200a8e1175bSopenharmony_ci
201a8e1175bSopenharmony_ci    def _skip_function(self, function: FunctionInfo) -> bool:
202a8e1175bSopenharmony_ci        """Whether to skip this function.
203a8e1175bSopenharmony_ci
204a8e1175bSopenharmony_ci        By default, static or inline functions are skipped.
205a8e1175bSopenharmony_ci        """
206a8e1175bSopenharmony_ci        if not self._SKIP_FUNCTION_WITH_QUALIFIERS.isdisjoint(function.qualifiers):
207a8e1175bSopenharmony_ci            return True
208a8e1175bSopenharmony_ci        return False
209a8e1175bSopenharmony_ci
210a8e1175bSopenharmony_ci    _FUNCTION_GUARDS = {
211a8e1175bSopenharmony_ci    } #type: Dict[str, str]
212a8e1175bSopenharmony_ci
213a8e1175bSopenharmony_ci    def _function_guard(self, function: FunctionInfo) -> Optional[str]:
214a8e1175bSopenharmony_ci        """A preprocessor condition for this function.
215a8e1175bSopenharmony_ci
216a8e1175bSopenharmony_ci        The wrapper will be guarded with `#if` on this condition, if not None.
217a8e1175bSopenharmony_ci        """
218a8e1175bSopenharmony_ci        return self._FUNCTION_GUARDS.get(function.name)
219a8e1175bSopenharmony_ci
220a8e1175bSopenharmony_ci    def _wrapper_info(self, function: FunctionInfo) -> Optional[WrapperInfo]:
221a8e1175bSopenharmony_ci        """Information about the wrapper for one function.
222a8e1175bSopenharmony_ci
223a8e1175bSopenharmony_ci        Return None if the function should be skipped.
224a8e1175bSopenharmony_ci        """
225a8e1175bSopenharmony_ci        if self._skip_function(function):
226a8e1175bSopenharmony_ci            return None
227a8e1175bSopenharmony_ci        argument_names = [self._argument_name(function.name, num, arg)
228a8e1175bSopenharmony_ci                          for num, arg in enumerate(function.arguments)]
229a8e1175bSopenharmony_ci        return WrapperInfo(
230a8e1175bSopenharmony_ci            argument_names=argument_names,
231a8e1175bSopenharmony_ci            guard=self._function_guard(function),
232a8e1175bSopenharmony_ci            wrapper_name=self._wrapper_function_name(function.name),
233a8e1175bSopenharmony_ci        )
234a8e1175bSopenharmony_ci
235a8e1175bSopenharmony_ci    def _write_function_prototype(self, out: typing_util.Writable,
236a8e1175bSopenharmony_ci                                  function: FunctionInfo,
237a8e1175bSopenharmony_ci                                  wrapper: WrapperInfo,
238a8e1175bSopenharmony_ci                                  header: bool) -> None:
239a8e1175bSopenharmony_ci        """Write the prototype of a wrapper function.
240a8e1175bSopenharmony_ci
241a8e1175bSopenharmony_ci        If header is true, write a function declaration, with a semicolon at
242a8e1175bSopenharmony_ci        the end. Otherwise just write the prototype, intended to be followed
243a8e1175bSopenharmony_ci        by the function's body.
244a8e1175bSopenharmony_ci        """
245a8e1175bSopenharmony_ci        declaration_start = self._wrapper_declaration_start(function,
246a8e1175bSopenharmony_ci                                                            wrapper.wrapper_name)
247a8e1175bSopenharmony_ci        arg_indent = '    '
248a8e1175bSopenharmony_ci        terminator = ';\n' if header else '\n'
249a8e1175bSopenharmony_ci        if function.arguments:
250a8e1175bSopenharmony_ci            out.write(declaration_start + '(\n')
251a8e1175bSopenharmony_ci            for num in range(len(function.arguments)):
252a8e1175bSopenharmony_ci                arg_def = self._wrapper_declaration_argument(
253a8e1175bSopenharmony_ci                    function.name,
254a8e1175bSopenharmony_ci                    num, wrapper.argument_names[num], function.arguments[num])
255a8e1175bSopenharmony_ci                arg_terminator = \
256a8e1175bSopenharmony_ci                    (')' + terminator if num == len(function.arguments) - 1 else
257a8e1175bSopenharmony_ci                     ',\n')
258a8e1175bSopenharmony_ci                out.write(arg_indent + arg_def + arg_terminator)
259a8e1175bSopenharmony_ci        else:
260a8e1175bSopenharmony_ci            out.write(declaration_start + '(void)' + terminator)
261a8e1175bSopenharmony_ci
262a8e1175bSopenharmony_ci    def _write_c_function(self, out: typing_util.Writable,
263a8e1175bSopenharmony_ci                          function: FunctionInfo) -> None:
264a8e1175bSopenharmony_ci        """Write wrapper code for one function.
265a8e1175bSopenharmony_ci
266a8e1175bSopenharmony_ci        Do nothing if the function is skipped.
267a8e1175bSopenharmony_ci        """
268a8e1175bSopenharmony_ci        wrapper = self._wrapper_info(function)
269a8e1175bSopenharmony_ci        if wrapper is None:
270a8e1175bSopenharmony_ci            return
271a8e1175bSopenharmony_ci        out.write("""
272a8e1175bSopenharmony_ci/* Wrapper for {} */
273a8e1175bSopenharmony_ci"""
274a8e1175bSopenharmony_ci                  .format(function.name))
275a8e1175bSopenharmony_ci        if wrapper.guard is not None:
276a8e1175bSopenharmony_ci            out.write('#if {}\n'.format(wrapper.guard))
277a8e1175bSopenharmony_ci        self._write_function_prototype(out, function, wrapper, False)
278a8e1175bSopenharmony_ci        out.write('{\n')
279a8e1175bSopenharmony_ci        self._write_function_body(out, function, wrapper.argument_names)
280a8e1175bSopenharmony_ci        out.write('}\n')
281a8e1175bSopenharmony_ci        if wrapper.guard is not None:
282a8e1175bSopenharmony_ci            out.write('#endif /* {} */\n'.format(wrapper.guard))
283a8e1175bSopenharmony_ci
284a8e1175bSopenharmony_ci    def _write_h_function_declaration(self, out: typing_util.Writable,
285a8e1175bSopenharmony_ci                                      function: FunctionInfo,
286a8e1175bSopenharmony_ci                                      wrapper: WrapperInfo) -> None:
287a8e1175bSopenharmony_ci        """Write the declaration of one wrapper function.
288a8e1175bSopenharmony_ci        """
289a8e1175bSopenharmony_ci        self._write_function_prototype(out, function, wrapper, True)
290a8e1175bSopenharmony_ci
291a8e1175bSopenharmony_ci    def _write_h_macro_definition(self, out: typing_util.Writable,
292a8e1175bSopenharmony_ci                                  function: FunctionInfo,
293a8e1175bSopenharmony_ci                                  wrapper: WrapperInfo) -> None:
294a8e1175bSopenharmony_ci        """Write the macro definition for one wrapper.
295a8e1175bSopenharmony_ci        """
296a8e1175bSopenharmony_ci        arg_list = ', '.join(wrapper.argument_names)
297a8e1175bSopenharmony_ci        out.write('#define {function_name}({args}) \\\n    {wrapper_name}({args})\n'
298a8e1175bSopenharmony_ci                  .format(function_name=function.name,
299a8e1175bSopenharmony_ci                          wrapper_name=wrapper.wrapper_name,
300a8e1175bSopenharmony_ci                          args=arg_list))
301a8e1175bSopenharmony_ci
302a8e1175bSopenharmony_ci    def _write_h_function(self, out: typing_util.Writable,
303a8e1175bSopenharmony_ci                          function: FunctionInfo) -> None:
304a8e1175bSopenharmony_ci        """Write the complete header content for one wrapper.
305a8e1175bSopenharmony_ci
306a8e1175bSopenharmony_ci        This is the declaration of the wrapper function, and the
307a8e1175bSopenharmony_ci        definition of a function-like macro that calls the wrapper function.
308a8e1175bSopenharmony_ci
309a8e1175bSopenharmony_ci        Do nothing if the function is skipped.
310a8e1175bSopenharmony_ci        """
311a8e1175bSopenharmony_ci        wrapper = self._wrapper_info(function)
312a8e1175bSopenharmony_ci        if wrapper is None:
313a8e1175bSopenharmony_ci            return
314a8e1175bSopenharmony_ci        out.write('\n')
315a8e1175bSopenharmony_ci        if wrapper.guard is not None:
316a8e1175bSopenharmony_ci            out.write('#if {}\n'.format(wrapper.guard))
317a8e1175bSopenharmony_ci        self._write_h_function_declaration(out, function, wrapper)
318a8e1175bSopenharmony_ci        self._write_h_macro_definition(out, function, wrapper)
319a8e1175bSopenharmony_ci        if wrapper.guard is not None:
320a8e1175bSopenharmony_ci            out.write('#endif /* {} */\n'.format(wrapper.guard))
321a8e1175bSopenharmony_ci
322a8e1175bSopenharmony_ci    def write_c_file(self, filename: str) -> None:
323a8e1175bSopenharmony_ci        """Output a whole C file containing function wrapper definitions."""
324a8e1175bSopenharmony_ci        with open(filename, 'w', encoding='utf-8') as out:
325a8e1175bSopenharmony_ci            self._write_prologue(out, False)
326a8e1175bSopenharmony_ci            for name in sorted(self.functions):
327a8e1175bSopenharmony_ci                self._write_c_function(out, self.functions[name])
328a8e1175bSopenharmony_ci            self._write_epilogue(out, False)
329a8e1175bSopenharmony_ci
330a8e1175bSopenharmony_ci    def _header_guard_from_file_name(self, filename: str) -> str:
331a8e1175bSopenharmony_ci        """Preprocessor symbol used as a guard against multiple inclusion."""
332a8e1175bSopenharmony_ci        # Heuristic to strip irrelevant leading directories
333a8e1175bSopenharmony_ci        filename = re.sub(r'.*include[\\/]', r'', filename)
334a8e1175bSopenharmony_ci        return re.sub(r'[^0-9A-Za-z]', r'_', filename, re.A).upper()
335a8e1175bSopenharmony_ci
336a8e1175bSopenharmony_ci    def write_h_file(self, filename: str) -> None:
337a8e1175bSopenharmony_ci        """Output a header file with function wrapper declarations and macro definitions."""
338a8e1175bSopenharmony_ci        self.header_guard = self._header_guard_from_file_name(filename)
339a8e1175bSopenharmony_ci        with open(filename, 'w', encoding='utf-8') as out:
340a8e1175bSopenharmony_ci            self._write_prologue(out, True)
341a8e1175bSopenharmony_ci            for name in sorted(self.functions):
342a8e1175bSopenharmony_ci                self._write_h_function(out, self.functions[name])
343a8e1175bSopenharmony_ci            self._write_epilogue(out, True)
344a8e1175bSopenharmony_ci
345a8e1175bSopenharmony_ci
346a8e1175bSopenharmony_ciclass UnknownTypeForPrintf(Exception):
347a8e1175bSopenharmony_ci    """Exception raised when attempting to generate code that logs a value of an unknown type."""
348a8e1175bSopenharmony_ci
349a8e1175bSopenharmony_ci    def __init__(self, typ: str) -> None:
350a8e1175bSopenharmony_ci        super().__init__("Unknown type for printf format generation: " + typ)
351a8e1175bSopenharmony_ci
352a8e1175bSopenharmony_ci
353a8e1175bSopenharmony_ciclass Logging(Base):
354a8e1175bSopenharmony_ci    """Generate wrapper functions that log the inputs and outputs."""
355a8e1175bSopenharmony_ci
356a8e1175bSopenharmony_ci    def __init__(self) -> None:
357a8e1175bSopenharmony_ci        """Construct a wrapper generator including logging of inputs and outputs.
358a8e1175bSopenharmony_ci
359a8e1175bSopenharmony_ci        Log to stdout by default. Call `set_stream` to change this.
360a8e1175bSopenharmony_ci        """
361a8e1175bSopenharmony_ci        super().__init__()
362a8e1175bSopenharmony_ci        self.stream = 'stdout'
363a8e1175bSopenharmony_ci
364a8e1175bSopenharmony_ci    def set_stream(self, stream: str) -> None:
365a8e1175bSopenharmony_ci        """Set the stdio stream to log to.
366a8e1175bSopenharmony_ci
367a8e1175bSopenharmony_ci        Call this method before calling `write_c_output` or `write_h_output`.
368a8e1175bSopenharmony_ci        """
369a8e1175bSopenharmony_ci        self.stream = stream
370a8e1175bSopenharmony_ci
371a8e1175bSopenharmony_ci    def _write_prologue(self, out: typing_util.Writable, header: bool) -> None:
372a8e1175bSopenharmony_ci        super()._write_prologue(out, header)
373a8e1175bSopenharmony_ci        if not header:
374a8e1175bSopenharmony_ci            out.write("""
375a8e1175bSopenharmony_ci#if defined(MBEDTLS_FS_IO) && defined(MBEDTLS_TEST_HOOKS)
376a8e1175bSopenharmony_ci#include <stdio.h>
377a8e1175bSopenharmony_ci#include <inttypes.h>
378a8e1175bSopenharmony_ci#include <mbedtls/debug.h> // for MBEDTLS_PRINTF_SIZET
379a8e1175bSopenharmony_ci#include <mbedtls/platform.h> // for mbedtls_fprintf
380a8e1175bSopenharmony_ci#endif /* defined(MBEDTLS_FS_IO) && defined(MBEDTLS_TEST_HOOKS) */
381a8e1175bSopenharmony_ci""")
382a8e1175bSopenharmony_ci
383a8e1175bSopenharmony_ci    _PRINTF_SIMPLE_FORMAT = {
384a8e1175bSopenharmony_ci        'int': '%d',
385a8e1175bSopenharmony_ci        'long': '%ld',
386a8e1175bSopenharmony_ci        'long long': '%lld',
387a8e1175bSopenharmony_ci        'size_t': '%"MBEDTLS_PRINTF_SIZET"',
388a8e1175bSopenharmony_ci        'unsigned': '0x%08x',
389a8e1175bSopenharmony_ci        'unsigned int': '0x%08x',
390a8e1175bSopenharmony_ci        'unsigned long': '0x%08lx',
391a8e1175bSopenharmony_ci        'unsigned long long': '0x%016llx',
392a8e1175bSopenharmony_ci    }
393a8e1175bSopenharmony_ci
394a8e1175bSopenharmony_ci    def _printf_simple_format(self, typ: str) -> Optional[str]:
395a8e1175bSopenharmony_ci        """Use this printf format for a value of typ.
396a8e1175bSopenharmony_ci
397a8e1175bSopenharmony_ci        Return None if values of typ need more complex handling.
398a8e1175bSopenharmony_ci        """
399a8e1175bSopenharmony_ci        return self._PRINTF_SIMPLE_FORMAT.get(typ)
400a8e1175bSopenharmony_ci
401a8e1175bSopenharmony_ci    _PRINTF_TYPE_CAST = {
402a8e1175bSopenharmony_ci        'int32_t': 'int',
403a8e1175bSopenharmony_ci        'uint32_t': 'unsigned',
404a8e1175bSopenharmony_ci        'uint64_t': 'unsigned long long',
405a8e1175bSopenharmony_ci    } #type: Dict[str, str]
406a8e1175bSopenharmony_ci
407a8e1175bSopenharmony_ci    def _printf_type_cast(self, typ: str) -> Optional[str]:
408a8e1175bSopenharmony_ci        """Cast values of typ to this type before passing them to printf.
409a8e1175bSopenharmony_ci
410a8e1175bSopenharmony_ci        Return None if values of the given type do not need a cast.
411a8e1175bSopenharmony_ci        """
412a8e1175bSopenharmony_ci        return self._PRINTF_TYPE_CAST.get(typ)
413a8e1175bSopenharmony_ci
414a8e1175bSopenharmony_ci    _POINTER_TYPE_RE = re.compile(r'\s*\*\Z')
415a8e1175bSopenharmony_ci
416a8e1175bSopenharmony_ci    def _printf_parameters(self, typ: str, var: str) -> Tuple[str, List[str]]:
417a8e1175bSopenharmony_ci        """The printf format and arguments for a value of type typ stored in var.
418a8e1175bSopenharmony_ci        """
419a8e1175bSopenharmony_ci        expr = var
420a8e1175bSopenharmony_ci        base_type = typ
421a8e1175bSopenharmony_ci        # For outputs via a pointer, get the value that has been written.
422a8e1175bSopenharmony_ci        # Note: we don't support pointers to pointers here.
423a8e1175bSopenharmony_ci        pointer_match = self._POINTER_TYPE_RE.search(base_type)
424a8e1175bSopenharmony_ci        if pointer_match:
425a8e1175bSopenharmony_ci            base_type = base_type[:pointer_match.start(0)]
426a8e1175bSopenharmony_ci            expr = '*({})'.format(expr)
427a8e1175bSopenharmony_ci        # Maybe cast the value to a standard type.
428a8e1175bSopenharmony_ci        cast_to = self._printf_type_cast(base_type)
429a8e1175bSopenharmony_ci        if cast_to is not None:
430a8e1175bSopenharmony_ci            expr = '({}) {}'.format(cast_to, expr)
431a8e1175bSopenharmony_ci            base_type = cast_to
432a8e1175bSopenharmony_ci        # Try standard types.
433a8e1175bSopenharmony_ci        fmt = self._printf_simple_format(base_type)
434a8e1175bSopenharmony_ci        if fmt is not None:
435a8e1175bSopenharmony_ci            return '{}={}'.format(var, fmt), [expr]
436a8e1175bSopenharmony_ci        raise UnknownTypeForPrintf(typ)
437a8e1175bSopenharmony_ci
438a8e1175bSopenharmony_ci    def _write_function_logging(self, out: typing_util.Writable,
439a8e1175bSopenharmony_ci                                function: FunctionInfo,
440a8e1175bSopenharmony_ci                                argument_names: List[str]) -> None:
441a8e1175bSopenharmony_ci        """Write code to log the function's inputs and outputs."""
442a8e1175bSopenharmony_ci        formats, values = '%s', ['"' + function.name + '"']
443a8e1175bSopenharmony_ci        for arg_info, arg_name in zip(function.arguments, argument_names):
444a8e1175bSopenharmony_ci            fmt, vals = self._printf_parameters(arg_info.type, arg_name)
445a8e1175bSopenharmony_ci            if fmt:
446a8e1175bSopenharmony_ci                formats += ' ' + fmt
447a8e1175bSopenharmony_ci                values += vals
448a8e1175bSopenharmony_ci        if not function.returns_void():
449a8e1175bSopenharmony_ci            ret_name = self._return_variable_name(function)
450a8e1175bSopenharmony_ci            fmt, vals = self._printf_parameters(function.return_type, ret_name)
451a8e1175bSopenharmony_ci            if fmt:
452a8e1175bSopenharmony_ci                formats += ' ' + fmt
453a8e1175bSopenharmony_ci                values += vals
454a8e1175bSopenharmony_ci        out.write("""\
455a8e1175bSopenharmony_ci#if defined(MBEDTLS_FS_IO) && defined(MBEDTLS_TEST_HOOKS)
456a8e1175bSopenharmony_ci    if ({stream}) {{
457a8e1175bSopenharmony_ci        mbedtls_fprintf({stream}, "{formats}\\n",
458a8e1175bSopenharmony_ci                        {values});
459a8e1175bSopenharmony_ci    }}
460a8e1175bSopenharmony_ci#endif /* defined(MBEDTLS_FS_IO) && defined(MBEDTLS_TEST_HOOKS) */
461a8e1175bSopenharmony_ci"""
462a8e1175bSopenharmony_ci                  .format(stream=self.stream,
463a8e1175bSopenharmony_ci                          formats=formats,
464a8e1175bSopenharmony_ci                          values=', '.join(values)))
465a8e1175bSopenharmony_ci
466a8e1175bSopenharmony_ci    def _write_function_body(self, out: typing_util.Writable,
467a8e1175bSopenharmony_ci                             function: FunctionInfo,
468a8e1175bSopenharmony_ci                             argument_names: List[str]) -> None:
469a8e1175bSopenharmony_ci        """Write the body of the wrapper code for the specified function.
470a8e1175bSopenharmony_ci        """
471a8e1175bSopenharmony_ci        self._write_function_call(out, function, argument_names)
472a8e1175bSopenharmony_ci        self._write_function_logging(out, function, argument_names)
473a8e1175bSopenharmony_ci        self._write_function_return(out, function)
474