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