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 "resolveDepsRelation.h"
17
18 #include <util/commonUtil.h>
19
20 namespace panda::es2panda::aot {
21
CollectCommonjsRecords(const std::vector<panda::pandasm::Field> &fieldList, const std::string &progKey, const std::string &recordName)22 bool DepsRelationResolver::CollectCommonjsRecords(const std::vector<panda::pandasm::Field> &fieldList,
23 const std::string &progKey, const std::string &recordName)
24 {
25 for (const auto &field: fieldList) {
26 if (field.name.find(util::IS_COMMONJS) == std::string::npos) {
27 continue;
28 }
29 ASSERT(field.metadata->GetValue().has_value());
30 ASSERT(field.type.GetId() == panda_file::Type::TypeId::U8);
31 if (field.metadata->GetValue().value().GetValue<uint8_t>() > 0) {
32 resolvedDepsRelation_[progKey].insert(recordName);
33 return true;
34 }
35 }
36 return false;
37 }
38
FillRecord2ProgramMap(std::unordered_map<std::string, std::string> &record2ProgramMap)39 void DepsRelationResolver::FillRecord2ProgramMap(std::unordered_map<std::string, std::string> &record2ProgramMap)
40 {
41 for (const auto &progInfo : progsInfo_) {
42 for (const auto &record : progInfo.second->program.record_table) {
43 if (record.second.field_list.empty()) {
44 continue;
45 }
46 if (progInfo.first.find(util::NPM_ENTRIES) != std::string::npos) {
47 resolvedDepsRelation_[progInfo.first].insert(record.second.name);
48 continue;
49 }
50
51 // All commonjs files will be include as dependencies for compilation without resolve. Since commonjs files
52 // will only have commonjs as dependencies, there'll be no dependencies missing.
53 if (CollectCommonjsRecords(record.second.field_list, progInfo.first, record.second.name)) {
54 break;
55 }
56 record2ProgramMap[record.second.name] = progInfo.first;
57 }
58 }
59 }
60
CollectDepsIfNeeded(const std::string &ohmurl)61 void DepsRelationResolver::CollectDepsIfNeeded(const std::string &ohmurl)
62 {
63 if (ohmurl.find(util::NORMALIZED_OHMURL_NOT_SO) != std::string::npos &&
64 !util::IsExternalPkgNames(ohmurl, compileContextInfo_.externalPkgNames)) {
65 std::string collectRecord = util::GetRecordNameFromNormalizedOhmurl(ohmurl);
66 if (!collectRecord.empty() && this->resolvedRecords_.count(collectRecord) == 0) {
67 this->depsToBeResolved_.push(collectRecord);
68 this->resolvedRecords_.insert(collectRecord);
69 }
70 }
71 }
72
DumpDepsRelations()73 void DepsRelationResolver::DumpDepsRelations()
74 {
75 auto &ss = std::cout;
76 ss << "All Dependency Files:" << std::endl;
77 for (auto dep : resolvedDepsRelation_) {
78 auto fileName = dep.first;
79 size_t abcFileNameSeparatorPos = dep.first.rfind(util::CHAR_VERTICAL_LINE);
80 size_t pos = dep.first.rfind(util::SLASH_TAG, abcFileNameSeparatorPos);
81 if (pos != std::string::npos) {
82 fileName = dep.first.substr(pos + 1, abcFileNameSeparatorPos - pos - 1);
83 }
84 ss << "program_file: " << fileName << std::endl;
85 for (auto r : dep.second) {
86 ss << "record_name: " << r << std::endl;
87 }
88 }
89 ss << std::endl;
90 }
91
Resolve()92 bool DepsRelationResolver::Resolve()
93 {
94 std::unordered_map<std::string, std::string> record2ProgramMap {};
95 FillRecord2ProgramMap(record2ProgramMap);
96
97 for (auto &entryRecord : compileContextInfo_.compileEntries) {
98 depsToBeResolved_.push(entryRecord);
99 resolvedRecords_.insert(entryRecord);
100
101 while (!depsToBeResolved_.empty()) {
102 auto record = depsToBeResolved_.front();
103 depsToBeResolved_.pop();
104 const auto progkeyItr = record2ProgramMap.find(record);
105 if (progkeyItr == record2ProgramMap.end()) {
106 // Skip external record, may happen at PatchFix or HSP scenario
107 continue;
108 }
109 const auto progItr = progsInfo_.find(progkeyItr->second);
110 if (progItr == progsInfo_.end()) {
111 std::cerr << "Failed to find program for file: " << progkeyItr->second << std::endl;
112 return false;
113 }
114 resolvedDepsRelation_[progkeyItr->second].insert(record);
115
116 CollectStaticImportDepsRelation(progItr->second->program, record);
117 CollectDynamicImportDepsRelation(progItr->second->program, record);
118 }
119 }
120 if (dumpDepsInfo_) {
121 DumpDepsRelations();
122 }
123 return true;
124 }
125
CollectStaticImportDepsRelation(const panda::pandasm::Program &program, const std::string &recordName)126 void DepsRelationResolver::CollectStaticImportDepsRelation(const panda::pandasm::Program &program,
127 const std::string &recordName)
128 {
129 auto &recordTable = program.record_table;
130 std::string literalArrayKey;
131 for (auto &pair : recordTable) {
132 if (pair.first.find(recordName) == std::string::npos) {
133 continue;
134 }
135 util::VisitStaticImports<true>(program, pair.second, [this](const std::string &ohmurl) {
136 this->CollectDepsIfNeeded(ohmurl);
137 });
138 }
139 }
140
CollectDynamicImportDepsRelation(const panda::pandasm::Program &program, const std::string &recordName)141 void DepsRelationResolver::CollectDynamicImportDepsRelation(const panda::pandasm::Program &program,
142 const std::string &recordName)
143 {
144 for (const auto &func: program.function_table) {
145 if (func.second.name.find(recordName) == std::string::npos) {
146 continue;
147 }
148 util::VisitDyanmicImports<true>(func.second, [this](const std::string &ohmurl) {
149 this->CollectDepsIfNeeded(ohmurl);
150 });
151 }
152 }
153
154 } // namespace panda::es2panda::aot
155