• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2025 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 "dep_analyzer.h"
17 #include <chrono>
18 
Dump(std::string & outFilePath)19 void DepAnalyzer::Dump(std::string &outFilePath)
20 {
21     std::ofstream outFile(outFilePath);
22     if (!outFile) {
23         std::cerr << "Error when opening a file " << outFilePath << std::endl;
24         return;
25     }
26 
27     Dump(outFile);
28 
29     outFile.close();
30 }
31 
ConvertPath(const std::string & path) const32 std::string DepAnalyzer::ConvertPath(const std::string &path) const
33 {
34     std::string converted = path;
35 
36 #if defined(_WIN32)
37     constexpr char BACKSLASH = '\\';
38     constexpr size_t ESCAPED_BACKSLASH_LENGTH = 2;
39 
40     size_t foundPos = 0;
41     while ((foundPos = converted.find(BACKSLASH, foundPos)) != std::string::npos) {
42         converted.replace(foundPos, 1, "\\\\");
43         foundPos += ESCAPED_BACKSLASH_LENGTH;
44     }
45 #endif
46 
47     return converted;
48 }
49 
Dump(std::ostream & ostr)50 void DepAnalyzer::Dump(std::ostream &ostr)
51 {
52     std::string jsonTab = "  ";
53     ostr << "{\n";
54 
55     ostr << jsonTab << "\"dependencies\": {\n";
56     bool isFirst = true;
57     for (auto [file, deps] : fileDirectDependencies_) {
58         if (!isFirst) {
59             ostr << ",\n";
60         } else {
61             isFirst = false;
62         }
63         ostr << jsonTab << jsonTab << "\"" << ConvertPath(file) << "\": [";
64         bool isFirst2 = true;
65         for (const auto &dep : deps) {
66             if (!isFirst2) {
67                 ostr << ", ";
68             } else {
69                 isFirst2 = false;
70             }
71             ostr << "\"" << ConvertPath(dep) << "\"";
72         }
73         ostr << "]";
74     }
75     ostr << "\n" << jsonTab << "},\n";
76 
77     ostr << jsonTab << "\"dependants\": {\n";
78     isFirst = true;
79     for (auto [file, deps] : fileDirectDependants_) {
80         if (!isFirst) {
81             ostr << ",\n";
82         } else {
83             isFirst = false;
84         }
85         ostr << jsonTab << jsonTab << "\"" << ConvertPath(file) << "\": [";
86         bool isFirst2 = true;
87         for (const auto &dep : deps) {
88             if (!isFirst2) {
89                 ostr << ", ";
90             } else {
91                 isFirst2 = false;
92             }
93             ostr << "\"" << ConvertPath(dep) << "\"";
94         }
95         ostr << "]";
96     }
97     ostr << "\n" << jsonTab << "}\n";
98     ostr << "}";
99 }
100 
AddImports(ark::es2panda::parser::ETSParser * parser)101 void DepAnalyzer::AddImports(ark::es2panda::parser::ETSParser *parser)
102 {
103     ark::es2panda::util::StringView firstSourceFilePath = parser->GetGlobalProgramAbsName();
104     sourcePaths_.emplace_back(std::string(firstSourceFilePath));
105 
106     ark::es2panda::util::ImportPathManager *manager = parser->GetImportPathManager();
107     auto &parseList = manager->ParseList();
108 
109     for (auto &pl : parseList) {
110         sourcePaths_.emplace_back(std::string(pl.importData.resolvedSource));
111     }
112 }
113 
MergeFileDeps(ark::es2panda::parser::Program * mainProgram)114 void DepAnalyzer::MergeFileDeps(ark::es2panda::parser::Program *mainProgram)
115 {
116     std::string progAbsPath = std::string {mainProgram->AbsoluteName()};
117 
118     if (mainProgram->GetFileDependencies().empty()) {
119         fileDirectDependencies_.emplace(progAbsPath, std::unordered_set<std::string>());
120     }
121     if (fileDirectDependants_.count(progAbsPath) == 0U) {
122         fileDirectDependants_.emplace(progAbsPath, std::unordered_set<std::string>());
123     }
124     for (auto &[_, progs] : mainProgram->DirectExternalSources()) {
125         for (auto prog : progs) {
126             auto extprogAbsPath = std::string {prog->AbsoluteName()};
127             if (extprogAbsPath == progAbsPath) {
128                 continue;
129             }
130 
131             fileDirectDependencies_[progAbsPath].insert(extprogAbsPath);
132             fileDirectDependants_[extprogAbsPath].insert(progAbsPath);
133         }
134     }
135     for (const auto &[key, valueSet] : mainProgram->GetFileDependencies()) {
136         fileDirectDependencies_[key].insert(valueSet.begin(), valueSet.end());
137         for (const auto &v : valueSet) {
138             fileDirectDependants_[v].insert(key);
139         }
140     }
141 }
142 
AnalyzeDepsForMultiFiles(const char * exec,std::vector<std::string> & fileList,std::string & arktsconfig)143 int DepAnalyzer::AnalyzeDepsForMultiFiles(const char *exec, std::vector<std::string> &fileList,
144                                           std::string &arktsconfig)
145 {
146     std::unordered_set<std::string> parsedFileList;
147     const auto *impl = es2panda_GetImpl(ES2PANDA_LIB_VERSION);
148 
149     for (auto &file : fileList) {
150         if (parsedFileList.count(file) != 0U || fileDirectDependencies_.count(file) != 0U) {
151             continue;
152         }
153 
154         es2panda_Config *cfg = nullptr;
155         if (!arktsconfig.empty()) {
156             std::array<const char *, 3> args = {exec, file.c_str(), arktsconfig.c_str()};
157             cfg = impl->CreateConfig(args.size(), args.data());
158         } else {
159             std::array<const char *, 2> args = {exec, file.c_str()};
160             cfg = impl->CreateConfig(args.size(), args.data());
161         }
162 
163         if (cfg == nullptr) {
164             std::cerr << "Failed to create config" << std::endl;
165             return 1;
166         }
167         auto *cfgImpl = reinterpret_cast<ark::es2panda::public_lib::ConfigImpl *>(cfg);
168         auto parserInputCStr = cfgImpl->options->CStrParserInputContents().first;
169 
170         es2panda_Context *ctx =
171             impl->CreateContextFromString(cfg, parserInputCStr, cfgImpl->options->SourceFileName().c_str());
172         auto *ctxImpl = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
173         impl->ProceedToState(ctx, ES2PANDA_STATE_PARSED);
174 
175         if (ctxImpl->state == ES2PANDA_STATE_ERROR) {
176             ctxImpl->checker->LogTypeError(std::string("Parse Failed: ").append(ctxImpl->errorMessage),
177                                            ctxImpl->errorPos);
178             impl->DestroyContext(ctx);
179             impl->DestroyConfig(cfg);
180             return 1;
181         }
182 
183         ark::es2panda::parser::Program *mainProgram = ctxImpl->parserProgram;
184         std::string mainProgramAbsPath = std::string {mainProgram->AbsoluteName()};
185         parsedFileList.insert(mainProgramAbsPath);
186         MergeFileDeps(mainProgram);
187 
188         impl->DestroyContext(ctx);
189         impl->DestroyConfig(cfg);
190     }
191     return 0;
192 }
193 
AddFileList(std::string & fileListPath,std::vector<std::string> & fileList)194 static void AddFileList(std::string &fileListPath, std::vector<std::string> &fileList)
195 {
196     std::ifstream inFile(fileListPath);
197     if (!inFile.is_open()) {
198         std::cerr << "Error when opening a file " << fileListPath << std::endl;
199         return;
200     }
201     std::string line;
202     while (getline(inFile, line)) {
203         if (!line.empty()) {
204             fileList.emplace_back(line);
205         }
206     }
207 }
208 
ParseArguments(ark::Span<const char * const> args)209 std::optional<DepAnalyzerArgs> ParseArguments(ark::Span<const char *const> args)
210 {
211     DepAnalyzerArgs parsedArgs;
212     parsedArgs.programName = args[0];
213 
214     for (size_t i = 1; i < args.size(); i++) {
215         if (std::strncmp(args[i], "--arktsconfig=", std::strlen("--arktsconfig=")) == 0) {
216             parsedArgs.arktsconfig = args[i];
217             continue;
218         }
219         if (std::strncmp(args[i], "@", std::strlen("@")) == 0) {
220             std::string_view arg(args[i]);
221             std::string fileListPath(arg.substr(1));
222             AddFileList(fileListPath, parsedArgs.fileList);
223             continue;
224         }
225         parsedArgs.fileList.emplace_back(args[i]);
226     }
227 
228     return parsedArgs;
229 }
230 
AnalyzeDeps(int argc,const char ** argv)231 int DepAnalyzer::AnalyzeDeps(int argc, const char **argv)
232 {
233     int minArgCount = 2;
234     if (argc < minArgCount) {
235         std::cerr << "No file has been entered for analysis" << std::endl;
236         return 1;
237     }
238     ark::Span<const char *const> args(argv, static_cast<size_t>(argc));
239     auto parsedArgs = ParseArguments(args);
240     if (AnalyzeDepsForMultiFiles(parsedArgs->programName.c_str(), parsedArgs->fileList, parsedArgs->arktsconfig) != 0) {
241         return 1;
242     }
243     return 0;
244 }
245