1/*
2 * Copyright (c) 2021-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#ifndef ES2PANDA_VARBINDER_ETSBINDER_H
17#define ES2PANDA_VARBINDER_ETSBINDER_H
18
19#include "varbinder/TypedBinder.h"
20#include "varbinder/recordTable.h"
21#include "ir/ets/etsImportDeclaration.h"
22#include "ir/ets/etsReExportDeclaration.h"
23#include "ir/expressions/identifier.h"
24#include "ir/module/importSpecifier.h"
25#include "parser/program/program.h"
26#include "util/importPathManager.h"
27
28namespace ark::es2panda::varbinder {
29using AliasesByExportedNames = ArenaMap<util::StringView, util::StringView>;
30using ModulesToExportedNamesWithAliases = ArenaMap<util::StringView, AliasesByExportedNames>;
31
32struct DynamicImportData {
33    const ir::ETSImportDeclaration *import;
34    const ir::AstNode *specifier;
35    Variable *variable;
36};
37
38using DynamicImportVariables = ArenaUnorderedMap<const Variable *, DynamicImportData>;
39
40class ETSBinder : public TypedBinder {
41public:
42    explicit ETSBinder(ArenaAllocator *allocator)
43        : TypedBinder(allocator),
44          globalRecordTable_(allocator, Program(), RecordTableFlags::NONE),
45          recordTable_(&globalRecordTable_),
46          externalRecordTable_(Allocator()->Adapter()),
47          defaultImports_(Allocator()->Adapter()),
48          dynamicImports_(Allocator()->Adapter()),
49          reExportImports_(Allocator()->Adapter()),
50          dynamicImportVars_(Allocator()->Adapter()),
51          importSpecifiers_(Allocator()->Adapter()),
52          selectiveExportAliasMultimap_(Allocator()->Adapter())
53    {
54        InitImplicitThisParam();
55    }
56
57    NO_COPY_SEMANTIC(ETSBinder);
58    NO_MOVE_SEMANTIC(ETSBinder);
59    ~ETSBinder() override = default;
60
61    ScriptExtension Extension() const override
62    {
63        return ScriptExtension::ETS;
64    }
65
66    ResolveBindingOptions BindingOptions() const override
67    {
68        return ResolveBindingOptions::BINDINGS;
69    }
70
71    RecordTable *GetRecordTable()
72    {
73        return recordTable_;
74    }
75
76    const RecordTable *GetRecordTable() const
77    {
78        return recordTable_;
79    }
80
81    void SetRecordTable(RecordTable *table)
82    {
83        recordTable_ = table;
84    }
85
86    RecordTable *GetGlobalRecordTable()
87    {
88        return &globalRecordTable_;
89    }
90
91    const RecordTable *GetGlobalRecordTable() const
92    {
93        return &globalRecordTable_;
94    }
95
96    ArenaMap<parser::Program *, RecordTable *> &GetExternalRecordTable()
97    {
98        return externalRecordTable_;
99    }
100
101    const ArenaMap<parser::Program *, RecordTable *> &GetExternalRecordTable() const
102    {
103        return externalRecordTable_;
104    }
105
106    void HandleCustomNodes(ir::AstNode *childNode) override;
107
108    void IdentifierAnalysis() override;
109    void BuildClassDefinition(ir::ClassDefinition *classDef) override;
110    void BuildClassProperty(const ir::ClassProperty *prop) override;
111    void LookupIdentReference(ir::Identifier *ident) override;
112    bool BuildInternalName(ir::ScriptFunction *scriptFunc) override;
113    void AddCompilableFunction(ir::ScriptFunction *func) override;
114
115    void LookupTypeReference(ir::Identifier *ident, bool allowDynamicNamespaces);
116    void LookupTypeArgumentReferences(ir::ETSTypeReference *typeRef);
117    void BuildInterfaceDeclaration(ir::TSInterfaceDeclaration *decl);
118    void BuildMemberExpression(ir::MemberExpression *memberExpr);
119    void BuildMethodDefinition(ir::MethodDefinition *methodDef);
120    void BuildImportDeclaration(ir::ETSImportDeclaration *decl);
121    void BuildETSNewClassInstanceExpression(ir::ETSNewClassInstanceExpression *classInstance);
122    bool DetectNameConflict(const util::StringView localName, Variable *const var, Variable *const otherVar,
123                            const ir::StringLiteral *const importPath, bool overloadAllowed);
124    void AddSpecifiersToTopBindings(ir::AstNode *specifier, const ir::ETSImportDeclaration *import);
125    ArenaVector<parser::Program *> GetExternalProgram(const util::StringView &sourceName,
126                                                      const ir::StringLiteral *importPath);
127    bool AddImportNamespaceSpecifiersToTopBindings(ir::AstNode *specifier,
128                                                   const varbinder::Scope::VariableMap &globalBindings,
129                                                   const parser::Program *importProgram,
130                                                   const varbinder::GlobalScope *importGlobalScope,
131                                                   const ir::ETSImportDeclaration *import);
132    ir::ETSImportDeclaration *FindImportDeclInReExports(const ir::ETSImportDeclaration *const import,
133                                                        std::vector<ir::ETSImportDeclaration *> &viewedReExport,
134                                                        const util::StringView &imported,
135                                                        const ir::StringLiteral *const importPath);
136    bool AddImportSpecifiersToTopBindings(ir::AstNode *specifier, const varbinder::Scope::VariableMap &globalBindings,
137                                          const ir::ETSImportDeclaration *import,
138                                          const ArenaVector<parser::Program *> &recordRes,
139                                          std::vector<ir::ETSImportDeclaration *> viewedReExport);
140    void ValidateImportVariable(varbinder::Variable *const var, const ir::ETSImportDeclaration *const import,
141                                const util::StringView &imported, const ir::StringLiteral *const importPath);
142    Variable *FindImportSpecifiersVariable(const util::StringView &imported,
143                                           const varbinder::Scope::VariableMap &globalBindings,
144                                           const ArenaVector<parser::Program *> &recordRes);
145    Variable *FindStaticBinding(const ArenaVector<parser::Program *> &recordRes, const ir::StringLiteral *importPath);
146    void AddSpecifiersToTopBindings(
147        ir::AstNode *specifier, const ir::ETSImportDeclaration *import, ir::StringLiteral *path,
148        std::vector<ir::ETSImportDeclaration *> viewedReExport = std::vector<ir::ETSImportDeclaration *>());
149    void AddDynamicSpecifiersToTopBindings(ir::AstNode *specifier, const ir::ETSImportDeclaration *import);
150
151    void ResolveInterfaceDeclaration(ir::TSInterfaceDeclaration *decl);
152    void ResolveMethodDefinition(ir::MethodDefinition *methodDef);
153    LocalScope *ResolvePropertyReference(ir::ClassProperty *prop, ClassScope *scope);
154    void ResolveEnumDeclaration(ir::TSEnumDeclaration *enumDecl);
155    void InitializeInterfaceIdent(ir::TSInterfaceDeclaration *decl);
156    void BuildExternalProgram(parser::Program *extProgram);
157    void BuildProgram();
158
159    void BuildFunctionName(const ir::ScriptFunction *func) const;
160    bool BuildInternalNameWithCustomRecordTable(ir::ScriptFunction *scriptFunc, RecordTable *recordTable);
161    void BuildProxyMethod(ir::ScriptFunction *func, const util::StringView &containingClassName, bool isStatic,
162                          bool isExternal);
163    void AddFunctionThisParam(ir::ScriptFunction *func);
164
165    void SetDefaultImports(ArenaVector<ir::ETSImportDeclaration *> defaultImports)
166    {
167        defaultImports_ = std::move(defaultImports);
168    }
169
170    void AddDynamicImport(ir::ETSImportDeclaration *import)
171    {
172        ASSERT(import->Language().IsDynamic());
173        dynamicImports_.push_back(import);
174    }
175
176    const ArenaVector<ir::ETSImportDeclaration *> &DynamicImports() const
177    {
178        return dynamicImports_;
179    }
180
181    void AddReExportImport(ir::ETSReExportDeclaration *reExport)
182    {
183        reExportImports_.push_back(reExport);
184    }
185
186    const ArenaVector<ir::ETSReExportDeclaration *> &ReExportImports() const
187    {
188        return reExportImports_;
189    }
190
191    const DynamicImportVariables &DynamicImportVars() const
192    {
193        return dynamicImportVars_;
194    }
195
196    const ir::AstNode *DefaultExport()
197    {
198        return defaultExport_;
199    }
200
201    void SetDefaultExport(ir::AstNode *defaultExport)
202    {
203        defaultExport_ = defaultExport;
204    }
205
206    /* Returns the list of programs belonging to the same compilation unit based on a program path */
207    ArenaVector<parser::Program *> GetProgramList(const util::StringView &path) const
208    {
209        for (const auto &extRecords : globalRecordTable_.Program()->ExternalSources()) {
210            for (const auto &program : extRecords.second) {
211                if (program->AbsoluteName() == path) {
212                    return extRecords.second;
213                }
214
215                // in case of importing a package folder, the path could not be resolved to a specific file
216                if (program->IsPackageModule() && program->SourceFileFolder() == path) {
217                    return extRecords.second;
218                }
219            }
220        }
221
222        return ArenaVector<parser::Program *>(Allocator()->Adapter());
223    }
224
225    bool IsDynamicModuleVariable(const Variable *var) const;
226    bool IsDynamicNamespaceVariable(const Variable *var) const;
227    const DynamicImportData *DynamicImportDataForVar(const Variable *var) const;
228
229    void ResolveReferenceForScope(ir::AstNode *node, Scope *scope);
230    void ResolveReferencesForScope(ir::AstNode const *parent, Scope *scope);
231
232    void ResolveReferencesForScopeWithContext(ir::AstNode *node, Scope *scope);
233
234    bool CheckForRedeclarationError(const util::StringView &localName, Variable *const var,
235                                    const ir::StringLiteral *const importPath);
236
237    bool AddSelectiveExportAlias(util::StringView const &path, util::StringView const &key,
238                                 util::StringView const &value)
239    {
240        if (auto foundMap = selectiveExportAliasMultimap_.find(path); foundMap != selectiveExportAliasMultimap_.end()) {
241            return foundMap->second.insert({key, value}).second;
242        }
243
244        ArenaMap<util::StringView, util::StringView> map(Allocator()->Adapter());
245        bool insertResult = map.insert({key, value}).second;
246        selectiveExportAliasMultimap_.insert({path, map});
247        return insertResult;
248    }
249
250    [[nodiscard]] const ModulesToExportedNamesWithAliases &GetSelectiveExportAliasMultimap() const noexcept
251    {
252        return selectiveExportAliasMultimap_;
253    }
254
255    util::StringView FindNameInAliasMap(const util::StringView &pathAsKey, const util::StringView &aliasName)
256    {
257        if (auto relatedMap = selectiveExportAliasMultimap_.find(pathAsKey);
258            relatedMap != selectiveExportAliasMultimap_.end()) {
259            if (auto item = relatedMap->second.find(aliasName); item != relatedMap->second.end()) {
260                return item->second;
261            }
262        }
263
264        return "";
265    }
266
267    util::StringView FindLocalNameForImport(const ir::ImportSpecifier *const importSpecifier,
268                                            util::StringView &imported, const ir::StringLiteral *const importPath)
269    {
270        if (importSpecifier->Local() != nullptr) {
271            auto checkImportPathAndName = [&importPath, &imported](const auto &savedSpecifier) {
272                return importPath->Str() != savedSpecifier.first && imported == savedSpecifier.second;
273            };
274            if (!std::any_of(importSpecifiers_.begin(), importSpecifiers_.end(), checkImportPathAndName)) {
275                TopScope()->EraseBinding(imported);
276            }
277
278            importSpecifiers_.emplace_back(importPath->Str(), imported);
279
280            return importSpecifier->Local()->Name();
281        }
282
283        return imported;
284    }
285
286private:
287    void BuildClassDefinitionImpl(ir::ClassDefinition *classDef);
288    void InitImplicitThisParam();
289    void HandleStarImport(ir::TSQualifiedName *importName, util::StringView fullPath);
290    void ImportGlobalProperties(const ir::ClassDefinition *classDef);
291    bool ImportGlobalPropertiesForNotDefaultedExports(varbinder::Variable *var, const util::StringView &name,
292                                                      const ir::ClassElement *classElement);
293    void InsertForeignBinding(ir::AstNode *specifier, const ir::ETSImportDeclaration *import,
294                              const util::StringView &name, Variable *var);
295    void ImportAllForeignBindings(ir::AstNode *specifier, const varbinder::Scope::VariableMap &globalBindings,
296                                  const parser::Program *importProgram, const varbinder::GlobalScope *importGlobalScope,
297                                  const ir::ETSImportDeclaration *import);
298
299    RecordTable globalRecordTable_;
300    RecordTable *recordTable_;
301    ArenaMap<parser::Program *, RecordTable *> externalRecordTable_;
302    ArenaVector<ir::ETSImportDeclaration *> defaultImports_;
303    ArenaVector<ir::ETSImportDeclaration *> dynamicImports_;
304    ArenaVector<ir::ETSReExportDeclaration *> reExportImports_;
305    DynamicImportVariables dynamicImportVars_;
306    ir::Identifier *thisParam_ {};
307    ArenaVector<std::pair<util::StringView, util::StringView>> importSpecifiers_;
308    ir::AstNode *defaultExport_ {};
309    ModulesToExportedNamesWithAliases selectiveExportAliasMultimap_;
310
311    friend class RecordTableContext;
312};
313
314class RecordTableContext {
315public:
316    RecordTableContext(ETSBinder *varBinder, parser::Program *extProgram)
317        : varBinder_(varBinder), savedRecordTable_(varBinder->recordTable_)
318    {
319        varBinder->recordTable_ = varBinder->externalRecordTable_[extProgram];
320    }
321
322    NO_COPY_SEMANTIC(RecordTableContext);
323    NO_MOVE_SEMANTIC(RecordTableContext);
324
325    ~RecordTableContext()
326    {
327        varBinder_->recordTable_ = savedRecordTable_;
328    }
329
330private:
331    ETSBinder *varBinder_;
332    RecordTable *savedRecordTable_;
333};
334
335}  // namespace ark::es2panda::varbinder
336
337#endif
338