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