1/*
2 * Copyright (c) 2021 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 "sourceTextModuleRecord.h"
17#include <binder/scope.h>
18
19namespace panda::es2panda::parser {
20    int SourceTextModuleRecord::AddModuleRequest(const ModuleRequestRecord record)
21    {
22        ASSERT(!record.source_.Empty());
23        hasLazyImport_ = hasLazyImport_ || record.isLazy_;
24
25        int moduleRequestsSize = static_cast<int>(moduleRequestsMap_.size());
26        if (moduleRequestsMap_.find(record) == moduleRequestsMap_.end()) {
27            moduleRequests_.emplace_back(record);
28        }
29        auto insertedRes = moduleRequestsMap_.insert(std::make_pair(record, moduleRequestsSize));
30        return insertedRes.first->second;
31    }
32
33    // import x from 'test.js'
34    // import {x} from 'test.js'
35    // import {x as y} from 'test.js'
36    // import defaultExport from 'test.js'
37    void SourceTextModuleRecord::AddImportEntry(SourceTextModuleRecord::ImportEntry *entry)
38    {
39        CHECK_NOT_NULL(entry);
40        ASSERT(entry != nullptr);
41        ASSERT(!entry->importName_.Empty());
42        ASSERT(!entry->localName_.Empty());
43        ASSERT(entry->moduleRequestIdx_ != -1);
44        regularImportEntries_.insert(std::make_pair(entry->localName_, entry));
45        // the implicit indirect exports should be insert into indirectExportsEntries
46        // when add an ImportEntry.
47        // e.g. export { x }; import { x } from 'test.js'
48        CheckImplicitIndirectExport(entry);
49    }
50
51    // import * as x from 'test.js'
52    void SourceTextModuleRecord::AddStarImportEntry(SourceTextModuleRecord::ImportEntry *entry)
53    {
54        ASSERT(!entry->localName_.Empty());
55        ASSERT(entry->importName_.Empty());
56        ASSERT(entry->moduleRequestIdx_ != -1);
57        namespaceImportEntries_.push_back(entry);
58    }
59
60    // export {x}
61    // export {x as y}
62    // export VariableStatement
63    // export Declaration
64    // export default ...
65    bool SourceTextModuleRecord::AddLocalExportEntry(SourceTextModuleRecord::ExportEntry *entry)
66    {
67        CHECK_NOT_NULL(entry);
68        ASSERT(entry->importName_.Empty());
69        ASSERT(!entry->localName_.Empty());
70        ASSERT(!entry->exportName_.Empty());
71        ASSERT(entry->moduleRequestIdx_ == -1);
72
73        // the implicit indirect exports should be insert into indirectExportsEntries
74        // when add an ExportEntry.
75        // e.g. import { x } from 'test.js'; export { x }
76        if (CheckImplicitIndirectExport(entry)) {
77            return true;
78        }
79        if (!HasDuplicateExport(entry->exportName_)) {
80            localExportEntries_.insert(std::make_pair(entry->localName_, entry));
81            return true;
82        }
83        return false;
84    }
85
86    // export {x} from 'test.js'
87    // export {x as y} from 'test.js'
88    // import { x } from 'test.js'; export { x }
89    bool SourceTextModuleRecord::AddIndirectExportEntry(SourceTextModuleRecord::ExportEntry *entry)
90    {
91        CHECK_NOT_NULL(entry);
92        ASSERT(entry != nullptr);
93        ASSERT(!entry->importName_.Empty());
94        ASSERT(!entry->exportName_.Empty());
95        ASSERT(entry->localName_.Empty());
96        ASSERT(entry->moduleRequestIdx_ != -1);
97        if (!HasDuplicateExport(entry->exportName_)) {
98            indirectExportEntries_.push_back(entry);
99            return true;
100        }
101        return false;
102    }
103
104    // export * from 'test.js'
105    void SourceTextModuleRecord::AddStarExportEntry(SourceTextModuleRecord::ExportEntry *entry)
106    {
107        ASSERT(entry->importName_.Empty());
108        ASSERT(entry->localName_.Empty());
109        ASSERT(entry->exportName_.Empty());
110        ASSERT(entry->moduleRequestIdx_ != -1);
111        starExportEntries_.push_back(entry);
112    }
113
114    bool SourceTextModuleRecord::HasDuplicateExport(util::StringView exportName) const
115    {
116        for (auto const &entryUnit : localExportEntries_) {
117            const SourceTextModuleRecord::ExportEntry *e = entryUnit.second;
118            if (exportName == e->exportName_) {
119                return true;
120            }
121        }
122
123        for (const auto *e : indirectExportEntries_) {
124            if (exportName == e->exportName_) {
125                return true;
126            }
127        }
128
129        return false;
130    }
131
132    bool SourceTextModuleRecord::CheckImplicitIndirectExport(SourceTextModuleRecord::ExportEntry *exportEntry)
133    {
134        CHECK_NOT_NULL(exportEntry);
135        ASSERT(exportEntry != nullptr);
136        ASSERT(!exportEntry->localName_.Empty());
137        auto regularImport = regularImportEntries_.find(exportEntry->localName_);
138        if (regularImport != regularImportEntries_.end()) {
139            ConvertLocalExportToIndirect(regularImport->second, exportEntry);
140            return AddIndirectExportEntry(exportEntry);
141        }
142        return false;
143    }
144
145    void SourceTextModuleRecord::CheckImplicitIndirectExport(SourceTextModuleRecord::ImportEntry *importEntry)
146    {
147        ASSERT(!importEntry->localName_.Empty());
148        auto range = localExportEntries_.equal_range(importEntry->localName_);
149        // not found implicit indirect
150        if (range.first == range.second) {
151            return;
152        }
153
154        for (auto it = range.first; it != range.second; ++it) {
155            SourceTextModuleRecord::ExportEntry *exportEntry = it->second;
156            ConvertLocalExportToIndirect(importEntry, exportEntry);
157            indirectExportEntries_.push_back(exportEntry);
158        }
159        localExportEntries_.erase(range.first, range.second);
160    }
161
162    void SourceTextModuleRecord::ConvertLocalExportToIndirect(SourceTextModuleRecord::ImportEntry *importEntry,
163                                                              SourceTextModuleRecord::ExportEntry *exportEntry)
164    {
165        CHECK_NOT_NULL(importEntry);
166        ASSERT(exportEntry->importName_.Empty());
167        ASSERT(exportEntry->moduleRequestIdx_ == -1);
168        ASSERT(!importEntry->importName_.Empty());
169        ASSERT(importEntry->moduleRequestIdx_ != -1);
170        exportEntry->importName_ = importEntry->importName_;
171        exportEntry->moduleRequestIdx_ = importEntry->moduleRequestIdx_;
172        exportEntry->localName_ = util::StringView("");
173    }
174
175    void SourceTextModuleRecord::AssignIndexToModuleVariable(binder::ModuleScope *moduleScope)
176    {
177        uint32_t index = 0;
178        for (auto it = localExportEntries_.begin(); it != localExportEntries_.end();
179             it = localExportEntries_.upper_bound(it->first)) {
180            auto variable = CheckAndAssignIndex(moduleScope, it->first, &index);
181            if (variable != nullptr && variable->IsModuleVariable() && variable->Declaration()->IsConstDecl()) {
182                auto range = localExportEntries_.equal_range(it->first);
183                for (auto local_iter = range.first; local_iter != range.second; local_iter++) {
184                    local_iter->second->SetAsConstant();
185                }
186            }
187        }
188
189        index = 0;
190        for (const auto &elem : regularImportEntries_) {
191            CheckAndAssignIndex(moduleScope, elem.first, &index);
192        }
193    }
194
195    binder::Variable *SourceTextModuleRecord::CheckAndAssignIndex(binder::ModuleScope *moduleScope,
196                                                                  util::StringView name,
197                                                                  uint32_t *index) const
198    {
199        auto modulevar = moduleScope->FindLocal(name);
200        if (modulevar != nullptr) {
201            moduleScope->AssignIndexToModuleVariable(name, *index);
202            (*index)++;
203        }
204        return modulevar;
205    }
206
207    void SourceTextModuleRecord::RemoveDefaultLocalExportEntry()
208    {
209        util::StringView localName = parser::SourceTextModuleRecord::DEFAULT_LOCAL_NAME;
210        localExportEntries_.erase(localName);
211    }
212
213    int SourceTextModuleRecord::GetModuleRequestIdx(const util::StringView localName)
214    {
215        for (const auto &it : regularImportEntries_) {
216            if (it.first != localName) {
217                continue;
218            }
219
220            return it.second->moduleRequestIdx_;
221        }
222
223        for (const auto &it : namespaceImportEntries_) {
224            if (it->localName_ != localName) {
225                continue;
226            }
227
228            return it->moduleRequestIdx_;
229        }
230
231        return SourceTextModuleRecord::INVALID_MODULEREQUEST_ID;
232    }
233} // namespace panda::es2panda::parser
234