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 "evaluate/pathResolver.h"
17 #include "evaluate/debugInfoStorage.h"
18
19 #include "parser/program/program.h"
20
21 namespace ark::es2panda::evaluate {
22
FindImportedFunctions(ArenaVector<EntityInfo> &overloadSet, std::string_view filePath, std::string_view entityName)23 void PathResolver::FindImportedFunctions(ArenaVector<EntityInfo> &overloadSet, std::string_view filePath,
24 std::string_view entityName)
25 {
26 // NOTE: cache all the resolved paths.
27 auto *table = debugInfoStorage_.GetImportExportTable(filePath);
28 if (table == nullptr) {
29 LOG(WARNING, ES2PANDA) << "Failed to find import/export table for " << filePath;
30 return;
31 }
32
33 // `import * as B from "C"` should not be searched, as it handled differently in compiler.
34 const auto &imports = table->GetImports();
35 auto optOverloadSet = imports.find(entityName);
36 if (optOverloadSet == imports.end()) {
37 return;
38 }
39
40 ASSERT(!optOverloadSet->second.empty());
41 for (const auto &[path, entity] : optOverloadSet->second) {
42 // `import {A as B} from "C"`
43 FindExportedFunctions(overloadSet, path, entity);
44 }
45 }
46
FindExportedFunctions(ArenaVector<EntityInfo> &overloadSet, std::string_view filePath, std::string_view entityName)47 void PathResolver::FindExportedFunctions(ArenaVector<EntityInfo> &overloadSet, std::string_view filePath,
48 std::string_view entityName)
49 {
50 // NOTE: cache all the resolved paths.
51 auto *table = debugInfoStorage_.GetImportExportTable(filePath);
52 if (table == nullptr) {
53 LOG(WARNING, ES2PANDA) << "Failed to find import/export table for " << filePath;
54 return;
55 }
56
57 const auto &exports = table->GetExports();
58 const auto optOverloadSet = exports.find(entityName);
59 if (optOverloadSet != exports.end()) {
60 ASSERT(!optOverloadSet->second.empty());
61 for (const auto &[path, entity] : optOverloadSet->second) {
62 // `export {A as B} from "C"`
63 if (path.empty()) {
64 overloadSet.push_back(EntityInfo(filePath, entity));
65 } else {
66 FindExportedFunctions(overloadSet, path, entity);
67 }
68 }
69 }
70
71 // Still need to traverse re-export-all statements to fill the complete overload set.
72 const auto optReExportAll = exports.find(STAR_IMPORT);
73 if (optReExportAll != exports.end()) {
74 ASSERT(!optReExportAll->second.empty());
75 for (const auto &[path, entity] : optReExportAll->second) {
76 // export * from "C"
77 (void)entity;
78 ASSERT(entity == STAR_IMPORT);
79
80 FindExportedFunctions(overloadSet, path, entityName);
81 }
82 }
83 }
84
FindNamedImportAll(std::string_view filePath, std::string_view bindingName)85 std::string_view PathResolver::FindNamedImportAll(std::string_view filePath, std::string_view bindingName)
86 {
87 auto *table = debugInfoStorage_.GetImportExportTable(filePath);
88 if (table == nullptr) {
89 LOG(WARNING, ES2PANDA) << "Failed to find import/export table for " << filePath;
90 return {};
91 }
92
93 const auto &imports = table->GetImports();
94 auto optEntity = imports.find(bindingName);
95 if (optEntity == imports.end()) {
96 return {};
97 }
98
99 ASSERT(!optEntity->second.empty());
100 for (const auto &[path, entity] : optEntity->second) {
101 if (entity == STAR_IMPORT) {
102 return path;
103 }
104 }
105 return {};
106 }
107
FindImportedEntity(std::string_view filePath, std::string_view entityName)108 std::optional<EntityInfo> PathResolver::FindImportedEntity(std::string_view filePath, std::string_view entityName)
109 {
110 // NOTE: cache all the resolved paths.
111 auto *table = debugInfoStorage_.GetImportExportTable(filePath);
112 if (table == nullptr) {
113 LOG(WARNING, ES2PANDA) << "Failed to find import/export table for " << filePath;
114 return {};
115 }
116
117 // `import * as B from "C"` should not be searched, as it handled differently in compiler.
118 const auto &imports = table->GetImports();
119 auto optEntity = imports.find(entityName);
120 if (optEntity == imports.end()) {
121 return {};
122 }
123
124 ASSERT(!optEntity->second.empty());
125 if (optEntity->second.size() > 1) {
126 // Have more than one imports for the given name - it could not be a variable.
127 return {};
128 }
129 // `import {A as B} from "C"`
130 auto [path, entity] = optEntity->second[0];
131 return FindExportedEntity(path, entity);
132 }
133
134 // Note that the current implementation does not guarantee that the found entity is indeed a variable,
135 // so users must check it manually by traversing the found file's ETSGLOBAL fields.
FindExportedEntity(std::string_view filePath, std::string_view entityName)136 std::optional<EntityInfo> PathResolver::FindExportedEntity(std::string_view filePath, std::string_view entityName)
137 {
138 // NOTE: cache all the resolved paths.
139 auto *table = debugInfoStorage_.GetImportExportTable(filePath);
140 if (table == nullptr) {
141 LOG(WARNING, ES2PANDA) << "Failed to find import/export table for " << filePath;
142 return {};
143 }
144
145 const auto &exports = table->GetExports();
146 const auto optOverloadSet = exports.find(entityName);
147 if (optOverloadSet != exports.end()) {
148 ASSERT(!optOverloadSet->second.empty());
149 if (optOverloadSet->second.size() > 1) {
150 // Have more than one imports for the given name, but we search for the single one - variable or class.
151 return {};
152 }
153 // export {A as B} from "C"
154 const auto &[path, entity] = optOverloadSet->second[0];
155 if (path.empty()) {
156 return EntityInfo(filePath, entity);
157 }
158 return FindExportedEntity(path, entity);
159 }
160
161 const auto optReExportAll = exports.find(STAR_IMPORT);
162 if (optReExportAll != exports.end()) {
163 ASSERT(!optReExportAll->second.empty());
164 for (const auto &[path, entity] : optReExportAll->second) {
165 // export * from "C"
166 (void)entity;
167 ASSERT(entity == STAR_IMPORT);
168
169 auto optResult = FindExportedEntity(path, entityName);
170 if (optResult) {
171 return optResult;
172 }
173 }
174 }
175
176 return {};
177 }
178
179 } // namespace ark::es2panda::evaluate
180