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
20namespace panda::es2panda::aot {
21
22bool 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
39void 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
61void 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
73void 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
92bool 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
126void 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
141void 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