* Copyright (c) 2021-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ES2PANDA_VARBINDER_ETSBINDER_H
#define ES2PANDA_VARBINDER_ETSBINDER_H
#include "varbinder/TypedBinder.h"
#include "varbinder/recordTable.h"
#include "ir/ets/etsImportDeclaration.h"
#include "ir/ets/etsReExportDeclaration.h"
#include "ir/expressions/identifier.h"
#include "ir/module/importSpecifier.h"
#include "ir/statements/annotationDeclaration.h"
#include "parser/ETSparser.h"
namespace ark::es2panda::ir {
class ETSImportDeclaration;
class AstNode;
class Identifier;
class ObjectExpression;
class ETSTypeReference;
class MethodDefinition;
class AnnotationUsage;
class StringLiteral;
class ETSReExportDeclaration;
class TSEnumDeclaration;
class TSQualifiedName;
class ClassElement;
class ImportSpecifier;
class ETSNewClassInstanceExpression;
}
namespace ark::es2panda::varbinder {
using AliasesByExportedNames = ArenaMap<util::StringView, std::pair<util::StringView, ir::AstNode const *>>;
using ModulesToExportedNamesWithAliases = ArenaMap<util::StringView, AliasesByExportedNames>;
struct DynamicImportData {
const ir::ETSImportDeclaration *import;
const ir::AstNode *specifier;
Variable *variable;
};
using DynamicImportVariables = ArenaUnorderedMap<const Variable *, DynamicImportData>;
class ETSBinder : public TypedBinder {
public:
explicit ETSBinder(public_lib::Context *context)
: TypedBinder(context),
globalRecordTable_(Allocator()->New<RecordTable>(Allocator(), nullptr, RecordTableFlags::NONE)),
recordTable_(globalRecordTable_),
externalRecordTable_(Allocator()->Adapter()),
defaultImports_(Allocator()->Adapter()),
dynamicImports_(Allocator()->Adapter()),
reExportImports_(Allocator()->Adapter()),
reexportedNames_(Allocator()->Adapter()),
selectiveExportAliasMultimap_(Allocator()->New<ModulesToExportedNamesWithAliases>())
{
InitImplicitThisParam();
}
ETSBinder() = delete;
NO_COPY_SEMANTIC(ETSBinder);
NO_MOVE_SEMANTIC(ETSBinder);
~ETSBinder() override = default;
[[nodiscard]] ScriptExtension Extension() const noexcept override
{
return ScriptExtension::ETS;
}
[[nodiscard]] ResolveBindingOptions BindingOptions() const noexcept override
{
return ResolveBindingOptions::BINDINGS;
}
[[nodiscard]] RecordTable *GetRecordTable() noexcept
{
return recordTable_;
}
[[nodiscard]] const RecordTable *GetRecordTable() const noexcept
{
return recordTable_;
}
void SetRecordTable(RecordTable *table) noexcept
{
recordTable_ = table;
}
bool CheckRecordTablesConsistency(parser::Program *program = nullptr) const;
void SetGlobalRecordTable(RecordTable *tbl) noexcept
{
globalRecordTable_ = tbl;
}
[[nodiscard]] RecordTable *GetGlobalRecordTable() noexcept
{
return globalRecordTable_;
}
[[nodiscard]] const RecordTable *GetGlobalRecordTable() const noexcept
{
return globalRecordTable_;
}
[[nodiscard]] ArenaMap<parser::Program *, RecordTable *> &GetExternalRecordTable() noexcept
{
return externalRecordTable_;
}
[[nodiscard]] const ArenaMap<parser::Program *, RecordTable *> &GetExternalRecordTable() const noexcept
{
return externalRecordTable_;
}
void CleanScopesAndRecordTables(parser::Program *program)
{
this->CleanScopes();
externalRecordTable_.clear();
globalRecordTable_->CleanUp();
reExportImports_.erase(program);
}
void HandleCustomNodes(ir::AstNode *childNode) override;
void IdentifierAnalysis() override;
void BuildClassDefinition(ir::ClassDefinition *classDef) override;
void BuildObjectExpression(ir::ObjectExpression *obj);
void BuildETSTypeReference(ir::ETSTypeReference *typeRef);
void BuildClassProperty(const ir::ClassProperty *prop) override;
void LookupIdentReference(ir::Identifier *ident) override;
[[nodiscard]] bool BuildInternalName(ir::ScriptFunction *scriptFunc) override;
void AddCompilableFunction(ir::ScriptFunction *func) override;
static bool IsSpecialName(const util::StringView &name);
[[nodiscard]] bool LookupInDebugInfoPlugin(ir::Identifier *ident);
void LookupTypeReference(ir::Identifier *ident);
void LookupTypeArgumentReferences(ir::ETSTypeReference *typeRef);
void BuildInterfaceDeclaration(ir::TSInterfaceDeclaration *decl);
void BuildMemberExpression(ir::MemberExpression *memberExpr);
void BuildMethodDefinition(ir::MethodDefinition *methodDef);
void BuildOverloadDeclaration(ir::OverloadDeclaration *overloadDef);
void BuildAnnotationDeclaration(ir::AnnotationDeclaration *annoDecl);
void BuildAnnotationUsage(ir::AnnotationUsage *annoUsage);
void BuildImportDeclaration(ir::ETSImportDeclaration *decl);
void ValidateReexportDeclaration(ir::ETSReExportDeclaration *decl);
void ValidateReexports();
Variable *ValidateImportSpecifier(const ir::ImportSpecifier *const specifier,
const ir::ETSImportDeclaration *const import);
void BuildETSNewClassInstanceExpression(ir::ETSNewClassInstanceExpression *classInstance);
[[nodiscard]] bool DetectNameConflict(const util::StringView localName, Variable *const var,
Variable *const otherVar, ir::Identifier const *local);
parser::Program *GetExternalProgram(const ir::ETSImportDeclaration *import);
std::pair<ir::ETSImportDeclaration *, ir::AstNode *> FindImportDeclInReExports(
const ir::ETSImportDeclaration *const import, const util::StringView &imported);
std::pair<ir::ETSImportDeclaration *, ir::AstNode *> FindImportDeclInNamedExports(
const ir::ETSImportDeclaration *const import, const util::StringView &imported);
std::pair<ir::ETSImportDeclaration *, ir::AstNode *> FindImportDeclInExports(
const ir::ETSImportDeclaration *const import, const util::StringView &imported);
void AddImportNamespaceSpecifiersToTopBindings(parser::Program *const importedProgram,
ir::ImportNamespaceSpecifier *namespaceSpecifier,
const ir::ETSImportDeclaration *import);
util::StringView GetAdjustedImportedName(ir::ImportSpecifier *const importSpecifier,
const ir::ETSImportDeclaration *const import);
bool AddImportSpecifiersToTopBindings(parser::Program *importedProgram, ir::ImportSpecifier *importSpecifier,
const ir::ETSImportDeclaration *import);
void AddImportDefaultSpecifiersToTopBindings(parser::Program *const importedProgram,
ir::ImportDefaultSpecifier *importDefaultSpecifier,
const ir::ETSImportDeclaration *import);
void ValidateImportVariable(const ir::AstNode *node, const ir::AstNode *declNode, const util::StringView &imported,
const ir::ETSImportDeclaration *import);
Variable *FindImportSpecifiersVariable(const util::StringView &imported, parser::Program *importedProgram);
Variable *FindStaticBinding(parser::Program *const importedProgram, const ir::ETSImportDeclaration *import);
Variable *AddImportSpecifierFromReExport(ir::AstNode *importSpecifier, const ir::ETSImportDeclaration *const import,
const util::StringView &imported);
void AddSpecifiersToTopBindings(ir::AstNode *const specifier, const ir::ETSImportDeclaration *const import);
void ResolveInterfaceDeclaration(ir::TSInterfaceDeclaration *decl);
void ResolveMethodDefinition(ir::MethodDefinition *methodDef);
LocalScope *ResolvePropertyReference(ir::ClassProperty *prop, ClassScope *scope);
void ResolveEnumDeclaration(ir::TSEnumDeclaration *enumDecl);
void InitializeInterfaceIdent(ir::TSInterfaceDeclaration *decl);
void BuildExternalProgram(parser::Program *extProgram);
void BuildProgram();
void BuildFunctionName(const ir::ScriptFunction *func) const;
bool BuildInternalNameWithCustomRecordTable(ir::ScriptFunction *scriptFunc, RecordTable *recordTable);
void AddFunctionThisParam(ir::ScriptFunction *func);
void ThrowError(const lexer::SourcePosition &pos, const diagnostic::DiagnosticKind &kind) const
{
ThrowError(pos, kind, util::DiagnosticMessageParams {});
}
void ThrowError(const lexer::SourcePosition &pos, const diagnostic::DiagnosticKind &kind,
const util::DiagnosticMessageParams ¶ms) const override;
bool IsGlobalIdentifier(const util::StringView &str) const override;
void SetDefaultImports(ArenaVector<ir::ETSImportDeclaration *> defaultImports) noexcept
{
defaultImports_ = std::move(defaultImports);
}
void AddDynamicImport(ir::ETSImportDeclaration *import);
[[nodiscard]] const ArenaVector<ir::ETSImportDeclaration *> &DynamicImports() const noexcept
{
return dynamicImports_;
}
void AddReExportImport(ir::ETSReExportDeclaration *reExport) noexcept
{
reExportImports_[reExport->Program()].push_back(reExport);
}
const auto &ReExportImports() const
{
return reExportImports_;
}
auto &ReExportImports()
{
return reExportImports_;
}
[[nodiscard]] const ir::AstNode *DefaultExport() noexcept
{
return defaultExport_;
}
void SetDefaultExport(ir::AstNode *defaultExport) noexcept
{
defaultExport_ = defaultExport;
}
void ResolveReferenceForScope(ir::AstNode *node, Scope *scope);
void ResolveReferencesForScope(ir::AstNode const *parent, Scope *scope);
void ResolveReferencesForScopeWithContext(ir::AstNode *node, Scope *scope);
[[nodiscard]] bool AddSelectiveExportAlias(parser::ETSParser *parser, util::StringView const &path,
util::StringView const &key, ir::Identifier const *valueIdent,
ir::AstNode const *decl) noexcept;
[[nodiscard]] const ModulesToExportedNamesWithAliases &GetSelectiveExportAliasMultimap() const noexcept
{
return *selectiveExportAliasMultimap_;
}
[[nodiscard]] ModulesToExportedNamesWithAliases &GetSelectiveExportAliasMultimap() noexcept
{
return *selectiveExportAliasMultimap_;
}
util::StringView FindNameInAliasMap(const util::StringView &pathAsKey, const util::StringView &aliasName);
std::pair<util::StringView, const ir::AstNode *> FindNameAndNodeInAliasMap(const util::StringView &pathAsKey,
const util::StringView &aliasName);
void CleanUp() override
{
VarBinder::CleanUp();
externalRecordTable_.clear();
InitImplicitThisParam();
dynamicImports_.clear();
reexportedNames_.clear();
reExportImports_.clear();
defaultExport_ = nullptr;
globalRecordTable_->CleanUp();
}
void CopyTo(VarBinder *target) override
{
auto targetImpl = reinterpret_cast<ETSBinder *>(target);
targetImpl->defaultImports_ = defaultImports_;
InitImplicitThisParam();
targetImpl->selectiveExportAliasMultimap_ = selectiveExportAliasMultimap_;
VarBinder::CopyTo(target);
}
private:
void BuildClassDefinitionImpl(ir::ClassDefinition *classDef);
void InitImplicitThisParam();
void HandleStarImport(ir::TSQualifiedName *importName, util::StringView fullPath);
void InsertForeignBinding(const util::StringView &name, Variable *var);
void InsertOrAssignForeignBinding(const util::StringView &name, Variable *var);
void ImportAllForeignBindings(const parser::Program *importedProgram);
void ThrowRedeclarationError(const lexer::SourcePosition &pos, const Variable *const var,
const Variable *const variable, util::StringView localName);
void ThrowReexportError(const parser::Program *importedProgram, const ir::ETSImportDeclaration *const import);
RecordTable *globalRecordTable_;
RecordTable *recordTable_;
ArenaMap<parser::Program *, RecordTable *> externalRecordTable_;
ArenaVector<ir::ETSImportDeclaration *> defaultImports_;
ArenaVector<ir::ETSImportDeclaration *> dynamicImports_;
ArenaMap<const parser::Program *, ArenaVector<ir::ETSReExportDeclaration *>> reExportImports_;
ArenaSet<util::StringView> reexportedNames_;
ir::Identifier *thisParam_ {};
ir::AstNode *defaultExport_ {};
ModulesToExportedNamesWithAliases *selectiveExportAliasMultimap_;
friend class RecordTableContext;
};
class RecordTableContext {
public:
RecordTableContext(ETSBinder *varBinder, parser::Program *extProgram)
: varBinder_(varBinder), savedRecordTable_(varBinder->recordTable_)
{
if (extProgram != nullptr &&
varBinder->externalRecordTable_.find(extProgram) != varBinder->externalRecordTable_.end()) {
varBinder->recordTable_ = varBinder->externalRecordTable_[extProgram];
} else if (varBinder->GetGlobalRecordTable()->Program() == extProgram) {
varBinder->recordTable_ = varBinder->GetGlobalRecordTable();
}
}
NO_COPY_SEMANTIC(RecordTableContext);
NO_MOVE_SEMANTIC(RecordTableContext);
~RecordTableContext()
{
varBinder_->recordTable_ = savedRecordTable_;
}
private:
ETSBinder *varBinder_;
RecordTable *savedRecordTable_;
};
}
#endif