• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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