• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 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<FileDetail> & fileDetails)40 bool Preprocessor::Preprocess(std::vector<FileDetail> &fileDetails)
41 {
42     std::set<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, fileDetails)) {
57         return false;
58     }
59 
60     return true;
61 }
62 
UnitPreprocess(FileDetailMap & fileDetails)63 bool Preprocessor::UnitPreprocess(FileDetailMap &fileDetails)
64 {
65     std::set<std::string> sourceFiles = Options::GetInstance().GetSourceFiles();
66     // check all path of idl files
67     if (!CheckAllFilesPath(sourceFiles)) {
68         return false;
69     }
70 
71     for (const auto &sourceFile : sourceFiles) {
72         FileDetail info;
73         if (!ParseFileDetail(sourceFile, info)) {
74             return false;
75         }
76         fileDetails[info.GetFullName()] = info;
77     }
78 
79     return true;
80 }
81 
CheckAllFilesPath(const std::set<std::string> & sourceFiles)82 bool Preprocessor::CheckAllFilesPath(const std::set<std::string> &sourceFiles)
83 {
84     if (sourceFiles.empty()) {
85         Logger::E(TAG, "no source files");
86         return false;
87     }
88 
89     bool ret = true;
90     for (const auto &filePath : sourceFiles) {
91         if (!File::CheckValid(filePath)) {
92             Logger::E(TAG, "invailed file path '%s'.", filePath.c_str());
93             ret = false;
94         }
95     }
96 
97     return ret;
98 }
99 
AnalyseImportInfo(std::set<std::string> sourceFiles,FileDetailMap & allFileDetails)100 bool Preprocessor::AnalyseImportInfo(std::set<std::string> sourceFiles, FileDetailMap &allFileDetails)
101 {
102     std::set<std::string> processSource(sourceFiles);
103     while (!processSource.empty()) {
104         auto fileIter = processSource.begin();
105         FileDetail info;
106         if (!ParseFileDetail(*fileIter, info)) {
107             return false;
108         }
109 
110         processSource.erase(fileIter);
111         allFileDetails[info.GetFullName()] = info;
112         if (!LoadOtherIdlFiles(info, allFileDetails, processSource)) {
113             Logger::E(TAG, "failed to load other by %s", info.GetFullName().c_str());
114             return false;
115         }
116     }
117 
118     return true;
119 }
120 
ParseFileDetail(const std::string & sourceFile,FileDetail & info)121 bool Preprocessor::ParseFileDetail(const std::string &sourceFile, FileDetail &info)
122 {
123     Lexer lexer;
124     if (!lexer.Reset(sourceFile)) {
125         Logger::E(TAG, "failed to open file '%s'.", sourceFile.c_str());
126         return false;
127     }
128 
129     info.filePath_ = lexer.GetFilePath();
130     size_t startIndex = info.filePath_.rfind(SEPARATOR);
131     size_t endIndex = info.filePath_.rfind(".idl");
132     if (startIndex == std::string::npos || endIndex == std::string::npos || (startIndex >= endIndex)) {
133         Logger::E(TAG, "failed to get file name from '%s'.", info.filePath_.c_str());
134         return false;
135     }
136     info.fileName_ = StringHelper::SubStr(info.filePath_, startIndex + 1, endIndex);
137 
138     if (!ParsePackage(lexer, info)) {
139         return false;
140     }
141 
142     if (!ParseImports(lexer, info)) {
143         return false;
144     }
145     return true;
146 }
147 
ParsePackage(Lexer & lexer,FileDetail & info)148 bool Preprocessor::ParsePackage(Lexer &lexer, FileDetail &info)
149 {
150     Token token = lexer.PeekToken();
151     if (token.kind != TokenType::PACKAGE) {
152         Logger::E(TAG, "%s: expected 'package' before '%s' token", LocInfo(token).c_str(), token.value.c_str());
153         return false;
154     }
155     lexer.GetToken();
156 
157     token = lexer.PeekToken();
158     if (token.kind != TokenType::ID) {
159         Logger::E(TAG, "%s: expected package name before '%s' token", LocInfo(token).c_str(), token.value.c_str());
160         return false;
161     }
162 
163     if (!CheckPackageName(info.filePath_, token.value)) {
164         Logger::E(TAG, "%s:package name '%s' does not match file path '%s'", LocInfo(token).c_str(),
165             token.value.c_str(), info.filePath_.c_str());
166         return false;
167     }
168     info.packageName_ = token.value;
169     lexer.GetToken();
170 
171     token = lexer.PeekToken();
172     if (token.kind != TokenType::SEMICOLON) {
173         Logger::E(TAG, "%s:expected ';' before '%s' token", LocInfo(token).c_str(), token.value.c_str());
174         return false;
175     }
176     lexer.GetToken();
177     return true;
178 }
179 
ParseImports(Lexer & lexer,FileDetail & info)180 bool Preprocessor::ParseImports(Lexer &lexer, FileDetail &info)
181 {
182     Token token = lexer.PeekToken();
183     while (token.kind != TokenType::END_OF_FILE) {
184         if (token.kind != TokenType::IMPORT) {
185             lexer.GetToken();
186             token = lexer.PeekToken();
187             continue;
188         }
189 
190         lexer.GetToken();
191         token = lexer.PeekToken();
192         if (token.kind != TokenType::ID) {
193             Logger::E(TAG, "%s: expected import name before '%s' token", LocInfo(token).c_str(), token.value.c_str());
194             return false;
195         }
196 
197         if (!File::CheckValid(Options::GetInstance().GetImportFilePath(token.value))) {
198             Logger::E(TAG, "%s: import invalid package '%s'", LocInfo(token).c_str(), token.value.c_str());
199             return false;
200         }
201 
202         info.imports_.emplace(token.value);
203         lexer.GetToken();
204 
205         token = lexer.PeekToken();
206         if (token.kind != TokenType::SEMICOLON) {
207             Logger::E(TAG, "%s:expected ';' before '%s' token", LocInfo(token).c_str(), token.value.c_str());
208             return false;
209         }
210         lexer.GetToken();
211 
212         token = lexer.PeekToken();
213     }
214     return true;
215 }
216 
LoadOtherIdlFiles(const FileDetail & ownerFileDetail,FileDetailMap & allFileDetails,std::set<std::string> & sourceFiles)217 bool Preprocessor::LoadOtherIdlFiles(
218     const FileDetail &ownerFileDetail, FileDetailMap &allFileDetails, std::set<std::string> &sourceFiles)
219 {
220     for (const auto &importName : ownerFileDetail.imports_) {
221         if (allFileDetails.find(importName) != allFileDetails.end()) {
222             continue;
223         }
224 
225         std::string otherFilePath = Options::GetInstance().GetImportFilePath(importName);
226         if (otherFilePath.empty()) {
227             Logger::E(TAG, "importName:%s, is failed", importName.c_str());
228             return false;
229         }
230 
231         sourceFiles.insert(otherFilePath);
232     }
233     return true;
234 }
235 
CheckCircularReference(const FileDetailMap & allFileDetails,std::vector<FileDetail> & compileSourceFiles)236 bool Preprocessor::CheckCircularReference(const FileDetailMap &allFileDetails,
237     std::vector<FileDetail> &compileSourceFiles)
238 {
239     FileDetailMap allFileDetailsTemp = allFileDetails;
240     std::queue<FileDetail> fileQueue;
241     for (const auto &filePair : allFileDetailsTemp) {
242         const FileDetail &file = filePair.second;
243         if (file.imports_.size() == 0) {
244             fileQueue.push(file);
245         }
246     }
247 
248     compileSourceFiles.clear();
249     while (!fileQueue.empty()) {
250         FileDetail curFile = fileQueue.front();
251         fileQueue.pop();
252         compileSourceFiles.push_back(allFileDetails.at(curFile.GetFullName()));
253 
254         for (auto &filePair : allFileDetailsTemp) {
255             FileDetail &otherFile = filePair.second;
256             if (otherFile.imports_.empty()) {
257                 continue;
258             }
259 
260             auto position = otherFile.imports_.find(curFile.GetFullName());
261             if (position != otherFile.imports_.end()) {
262                 otherFile.imports_.erase(position);
263             }
264 
265             if (otherFile.imports_.size() == 0) {
266                 fileQueue.push(otherFile);
267             }
268         }
269     }
270 
271     if (compileSourceFiles.size() == allFileDetailsTemp.size()) {
272         return true;
273     }
274 
275     PrintCyclefInfo(allFileDetailsTemp);
276     return false;
277 }
278 
PrintCyclefInfo(FileDetailMap & allFileDetails)279 void Preprocessor::PrintCyclefInfo(FileDetailMap &allFileDetails)
280 {
281     for (FileDetailMap::iterator it = allFileDetails.begin(); it != allFileDetails.end();) {
282         if (it->second.imports_.size() == 0) {
283             it = allFileDetails.erase(it);
284         } else {
285             ++it;
286         }
287     }
288 
289     for (const auto &filePair : allFileDetails) {
290         std::vector<std::string> traceNodes;
291         FindCycle(filePair.second.GetFullName(), allFileDetails, traceNodes);
292     }
293 }
294 
FindCycle(const std::string & curNode,FileDetailMap & allFiles,std::vector<std::string> & trace)295 void Preprocessor::FindCycle(const std::string &curNode, FileDetailMap &allFiles, std::vector<std::string> &trace)
296 {
297     auto iter = std::find_if(trace.begin(), trace.end(), [curNode](const std::string &name) {
298         return name == curNode;
299     });
300     if (iter != trace.end()) {
301         if (iter == trace.begin()) {
302             // print circular reference infomation
303             StringBuilder sb;
304             for (const auto &nodeName : trace) {
305                 sb.AppendFormat("%s -> ", nodeName.c_str());
306             }
307             sb.AppendFormat("%s", curNode.c_str());
308             Logger::E(TAG, "error: there are circular reference:\n%s", sb.ToString().c_str());
309         }
310         return;
311     }
312 
313     trace.push_back(curNode);
314     for (const auto &importFileName : allFiles[curNode].imports_) {
315         FindCycle(importFileName, allFiles, trace);
316     }
317 
318     trace.pop_back();
319 }
320 
321 /*
322  * filePath: ./ohos/interface/foo/v1_0/IFoo.idl
323  * package ohos.hdi.foo.v1_0;
324  */
CheckPackageName(const std::string & filePath,const std::string & packageName)325 bool Preprocessor::CheckPackageName(const std::string &filePath, const std::string &packageName)
326 {
327     std::string pkgToPath = Options::GetInstance().GetPackagePath(packageName);
328 
329     size_t index = filePath.rfind(SEPARATOR);
330     if (index == std::string::npos) {
331         return false;
332     }
333 
334     std::string parentDir = filePath.substr(0, index);
335     return parentDir == pkgToPath;
336 }
337 } // namespace HDI
338 } // namespace OHOS