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
21namespace ark::es2panda::evaluate {
22
23void 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
47void 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
85std::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
108std::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.
136std::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