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