1## @file
2# preprocess source file
3#
4#  Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
5#
6#  SPDX-License-Identifier: BSD-2-Clause-Patent
7#
8
9##
10# Import Modules
11#
12from __future__ import print_function
13from __future__ import absolute_import
14import sys
15import Common.LongFilePathOs as os
16import re
17from . import CodeFragmentCollector
18from . import FileProfile
19from CommonDataClass import DataClass
20from Common import EdkLogger
21from .EotToolError import *
22from . import EotGlobalData
23
24# Global Dicts
25IncludeFileListDict = {}
26IncludePathListDict = {}
27ComplexTypeDict = {}
28SUDict = {}
29
30## GetFuncDeclPattern() method
31#
32#  Get the pattern of function declaration
33#
34#  @return p:    the pattern of function declaration
35#
36def GetFuncDeclPattern():
37    p = re.compile(r'(EFIAPI|EFI_BOOT_SERVICE|EFI_RUNTIME_SERVICE)?\s*[_\w]+\s*\(.*\).*', re.DOTALL)
38    return p
39
40## GetArrayPattern() method
41#
42#  Get the pattern of array
43#
44#  @return p:    the pattern of array
45#
46def GetArrayPattern():
47    p = re.compile(r'[_\w]*\s*[\[.*\]]+')
48    return p
49
50## GetTypedefFuncPointerPattern() method
51#
52#  Get the pattern of function pointer
53#
54#  @return p:    the pattern of function pointer
55#
56def GetTypedefFuncPointerPattern():
57    p = re.compile('[_\w\s]*\([\w\s]*\*+\s*[_\w]+\s*\)\s*\(.*\)', re.DOTALL)
58    return p
59
60## GetDB() method
61#
62#  Get global database instance
63#
64#  @return EotGlobalData.gDb:    the global database instance
65#
66def GetDB():
67    return EotGlobalData.gDb
68
69## PrintErrorMsg() method
70#
71#  print error message
72#
73#  @param ErrorType: Type of error
74#  @param Msg: Error message
75#  @param TableName: table name of error found
76#  @param ItemId: id of item
77#
78def PrintErrorMsg(ErrorType, Msg, TableName, ItemId):
79    Msg = Msg.replace('\n', '').replace('\r', '')
80    MsgPartList = Msg.split()
81    Msg = ''
82    for Part in MsgPartList:
83        Msg += Part
84        Msg += ' '
85    GetDB().TblReport.Insert(ErrorType, OtherMsg = Msg, BelongsToTable = TableName, BelongsToItem = ItemId)
86
87## GetIdType() method
88#
89#  Find type of input string
90#
91#  @param Str: String to be parsed
92#
93#  @return Type: The type of the string
94#
95def GetIdType(Str):
96    Type = DataClass.MODEL_UNKNOWN
97    Str = Str.replace('#', '# ')
98    List = Str.split()
99    if List[1] == 'include':
100        Type = DataClass.MODEL_IDENTIFIER_INCLUDE
101    elif List[1] == 'define':
102        Type = DataClass.MODEL_IDENTIFIER_MACRO_DEFINE
103    elif List[1] == 'ifdef':
104        Type = DataClass.MODEL_IDENTIFIER_MACRO_IFDEF
105    elif List[1] == 'ifndef':
106        Type = DataClass.MODEL_IDENTIFIER_MACRO_IFNDEF
107    elif List[1] == 'endif':
108        Type = DataClass.MODEL_IDENTIFIER_MACRO_ENDIF
109    elif List[1] == 'pragma':
110        Type = DataClass.MODEL_IDENTIFIER_MACRO_PROGMA
111    else:
112        Type = DataClass.MODEL_UNKNOWN
113    return Type
114
115## GetIdentifierList() method
116#
117#  Get id of all files
118#
119#  @return IdList: The list of all id of files
120#
121def GetIdentifierList():
122    IdList = []
123
124    for pp in FileProfile.PPDirectiveList:
125        Type = GetIdType(pp.Content)
126        IdPP = DataClass.IdentifierClass(-1, '', '', '', pp.Content, Type, -1, -1, pp.StartPos[0], pp.StartPos[1], pp.EndPos[0], pp.EndPos[1])
127        IdList.append(IdPP)
128
129    for ae in FileProfile.AssignmentExpressionList:
130        IdAE = DataClass.IdentifierClass(-1, ae.Operator, '', ae.Name, ae.Value, DataClass.MODEL_IDENTIFIER_ASSIGNMENT_EXPRESSION, -1, -1, ae.StartPos[0], ae.StartPos[1], ae.EndPos[0], ae.EndPos[1])
131        IdList.append(IdAE)
132
133    FuncDeclPattern = GetFuncDeclPattern()
134    ArrayPattern = GetArrayPattern()
135    for var in FileProfile.VariableDeclarationList:
136        DeclText = var.Declarator.strip()
137        while DeclText.startswith('*'):
138            var.Modifier += '*'
139            DeclText = DeclText.lstrip('*').strip()
140        var.Declarator = DeclText
141        if FuncDeclPattern.match(var.Declarator):
142            DeclSplitList = var.Declarator.split('(')
143            FuncName = DeclSplitList[0]
144            FuncNamePartList = FuncName.split()
145            if len(FuncNamePartList) > 1:
146                FuncName = FuncNamePartList[-1]
147                Index = 0
148                while Index < len(FuncNamePartList) - 1:
149                    var.Modifier += ' ' + FuncNamePartList[Index]
150                    var.Declarator = var.Declarator.lstrip().lstrip(FuncNamePartList[Index])
151                    Index += 1
152            IdVar = DataClass.IdentifierClass(-1, var.Modifier, '', var.Declarator, '', DataClass.MODEL_IDENTIFIER_FUNCTION_DECLARATION, -1, -1, var.StartPos[0], var.StartPos[1], var.EndPos[0], var.EndPos[1])
153            IdList.append(IdVar)
154            continue
155
156        if var.Declarator.find('{') == -1:
157            for decl in var.Declarator.split(','):
158                DeclList = decl.split('=')
159                Name = DeclList[0].strip()
160                if ArrayPattern.match(Name):
161                    LSBPos = var.Declarator.find('[')
162                    var.Modifier += ' ' + Name[LSBPos:]
163                    Name = Name[0:LSBPos]
164
165                IdVar = DataClass.IdentifierClass(-1, var.Modifier, '', Name, (len(DeclList) > 1 and [DeclList[1]]or [''])[0], DataClass.MODEL_IDENTIFIER_VARIABLE, -1, -1, var.StartPos[0], var.StartPos[1], var.EndPos[0], var.EndPos[1])
166                IdList.append(IdVar)
167        else:
168            DeclList = var.Declarator.split('=')
169            Name = DeclList[0].strip()
170            if ArrayPattern.match(Name):
171                LSBPos = var.Declarator.find('[')
172                var.Modifier += ' ' + Name[LSBPos:]
173                Name = Name[0:LSBPos]
174            IdVar = DataClass.IdentifierClass(-1, var.Modifier, '', Name, (len(DeclList) > 1 and [DeclList[1]]or [''])[0], DataClass.MODEL_IDENTIFIER_VARIABLE, -1, -1, var.StartPos[0], var.StartPos[1], var.EndPos[0], var.EndPos[1])
175            IdList.append(IdVar)
176
177    for enum in FileProfile.EnumerationDefinitionList:
178        LBPos = enum.Content.find('{')
179        RBPos = enum.Content.find('}')
180        Name = enum.Content[4:LBPos].strip()
181        Value = enum.Content[LBPos+1:RBPos]
182        IdEnum = DataClass.IdentifierClass(-1, '', '', Name, Value, DataClass.MODEL_IDENTIFIER_ENUMERATE, -1, -1, enum.StartPos[0], enum.StartPos[1], enum.EndPos[0], enum.EndPos[1])
183        IdList.append(IdEnum)
184
185    for su in FileProfile.StructUnionDefinitionList:
186        Type = DataClass.MODEL_IDENTIFIER_STRUCTURE
187        SkipLen = 6
188        if su.Content.startswith('union'):
189            Type = DataClass.MODEL_IDENTIFIER_UNION
190            SkipLen = 5
191        LBPos = su.Content.find('{')
192        RBPos = su.Content.find('}')
193        if LBPos == -1 or RBPos == -1:
194            Name = su.Content[SkipLen:].strip()
195            Value = ''
196        else:
197            Name = su.Content[SkipLen:LBPos].strip()
198            Value = su.Content[LBPos+1:RBPos]
199        IdPE = DataClass.IdentifierClass(-1, '', '', Name, Value, Type, -1, -1, su.StartPos[0], su.StartPos[1], su.EndPos[0], su.EndPos[1])
200        IdList.append(IdPE)
201
202    TdFuncPointerPattern = GetTypedefFuncPointerPattern()
203    for td in FileProfile.TypedefDefinitionList:
204        Modifier = ''
205        Name = td.ToType
206        Value = td.FromType
207        if TdFuncPointerPattern.match(td.ToType):
208            Modifier = td.FromType
209            LBPos = td.ToType.find('(')
210            TmpStr = td.ToType[LBPos+1:].strip()
211            StarPos = TmpStr.find('*')
212            if StarPos != -1:
213                Modifier += ' ' + TmpStr[0:StarPos]
214            while TmpStr[StarPos] == '*':
215                Modifier += ' ' + '*'
216                StarPos += 1
217            TmpStr = TmpStr[StarPos:].strip()
218            RBPos = TmpStr.find(')')
219            Name = TmpStr[0:RBPos]
220            Value = 'FP' + TmpStr[RBPos + 1:]
221
222        IdTd = DataClass.IdentifierClass(-1, Modifier, '', Name, Value, DataClass.MODEL_IDENTIFIER_TYPEDEF, -1, -1, td.StartPos[0], td.StartPos[1], td.EndPos[0], td.EndPos[1])
223        IdList.append(IdTd)
224
225    for funcCall in FileProfile.FunctionCallingList:
226        IdFC = DataClass.IdentifierClass(-1, '', '', funcCall.FuncName, funcCall.ParamList, DataClass.MODEL_IDENTIFIER_FUNCTION_CALLING, -1, -1, funcCall.StartPos[0], funcCall.StartPos[1], funcCall.EndPos[0], funcCall.EndPos[1])
227        IdList.append(IdFC)
228    return IdList
229
230## GetParamList() method
231#
232#  Get a list of parameters
233#
234#  @param FuncDeclarator: Function declarator
235#  @param FuncNameLine: Line number of function name
236#  @param FuncNameOffset: Offset of function name
237#
238#  @return ParamIdList: A list of parameters
239#
240def GetParamList(FuncDeclarator, FuncNameLine = 0, FuncNameOffset = 0):
241    ParamIdList = []
242    DeclSplitList = FuncDeclarator.split('(')
243    if len(DeclSplitList) < 2:
244        return ParamIdList
245    FuncName = DeclSplitList[0]
246    ParamStr = DeclSplitList[1].rstrip(')')
247    LineSkipped = 0
248    OffsetSkipped = 0
249    Start = 0
250    while FuncName.find('\n', Start) != -1:
251        LineSkipped += 1
252        OffsetSkipped = 0
253        Start += FuncName.find('\n', Start)
254        Start += 1
255    OffsetSkipped += len(FuncName[Start:])
256    OffsetSkipped += 1 #skip '('
257    ParamBeginLine = FuncNameLine + LineSkipped
258    ParamBeginOffset = OffsetSkipped
259    for p in ParamStr.split(','):
260        ListP = p.split()
261        if len(ListP) == 0:
262            continue
263        ParamName = ListP[-1]
264        DeclText = ParamName.strip()
265        RightSpacePos = p.rfind(ParamName)
266        ParamModifier = p[0:RightSpacePos]
267        if ParamName == 'OPTIONAL':
268            if ParamModifier == '':
269                ParamModifier += ' ' + 'OPTIONAL'
270                DeclText = ''
271            else:
272                ParamName = ListP[-2]
273                DeclText = ParamName.strip()
274                RightSpacePos = p.rfind(ParamName)
275                ParamModifier = p[0:RightSpacePos]
276                ParamModifier += 'OPTIONAL'
277        while DeclText.startswith('*'):
278            ParamModifier += ' ' + '*'
279            DeclText = DeclText.lstrip('*').strip()
280        ParamName = DeclText
281
282        Start = 0
283        while p.find('\n', Start) != -1:
284            LineSkipped += 1
285            OffsetSkipped = 0
286            Start += p.find('\n', Start)
287            Start += 1
288        OffsetSkipped += len(p[Start:])
289
290        ParamEndLine = ParamBeginLine + LineSkipped
291        ParamEndOffset = OffsetSkipped
292        IdParam = DataClass.IdentifierClass(-1, ParamModifier, '', ParamName, '', DataClass.MODEL_IDENTIFIER_PARAMETER, -1, -1, ParamBeginLine, ParamBeginOffset, ParamEndLine, ParamEndOffset)
293        ParamIdList.append(IdParam)
294        ParamBeginLine = ParamEndLine
295        ParamBeginOffset = OffsetSkipped + 1 #skip ','
296
297    return ParamIdList
298
299## GetFunctionList()
300#
301#  Get a list of functions
302#
303#  @return FuncObjList: A list of function objects
304#
305def GetFunctionList():
306    FuncObjList = []
307    for FuncDef in FileProfile.FunctionDefinitionList:
308        ParamIdList = []
309        DeclText = FuncDef.Declarator.strip()
310        while DeclText.startswith('*'):
311            FuncDef.Modifier += '*'
312            DeclText = DeclText.lstrip('*').strip()
313
314        FuncDef.Declarator = FuncDef.Declarator.lstrip('*')
315        DeclSplitList = FuncDef.Declarator.split('(')
316        if len(DeclSplitList) < 2:
317            continue
318
319        FuncName = DeclSplitList[0]
320        FuncNamePartList = FuncName.split()
321        if len(FuncNamePartList) > 1:
322            FuncName = FuncNamePartList[-1]
323            Index = 0
324            while Index < len(FuncNamePartList) - 1:
325                FuncDef.Modifier += ' ' + FuncNamePartList[Index]
326                Index += 1
327
328        FuncObj = DataClass.FunctionClass(-1, FuncDef.Declarator, FuncDef.Modifier, FuncName.strip(), '', FuncDef.StartPos[0], FuncDef.StartPos[1], FuncDef.EndPos[0], FuncDef.EndPos[1], FuncDef.LeftBracePos[0], FuncDef.LeftBracePos[1], -1, ParamIdList, [])
329        FuncObjList.append(FuncObj)
330
331    return FuncObjList
332
333## CreateCCodeDB() method
334#
335#  Create database for all c code
336#
337#  @param FileNameList: A list of all c code file names
338#
339def CreateCCodeDB(FileNameList):
340    FileObjList = []
341    ParseErrorFileList = []
342    ParsedFiles = {}
343    for FullName in FileNameList:
344        if os.path.splitext(FullName)[1] in ('.h', '.c'):
345            if FullName.lower() in ParsedFiles:
346                continue
347            ParsedFiles[FullName.lower()] = 1
348            EdkLogger.info("Parsing " + FullName)
349            model = FullName.endswith('c') and DataClass.MODEL_FILE_C or DataClass.MODEL_FILE_H
350            collector = CodeFragmentCollector.CodeFragmentCollector(FullName)
351            try:
352                collector.ParseFile()
353            except:
354                ParseErrorFileList.append(FullName)
355            BaseName = os.path.basename(FullName)
356            DirName = os.path.dirname(FullName)
357            Ext = os.path.splitext(BaseName)[1].lstrip('.')
358            ModifiedTime = os.path.getmtime(FullName)
359            FileObj = DataClass.FileClass(-1, BaseName, Ext, DirName, FullName, model, ModifiedTime, GetFunctionList(), GetIdentifierList(), [])
360            FileObjList.append(FileObj)
361            collector.CleanFileProfileBuffer()
362
363    if len(ParseErrorFileList) > 0:
364        EdkLogger.info("Found unrecoverable error during parsing:\n\t%s\n" % "\n\t".join(ParseErrorFileList))
365
366    Db = EotGlobalData.gDb
367    for file in FileObjList:
368        Db.InsertOneFile(file)
369
370    Db.UpdateIdentifierBelongsToFunction()
371
372##
373#
374# This acts like the main() function for the script, unless it is 'import'ed into another
375# script.
376#
377if __name__ == '__main__':
378
379    EdkLogger.Initialize()
380    EdkLogger.SetLevel(EdkLogger.QUIET)
381    CollectSourceCodeDataIntoDB(sys.argv[1])
382
383    print('Done!')
384