1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "preprocessor/preprocessor.h"
17
18 #include <algorithm>
19 #include <queue>
20
21 #include "util/common.h"
22 #include "util/file.h"
23 #include "util/logger.h"
24 #include "util/options.h"
25 #include "util/string_builder.h"
26
27 namespace OHOS {
28 namespace Idl {
Dump() const29 std::string FileDetail::Dump() const
30 {
31 StringBuilder sb;
32 sb.AppendFormat("filePath:%s\n", filePath_.c_str());
33 sb.AppendFormat("fileName:%s\n", fileName_.c_str());
34 sb.AppendFormat("packageName:%s\n", packageName_.c_str());
35 if (imports_.empty()) {
36 sb.Append("import:[]\n");
37 } else {
38 sb.Append("import:[\n");
39 for (const auto &importStr : imports_) {
40 sb.AppendFormat("%s,\n", importStr.c_str());
41 }
42 sb.Append("]\n");
43 }
44 return sb.ToString();
45 }
46
Preprocess(std::vector<FileDetail> & fileDetails)47 bool Preprocessor::Preprocess(std::vector<FileDetail> &fileDetails)
48 {
49 std::set<std::string> sourceFiles = Options::GetInstance().GetSourceFiles();
50
51 // check all path of idl files
52 if (!CheckAllFilesPath(sourceFiles)) {
53 return false;
54 }
55
56 // analyse import infomation of all idl file
57 FileDetailMap allFileDetails;
58 if (!AnalyseImportInfo(sourceFiles, allFileDetails)) {
59 return false;
60 }
61
62 // calculate the order of idl files to compile by counter-topological sorting
63 if (!CheckCircularReference(allFileDetails, fileDetails)) {
64 return false;
65 }
66
67 return true;
68 }
69
UnitPreprocess(FileDetailMap & fileDetails)70 bool Preprocessor::UnitPreprocess(FileDetailMap &fileDetails)
71 {
72 std::set<std::string> sourceFiles = Options::GetInstance().GetSourceFiles();
73 // check all path of idl files
74 if (!CheckAllFilesPath(sourceFiles)) {
75 return false;
76 }
77
78 for (const auto &sourceFile : sourceFiles) {
79 FileDetail info;
80 if (!ParseFileDetail(sourceFile, info)) {
81 return false;
82 }
83 fileDetails[info.GetFullName()] = info;
84 }
85
86 return true;
87 }
88
CheckAllFilesPath(const std::set<std::string> & sourceFiles)89 bool Preprocessor::CheckAllFilesPath(const std::set<std::string> &sourceFiles)
90 {
91 if (sourceFiles.empty()) {
92 Logger::E(TAG, "no source files");
93 return false;
94 }
95
96 bool ret = true;
97 for (const auto &filePath : sourceFiles) {
98 if (!File::CheckValid(filePath)) {
99 Logger::E(TAG, "invailed file path '%s'.", filePath.c_str());
100 ret = false;
101 }
102 }
103
104 return ret;
105 }
106
AnalyseImportInfo(std::set<std::string> sourceFiles,FileDetailMap & allFileDetails)107 bool Preprocessor::AnalyseImportInfo(std::set<std::string> sourceFiles, FileDetailMap &allFileDetails)
108 {
109 std::set<std::string> processSource(sourceFiles);
110 while (!processSource.empty()) {
111 auto fileIter = processSource.begin();
112 FileDetail info;
113 if (!ParseFileDetail(*fileIter, info)) {
114 return false;
115 }
116
117 processSource.erase(fileIter);
118 allFileDetails[info.GetFullName()] = info;
119 if (!LoadOtherIdlFiles(info, allFileDetails, processSource)) {
120 Logger::E(TAG, "failed to load other by %s", info.GetFullName().c_str());
121 return false;
122 }
123 }
124
125 return true;
126 }
127
ParseFileDetail(const std::string & sourceFile,FileDetail & info)128 bool Preprocessor::ParseFileDetail(const std::string &sourceFile, FileDetail &info)
129 {
130 Lexer lexer;
131 if (!lexer.Reset(sourceFile)) {
132 Logger::E(TAG, "failed to open file '%s'.", sourceFile.c_str());
133 return false;
134 }
135
136 info.filePath_ = lexer.GetFilePath();
137 size_t startIndex = info.filePath_.rfind(SEPARATOR);
138 size_t endIndex = info.filePath_.rfind(".idl");
139 if (startIndex == std::string::npos || endIndex == std::string::npos || (startIndex >= endIndex)) {
140 Logger::E(TAG, "failed to get file name from '%s'.", info.filePath_.c_str());
141 return false;
142 }
143 info.fileName_ = StringHelper::SubStr(info.filePath_, startIndex + 1, endIndex);
144
145 if (!ParsePackage(lexer, info)) {
146 return false;
147 }
148
149 if (!ParseImports(lexer, info)) {
150 return false;
151 }
152 return true;
153 }
154
ParsePackage(Lexer & lexer,FileDetail & info)155 bool Preprocessor::ParsePackage(Lexer &lexer, FileDetail &info)
156 {
157 InterfaceType interfaceType = Options::GetInstance().GetInterfaceType();
158 if (interfaceType == InterfaceType::HDI ||
159 interfaceType == InterfaceType::SM ||
160 interfaceType == InterfaceType::SAM ||
161 interfaceType == InterfaceType::SAM_SM ||
162 interfaceType == InterfaceType::SAM_UDS ||
163 interfaceType == InterfaceType::SM_UDS) {
164 Token token = lexer.PeekToken();
165 if (token.kind != TokenType::PACKAGE) {
166 Logger::E(TAG, "%s: expected 'package' before '%s' token", LocInfo(token).c_str(), token.value.c_str());
167 return false;
168 }
169 lexer.GetToken();
170
171 token = lexer.PeekToken();
172 if (token.kind != TokenType::ID) {
173 Logger::E(TAG, "%s: expected package name before '%s' token", LocInfo(token).c_str(), token.value.c_str());
174 return false;
175 }
176
177 if (!CheckPackageName(info.filePath_, token.value)) {
178 Logger::E(TAG, "%s:package name '%s' does not match file path '%s'", LocInfo(token).c_str(),
179 token.value.c_str(), info.filePath_.c_str());
180 return false;
181 }
182 info.packageName_ = token.value;
183 lexer.GetToken();
184
185 token = lexer.PeekToken();
186 if (token.kind != TokenType::SEMICOLON) {
187 Logger::E(TAG, "%s:expected ';' before '%s' token", LocInfo(token).c_str(), token.value.c_str());
188 return false;
189 }
190 lexer.GetToken();
191 }
192 return true;
193 }
194
ParseImports(Lexer & lexer,FileDetail & info)195 bool Preprocessor::ParseImports(Lexer &lexer, FileDetail &info)
196 {
197 Token token = lexer.PeekToken();
198 while (token.kind != TokenType::END_OF_FILE) {
199 if (token.kind != TokenType::IMPORT) {
200 lexer.GetToken();
201 token = lexer.PeekToken();
202 continue;
203 }
204
205 lexer.GetToken();
206 token = lexer.PeekToken();
207 if (token.kind != TokenType::ID) {
208 Logger::E(TAG, "%s: expected import name before '%s' token", LocInfo(token).c_str(), token.value.c_str());
209 return false;
210 }
211
212 if (!File::CheckValid(Options::GetInstance().GetImportFilePath(token.value))) {
213 Logger::E(TAG, "%s: import invalid package '%s'", LocInfo(token).c_str(), token.value.c_str());
214 return false;
215 }
216
217 info.imports_.emplace(token.value);
218 lexer.GetToken();
219
220 token = lexer.PeekToken();
221 if (token.kind != TokenType::SEMICOLON) {
222 Logger::E(TAG, "%s:expected ';' before '%s' token", LocInfo(token).c_str(), token.value.c_str());
223 return false;
224 }
225 lexer.GetToken();
226
227 token = lexer.PeekToken();
228 }
229 return true;
230 }
231
LoadOtherIdlFiles(const FileDetail & ownerFileDetail,FileDetailMap & allFileDetails,std::set<std::string> & sourceFiles)232 bool Preprocessor::LoadOtherIdlFiles(
233 const FileDetail &ownerFileDetail, FileDetailMap &allFileDetails, std::set<std::string> &sourceFiles)
234 {
235 for (const auto &importName : ownerFileDetail.imports_) {
236 if (allFileDetails.find(importName) != allFileDetails.end()) {
237 continue;
238 }
239
240 std::string otherFilePath = Options::GetInstance().GetImportFilePath(importName);
241 if (otherFilePath.empty()) {
242 Logger::E(TAG, "importName:%s, is failed", importName.c_str());
243 return false;
244 }
245
246 sourceFiles.insert(otherFilePath);
247 }
248 return true;
249 }
250
CheckCircularReference(const FileDetailMap & allFileDetails,std::vector<FileDetail> & compileSourceFiles)251 bool Preprocessor::CheckCircularReference(const FileDetailMap &allFileDetails,
252 std::vector<FileDetail> &compileSourceFiles)
253 {
254 FileDetailMap allFileDetailsTemp = allFileDetails;
255 std::queue<FileDetail> fileQueue;
256 for (const auto &filePair : allFileDetailsTemp) {
257 const FileDetail &file = filePair.second;
258 if (file.imports_.size() == 0) {
259 fileQueue.push(file);
260 }
261 }
262
263 compileSourceFiles.clear();
264 while (!fileQueue.empty()) {
265 FileDetail curFile = fileQueue.front();
266 fileQueue.pop();
267 compileSourceFiles.push_back(allFileDetails.at(curFile.GetFullName()));
268
269 for (auto &filePair : allFileDetailsTemp) {
270 FileDetail &otherFile = filePair.second;
271 if (otherFile.imports_.empty()) {
272 continue;
273 }
274
275 auto position = otherFile.imports_.find(curFile.GetFullName());
276 if (position != otherFile.imports_.end()) {
277 otherFile.imports_.erase(position);
278 }
279
280 if (otherFile.imports_.size() == 0) {
281 fileQueue.push(otherFile);
282 }
283 }
284 }
285
286 if (compileSourceFiles.size() == allFileDetailsTemp.size()) {
287 return true;
288 }
289
290 PrintCyclefInfo(allFileDetailsTemp);
291 return false;
292 }
293
PrintCyclefInfo(FileDetailMap & allFileDetails)294 void Preprocessor::PrintCyclefInfo(FileDetailMap &allFileDetails)
295 {
296 for (FileDetailMap::iterator it = allFileDetails.begin(); it != allFileDetails.end();) {
297 if (it->second.imports_.size() == 0) {
298 it = allFileDetails.erase(it);
299 } else {
300 ++it;
301 }
302 }
303
304 for (const auto &filePair : allFileDetails) {
305 std::vector<std::string> traceNodes;
306 FindCycle(filePair.second.GetFullName(), allFileDetails, traceNodes);
307 }
308 }
309
FindCycle(const std::string & curNode,FileDetailMap & allFiles,std::vector<std::string> & trace)310 void Preprocessor::FindCycle(const std::string &curNode, FileDetailMap &allFiles, std::vector<std::string> &trace)
311 {
312 auto iter = std::find_if(trace.begin(), trace.end(), [curNode](const std::string &name) {
313 return name == curNode;
314 });
315 if (iter != trace.end()) {
316 if (iter == trace.begin()) {
317 // print circular reference infomation
318 StringBuilder sb;
319 for (const auto &nodeName : trace) {
320 sb.AppendFormat("%s -> ", nodeName.c_str());
321 }
322 sb.AppendFormat("%s", curNode.c_str());
323 Logger::E(TAG, "error: there are circular reference:\n%s", sb.ToString().c_str());
324 }
325 return;
326 }
327
328 trace.push_back(curNode);
329 for (const auto &importFileName : allFiles[curNode].imports_) {
330 FindCycle(importFileName, allFiles, trace);
331 }
332
333 trace.pop_back();
334 }
335
336 /*
337 * filePath: ./ohos/interface/foo/v1_0/IFoo.idl
338 * package ohos.hdi.foo.v1_0;
339 */
CheckPackageName(const std::string & filePath,const std::string & packageName)340 bool Preprocessor::CheckPackageName(const std::string &filePath, const std::string &packageName)
341 {
342 std::string pkgToPath = Options::GetInstance().GetPackagePath(packageName);
343
344 size_t index = filePath.rfind(SEPARATOR);
345 if (index == std::string::npos) {
346 return false;
347 }
348
349 std::string parentDir = filePath.substr(0, index);
350 return parentDir == pkgToPath;
351 }
352 } // namespace Idl
353 } // namespace OHOS