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