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