1#!/usr/bin/env python3 2# coding=utf-8 3 4#=============================================================================== 5# Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED. 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# Description: Parse C headers and extract message definitions as ctypes objects. 18#=============================================================================== 19import pycparser 20import ctypes 21 22binary_operators = { 23 "&": lambda x, y: x & y, 24 "|": lambda x, y: x | y, 25 "*": lambda x, y: x * y, 26 "+": lambda x, y: x + y, 27 "-": lambda x, y: x - y, 28 "/": lambda x, y: x // y, 29 "<<": lambda x, y: x << y, 30 ">>": lambda x, y: x >> y, 31 "<=": lambda x, y: int(x <= y), 32 ">=": lambda x, y: int(x >= y), 33 "==": lambda x, y: int(x == y), 34 "&&": lambda x, y: int(bool(x and y)), 35 "||": lambda x, y: int(bool(x or y)), 36} 37 38unary_operators = { 39 "-": lambda x: -x, 40 "!": lambda x: 1 if x else 0, 41 "sizeof": lambda x: ctypes.sizeof(x), 42} 43 44fundamental_types = { 45 "": ctypes.c_int, 46 "_Bool": ctypes.c_bool, 47 "char unsigned": ctypes.c_ubyte, 48 "char": ctypes.c_byte, 49 "double": ctypes.c_double, 50 # 51 # 32bit arm and riscv compilers define "long double" type as 8 and 16 byte long respectively. 52 # ctypes.c_longdouble under 32bit windows, 32bit linux, 64bit windows, 64bit linux OS has length 8, 12, 8, 16 respectively. 53 # We cannot find a ctype mapping to generalize. 54 # Especially that 16 byte riscv "long double" type cannot be interpreted by any 32/64bit windows ctype. 55 # Fortunately we don't have any long doubles in messages and etypes. So disable this mapping for now. 56 # 57 # "double long": ctypes.c_longdouble, 58 "float": ctypes.c_float, 59 "long long unsigned": ctypes.c_ulonglong, 60 "long long": ctypes.c_longlong, 61 # 62 # 32bit arm and riscv compilers define "long" type as 4 byte long. 63 # Messages xml generation calls this script with 32bit python while kv generation uses 64bit python. 64 # We need to map "long" type to ctypes.c_int which is 4 byte across 32/64bit OSs 65 # instead of ctypes.c_long which is 8 byte on 64bit linux and 4 byte otherwise. 66 # This also applies to "unsigned long" type. 67 # 68 "long unsigned": ctypes.c_uint, 69 "long": ctypes.c_int, 70 "short unsigned": ctypes.c_ushort, 71 "short": ctypes.c_short, 72 "unsigned": ctypes.c_uint, 73 "void": None, 74} 75 76# These types will have a typedef but map to alternative ctypes values. 77override_typedefs = { 78 "osal_char": ctypes.c_char, 79} 80 81def get_typename(names): 82 specifiers = [] 83 for name in sorted(names): 84 if name in ["signed", "int"]: 85 continue 86 specifiers.append(name) 87 return " ".join(specifiers) 88 89def create_enum_type(enumname, enums): 90 smallest = min(enums.values()) 91 largest = max(enums.values()) 92 if smallest < 0: 93 if -128 <= smallest and largest <= 127: 94 base = ctypes.c_byte 95 elif -32768 <= smallest and largest <= 32767: 96 base = ctypes.c_short 97 else: 98 base = ctypes.c_int 99 else: 100 if largest < 256: 101 base = ctypes.c_ubyte 102 elif largest < 65536: 103 base = ctypes.c_ushort 104 else: 105 base = ctypes.c_uint 106 return type(enumname, (base,), {"members": enums}) 107 108 109class Visitor(pycparser.c_ast.NodeVisitor): 110 # Parse all typedefs and expand identifiers. 111 def __init__(self): 112 self.enums = {} 113 self.typedefs = dict(fundamental_types) 114 115 # The context in which the ID names exist. 116 self.id_context = self.enums 117 self.anon_struct_count = 1 118 self.anon_enum_count = 1 119 self.anon_union_count = 1 120 121 def generic_visit(self, node): 122 # Dump ast to see what is not implemented. 123 raise SystemExit("Unhandled ast element at %s: %s" % (node.coord, node)) 124 125 def visit_Decl(self, node): 126 return 127 128 def visit_FuncDef(self, node): 129 return 130 131 def visit_FuncDecl(self, node): 132 return 133 134 def visit_FileAST(self, node): 135 for c in node: 136 self.visit(c) 137 138 def visit_ID(self, node): 139 try: 140 return self.id_context[node.name] 141 except KeyError: 142 raise SystemExit("Failed to resolve identifier '%s' at %s" % (node.name, node.coord)) 143 144 def visit_Typename(self, node): 145 return self.visit(node.type) 146 147 def visit_TypeDecl(self, node): 148 return self.visit(node.type) 149 150 def visit_CompoundLiteral(self, node): 151 return self.visit(node.type) 152 153 def visit_PtrDecl(self, node): 154 return ctypes.POINTER(self.visit(node.type)) 155 156 def visit_Typedef(self, node): 157 if node.name in self.typedefs: 158 raise SystemExit("Duplicate typedef '%s' at %s" % (node.name, node.coord)) 159 if node.name in override_typedefs: 160 value = override_typedefs[node.name] 161 else: 162 value = self.visit(node.type) 163 self.typedefs[node.name] = value 164 165 def visit_ArrayRef(self, node): 166 # For accessing type of an array element 167 array = self.visit(node.name) 168 #index = self.visit(node.subscript) 169 return array._type_ 170 171 def visit_StructRef(self, node): 172 # This is needed to get access to types inside a struct. 173 struct = self.visit(node.name) 174 self.id_context = dict(struct._fields_) 175 try: 176 return self.visit(node.field) 177 finally: 178 self.id_context = self.enums 179 180 def visit_BinaryOp(self, node): 181 try: 182 op = binary_operators[node.op] 183 except KeyError: 184 raise SystemExit("Unhandled binary operator '%s' at %s" % (node.op, node.coord)) 185 leftval = self.visit(node.left) 186 rightval = self.visit(node.right) 187 return op(leftval, rightval) 188 189 def visit_UnaryOp(self, node): 190 value = self.visit(node.expr) 191 try: 192 op = unary_operators[node.op] 193 except KeyError: 194 raise SystemExit("Unhandled unary operator '%s' at %s" % (node.op, node.coord)) 195 return op(value) 196 197 def visit_Enum(self, node): 198 # Mapping of enum names to enum values from all parsed enums. 199 value = -1 200 enums = {} 201 for enum in node.values: 202 if enum.value is None: 203 value += 1 204 else: 205 value = self.visit(enum.value) 206 self.enums[enum.name] = enums[enum.name] = value 207 208 if node.name is None: 209 enumname = "enum_anon_%d" % self.anon_enum_count 210 self.anon_enum_count += 1 211 else: 212 enumname = "enum_%s" % str(node.name) 213 return create_enum_type(enumname, enums) 214 215 def visit_Constant(self, node): 216 if node.type not in ["int", "unsigned int"]: 217 raise SystemExit("Unhandled Constant node type '%s' at %s" % (node.type, node.coord)) 218 value = node.value.rstrip("LlUu") 219 if value.startswith(("0x", "0X")): 220 return int(value, 16) 221 return int(value) 222 223 def visit_IdentifierType(self, node): 224 name = get_typename(node.names) 225 try: 226 return self.typedefs[name] 227 except KeyError: 228 raise SystemExit("Invalid type specifier '%s' at %s" % (name, node.coord)) 229 230 def _handle_fields(self, decls): 231 fields = [] 232 # node.decls can be None when the struct declaration is outside the typedef. 233 if decls is not None: 234 field_count = 1 235 for decl in decls: 236 value = self.visit(decl.type) 237 if value is None: 238 # This is the void type - indicates an invalid message definition type. 239 return None 240 if decl.name is None: 241 fieldname = "unnamed_%d" % field_count 242 field_count += 1 243 else: 244 fieldname = decl.name 245 fields.append((fieldname, value)) 246 return fields 247 248 def visit_Struct(self, node): 249 fields = self._handle_fields(node.decls) 250 if fields is None: 251 return None 252 253 if node.name is None: 254 structname = "struct_anon_%d" % self.anon_struct_count 255 self.anon_struct_count += 1 256 else: 257 structname = "struct_%s" % str(node.name) 258 return type(structname, (ctypes.Structure,), {"_fields_": fields}) 259 260 def visit_Union(self, node): 261 fields = self._handle_fields(node.decls) 262 if fields is None: 263 return None 264 265 if node.name is None: 266 unionname = "union_anon_%d" % self.anon_union_count 267 self.anon_union_count += 1 268 else: 269 unionname = "union_%s" % str(node.name) 270 return type(unionname, (ctypes.Union,), {"_fields_": fields}) 271 272 def visit_ArrayDecl(self, node): 273 type = self.visit(node.type) 274 if node.dim is None: 275 # Flexible array members do not have a size. 276 # Return a pointer for compatibility with old ctypesgen based implementation. 277 return ctypes.POINTER(type) 278 length = self.visit(node.dim) 279 if length is None or length < 0: 280 raise SystemExit("Invalid array len %s at %s" % (length, node.dim.coord)) 281 if type is None: 282 raise SystemExit("Invalid array type %s at %s" % (type, node.type.coord)) 283 return type * length 284 285def message_enum_name(elemName): 286 if elemName.endswith("_t"): 287 return elemName[:-2].upper() 288 if elemName.endswith("_s"): 289 # NAS messages, already uppercase 290 return elemName[:-2] 291 if elemName.endswith("_STRUCT"): 292 return elemName[:-7] 293 if elemName.upper().find("LOG_MESSAGE") != -1: 294 return elemName.upper() +'_ID' 295 296def parse_preprocessed_headers(source, core): 297 # Public function that returns the required data for producing messagexml 298 try: 299 node = pycparser.parse_file(source) 300 except pycparser.plyparser.ParseError as e: 301 raise SystemExit("ERROR parsing msgdefs %s: %s" % (source, e)) 302 v = Visitor() 303 if core == 'acore': 304 v.anon_struct_count = 0x8001 305 v.anon_enum_count = 0x8001 306 v.anon_union_count = 0x8001 307 v.visit(node) 308 309 messages = [] 310 for structname, fields in sorted(v.typedefs.items()): 311 messageEnumName = message_enum_name(structname) 312 if messageEnumName is None: 313 # Not a valid message definition name. 314 continue 315 try: 316 messageId = v.enums[messageEnumName] 317 except KeyError: 318 # No associated message id, so not a message definition. 319 continue 320 if fields is None: 321 raise SystemExit("Message definition contains undefined type: %s" % structname) 322 messages.append((messageEnumName, structname, messageId, fields)) 323 return messages 324