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