• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  *
4  * HDF is dual licensed: you can use it either under the terms of
5  * the GPL, or the BSD license, at your option.
6  * See the LICENSE file in the root of this repository for complete details.
7  */
8 
9 #include "preprocessor/preprocessor.h"
10 
11 #include <algorithm>
12 #include <queue>
13 
14 #include "util/common.h"
15 #include "util/file.h"
16 #include "util/logger.h"
17 #include "util/options.h"
18 #include "util/string_builder.h"
19 
20 namespace OHOS {
21 namespace HDI {
Dump() const22 std::string FileDetail::Dump() const
23 {
24     StringBuilder sb;
25     sb.AppendFormat("filePath:%s\n", filePath_.c_str());
26     sb.AppendFormat("fileName:%s\n", fileName_.c_str());
27     sb.AppendFormat("packageName:%s\n", packageName_.c_str());
28     if (imports_.size() == 0) {
29         sb.Append("import:[]\n");
30     } else {
31         sb.Append("import:[\n");
32         for (const auto &importStr : imports_) {
33             sb.AppendFormat("%s,\n", importStr.c_str());
34         }
35         sb.Append("]\n");
36     }
37     return sb.ToString();
38 }
39 
Preprocess(std::vector<std::string> & compileSourceFiles)40 bool Preprocessor::Preprocess(std::vector<std::string> &compileSourceFiles)
41 {
42     std::vector<std::string> sourceFiles = Options::GetInstance().GetSourceFiles();
43 
44     // check all path of idl files
45     if (!CheckAllFilesPath(sourceFiles)) {
46         return false;
47     }
48 
49     // analyse impirt infomation of all idl file
50     FileDetailMap allFileDetails;
51     if (!AnalyseImportInfo(sourceFiles, allFileDetails)) {
52         return false;
53     }
54 
55     // calculate the order of idl files to compile by counter-topological sorting
56     if (!CheckCircularReference(allFileDetails, compileSourceFiles)) {
57         return false;
58     }
59     return true;
60 }
61 
CheckAllFilesPath(const std::vector<std::string> & sourceFiles)62 bool Preprocessor::CheckAllFilesPath(const std::vector<std::string> &sourceFiles)
63 {
64     if (sourceFiles.empty()) {
65         Logger::E(TAG, "no source files");
66         return false;
67     }
68 
69     bool ret = true;
70     for (const auto &filePath : sourceFiles) {
71         if (!File::CheckValid(filePath)) {
72             Logger::E(TAG, "invailed file path '%s'.", filePath.c_str());
73             ret = false;
74         }
75     }
76 
77     return ret;
78 }
79 
AnalyseImportInfo(const std::vector<std::string> & sourceFiles,FileDetailMap & allFileDetails)80 bool Preprocessor::AnalyseImportInfo(const std::vector<std::string> &sourceFiles, FileDetailMap &allFileDetails)
81 {
82     if (sourceFiles.size() == 1) {
83         FileDetail info;
84         if (!ParseFileDetail(sourceFiles[0], info)) {
85             return false;
86         }
87         allFileDetails[info.GetFullName()] = info;
88         if (!LoadOtherIdlFiles(info, allFileDetails)) {
89             return false;
90         }
91     } else {
92         for (const auto &sourceFile : sourceFiles) {
93             FileDetail info;
94             if (!ParseFileDetail(sourceFile, info)) {
95                 return false;
96             }
97             allFileDetails[info.GetFullName()] = info;
98         }
99     }
100     return true;
101 }
102 
ParseFileDetail(const std::string & sourceFile,FileDetail & info)103 bool Preprocessor::ParseFileDetail(const std::string &sourceFile, FileDetail &info)
104 {
105     Lexer lexer;
106     if (!lexer.Reset(sourceFile)) {
107         Logger::E(TAG, "failed to open file '%s'.", sourceFile.c_str());
108         return false;
109     }
110 
111     info.filePath_ = lexer.GetFilePath();
112     size_t startIndex = info.filePath_.rfind(SEPARATOR);
113     size_t endIndex = info.filePath_.rfind(".idl");
114     if (startIndex == std::string::npos || endIndex == std::string::npos || (startIndex >= endIndex)) {
115         Logger::E(TAG, "failed to get file name from '%s'.", info.filePath_.c_str());
116         return false;
117     }
118     info.fileName_ = StringHelper::SubStr(info.filePath_, startIndex + 1, endIndex);
119 
120     if (!ParsePackage(lexer, info)) {
121         return false;
122     }
123 
124     if (!ParseImports(lexer, info)) {
125         return false;
126     }
127     return true;
128 }
129 
ParsePackage(Lexer & lexer,FileDetail & info)130 bool Preprocessor::ParsePackage(Lexer &lexer, FileDetail &info)
131 {
132     Token token = lexer.PeekToken();
133     if (token.kind_ != TokenType::PACKAGE) {
134         Logger::E(TAG, "%s: expected 'package' before '%s' token", LocInfo(token).c_str(), token.value_.c_str());
135         return false;
136     }
137     lexer.GetToken();
138 
139     token = lexer.PeekToken();
140     if (token.kind_ != TokenType::ID) {
141         Logger::E(TAG, "%s: expected package name before '%s' token", LocInfo(token).c_str(), token.value_.c_str());
142         return false;
143     }
144     info.packageName_ = token.value_;
145     lexer.GetToken();
146 
147     token = lexer.PeekToken();
148     if (token.kind_ != TokenType::SEMICOLON) {
149         Logger::E(TAG, "%s:expected ';' before '%s' token", LocInfo(token).c_str(), token.value_.c_str());
150         return false;
151     }
152     lexer.GetToken();
153     return true;
154 }
155 
ParseImports(Lexer & lexer,FileDetail & info)156 bool Preprocessor::ParseImports(Lexer &lexer, FileDetail &info)
157 {
158     Token token = lexer.PeekToken();
159     while (token.kind_ != TokenType::END_OF_FILE) {
160         if (token.kind_ != TokenType::IMPORT) {
161             lexer.GetToken();
162             token = lexer.PeekToken();
163             continue;
164         }
165 
166         lexer.GetToken();
167         token = lexer.PeekToken();
168         if (token.kind_ != TokenType::ID) {
169             Logger::E(TAG, "%s: expected import name before '%s' token", LocInfo(token).c_str(), token.value_.c_str());
170             return false;
171         }
172 
173         if (!File::CheckValid(Options::GetInstance().GetImportFilePath(token.value_))) {
174             Logger::E(TAG, "%s: import invalid package '%s'", LocInfo(token).c_str(), token.value_.c_str());
175             return false;
176         }
177 
178         info.imports_.emplace(token.value_);
179         lexer.GetToken();
180 
181         token = lexer.PeekToken();
182         if (token.kind_ != TokenType::SEMICOLON) {
183             Logger::E(TAG, "%s:expected ';' before '%s' token", LocInfo(token).c_str(), token.value_.c_str());
184             return false;
185         }
186         lexer.GetToken();
187 
188         token = lexer.PeekToken();
189     }
190     return true;
191 }
192 
LoadOtherIdlFiles(const FileDetail & ownerFileDetail,FileDetailMap & allFileDetails)193 bool Preprocessor::LoadOtherIdlFiles(const FileDetail &ownerFileDetail, FileDetailMap &allFileDetails)
194 {
195     for (const auto &importName : ownerFileDetail.imports_) {
196         if (allFileDetails.find(importName) != allFileDetails.end()) {
197             continue;
198         }
199 
200         std::string otherFilePath = Options::GetInstance().GetImportFilePath(importName);
201         FileDetail otherFileDetail;
202         if (!ParseFileDetail(otherFilePath, otherFileDetail)) {
203             return false;
204         }
205 
206         allFileDetails[otherFileDetail.GetFullName()] = otherFileDetail;
207         if (!LoadOtherIdlFiles(otherFileDetail, allFileDetails)) {
208             Logger::E(TAG, "failed to load file detail by import '%s'", otherFileDetail.filePath_.c_str());
209             return false;
210         }
211     }
212     return true;
213 }
214 
CheckCircularReference(FileDetailMap & allFileDetails,std::vector<std::string> & compileSourceFiles)215 bool Preprocessor::CheckCircularReference(FileDetailMap &allFileDetails, std::vector<std::string> &compileSourceFiles)
216 {
217     std::queue<FileDetail> fileQueue;
218     for (const auto &filePair : allFileDetails) {
219         const FileDetail &file = filePair.second;
220         if (file.imports_.size() == 0) {
221             fileQueue.push(file);
222         }
223     }
224 
225     compileSourceFiles.clear();
226     while (!fileQueue.empty()) {
227         FileDetail curFile = fileQueue.front();
228         fileQueue.pop();
229         compileSourceFiles.push_back(curFile.filePath_);
230 
231         for (auto &filePair : allFileDetails) {
232             FileDetail &otherFile = filePair.second;
233             if (otherFile.imports_.empty()) {
234                 continue;
235             }
236 
237             auto position = otherFile.imports_.find(curFile.GetFullName());
238             if (position != otherFile.imports_.end()) {
239                 otherFile.imports_.erase(position);
240             }
241 
242             if (otherFile.imports_.size() == 0) {
243                 fileQueue.push(otherFile);
244             }
245         }
246     }
247 
248     if (compileSourceFiles.size() == allFileDetails.size()) {
249         return true;
250     }
251 
252     PrintCyclefInfo(allFileDetails);
253     return false;
254 }
255 
PrintCyclefInfo(FileDetailMap & allFileDetails)256 void Preprocessor::PrintCyclefInfo(FileDetailMap &allFileDetails)
257 {
258     for (FileDetailMap::iterator it = allFileDetails.begin(); it != allFileDetails.end();) {
259         if (it->second.imports_.size() == 0) {
260             it = allFileDetails.erase(it);
261         } else {
262             ++it;
263         }
264     }
265 
266     for (const auto &filePair : allFileDetails) {
267         std::vector<std::string> traceNodes;
268         FindCycle(filePair.second.GetFullName(), allFileDetails, traceNodes);
269     }
270 }
271 
FindCycle(const std::string & curNode,FileDetailMap & allFiles,std::vector<std::string> & trace)272 void Preprocessor::FindCycle(const std::string &curNode, FileDetailMap &allFiles, std::vector<std::string> &trace)
273 {
274     auto iter = std::find_if(trace.begin(), trace.end(), [curNode](const std::string &name) {
275         return name == curNode;
276     });
277     if (iter != trace.end()) {
278         if (iter == trace.begin()) {
279             // print circular reference infomation
280             StringBuilder sb;
281             for (const auto &nodeName : trace) {
282                 sb.AppendFormat("%s -> ", nodeName.c_str());
283             }
284             sb.AppendFormat("%s", curNode.c_str());
285             Logger::E(TAG, "error: there are circular reference:\n%s", sb.ToString().c_str());
286         }
287         return;
288     }
289 
290     trace.push_back(curNode);
291     for (const auto &importFileName : allFiles[curNode].imports_) {
292         FindCycle(importFileName, allFiles, trace);
293     }
294 
295     trace.pop_back();
296 }
297 } // namespace HDI
298 } // namespace OHOS