* 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.
*/
#include "ETSBinder.h"
#include "es2panda.h"
#include "evaluate/scopedDebugInfoPlugin.h"
#include "public/public.h"
#include "compiler/lowering/util.h"
#include "util/helpers.h"
#include "util/nameMangler.h"
namespace ark::es2panda::varbinder {
void ETSBinder::IdentifierAnalysis()
{
ES2PANDA_ASSERT(Program()->Ast());
ES2PANDA_ASSERT(GetScope() == TopScope());
ES2PANDA_ASSERT(VarScope() == TopScope());
recordTable_->SetProgram(Program());
globalRecordTable_->SetClassDefinition(Program()->GlobalClass());
BuildProgram();
ES2PANDA_ASSERT(globalRecordTable_->ClassDefinition() == Program()->GlobalClass());
}
void ETSBinder::LookupTypeArgumentReferences(ir::ETSTypeReference *typeRef)
{
auto *iter = typeRef->Part();
if (typeRef->HasAnnotations()) {
for (auto *anno : typeRef->Annotations()) {
ResolveReference(anno);
}
}
while (iter != nullptr) {
if (iter->TypeParams() == nullptr) {
iter = iter->Previous();
continue;
}
ResolveReferences(iter->TypeParams());
iter = iter->Previous();
}
}
bool ETSBinder::IsSpecialName(const util::StringView &name)
{
constexpr std::array SPECIAL_KEYWORDS = {compiler::Signatures::ANY_TYPE_NAME, compiler::Signatures::ANY,
compiler::Signatures::UNDEFINED, compiler::Signatures::NULL_LITERAL};
constexpr std::array UTILITY_TYPES = {
compiler::Signatures::READONLY_TYPE_NAME, compiler::Signatures::PARTIAL_TYPE_NAME,
compiler::Signatures::REQUIRED_TYPE_NAME, compiler::Signatures::FIXED_ARRAY_TYPE_NAME,
compiler::Signatures::VALUE_ARRAY_TYPE_NAME, compiler::Signatures::AWAITED_TYPE_NAME,
compiler::Signatures::RETURN_TYPE_TYPE_NAME};
return std::find(SPECIAL_KEYWORDS.begin(), SPECIAL_KEYWORDS.end(), name.Utf8()) != SPECIAL_KEYWORDS.end() ||
std::find(UTILITY_TYPES.begin(), UTILITY_TYPES.end(), name.Utf8()) != UTILITY_TYPES.end();
}
static bool IsAnyOrUnknown(ETSBinder *binder, const util::StringView &name, const lexer::SourcePosition &pos)
{
if (name.Is("any") || name.Is("unknown")) {
binder->ThrowError(pos, diagnostic::ANY_UNKNOWN_TYPES);
return true;
}
return false;
}
bool ETSBinder::LookupInDebugInfoPlugin(ir::Identifier *ident)
{
auto *checker = GetContext()->GetChecker()->AsETSChecker();
auto *debugInfoPlugin = checker->GetDebugInfoPlugin();
if (UNLIKELY(debugInfoPlugin)) {
auto *var = debugInfoPlugin->FindClass(ident);
if (var != nullptr) {
ident->SetVariable(var);
return true;
}
}
return false;
}
static void CreateDummyVariable(ETSBinder *varBinder, ir::Identifier *ident)
{
auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, varBinder->VarScope());
auto [decl, var] =
varBinder->NewVarDecl<varbinder::LetDecl>(ident->Start(), compiler::GenName(varBinder->Allocator()).View());
var->SetScope(varBinder->GetScope());
ident->SetVariable(var);
ident->SetTsType(var->SetTsType(varBinder->GetContext()->GetChecker()->AsETSChecker()->GlobalTypeError()));
decl->BindNode(ident);
}
static bool IsInStaticMember(ir::AstNode *node)
{
if (node == nullptr || node->IsClassDefinition()) {
return false;
}
if (node->Parent() != nullptr && node->Parent()->IsClassDefinition()) {
return ((node->Modifiers() & ir::ModifierFlags::STATIC) != 0);
}
return IsInStaticMember(node->Parent());
}
static void CheckAndSetVariableReference(ETSBinder *varBinder, ir::Identifier *ident,
ark::es2panda::varbinder::Variable *resVar)
{
bool isIdentInStaticMethod = IsInStaticMember(ident);
bool isVarInStaticMethod = IsInStaticMember(resVar->Declaration()->Node());
if (isIdentInStaticMethod && !isVarInStaticMethod) {
varBinder->ThrowError(ident->Start(), diagnostic::STATIC_METHOD_CANNOT_REFERENCE_CLASS_TYPE, {ident->Name()});
} else {
ident->SetVariable(resVar);
}
}
void ETSBinder::LookupTypeReference(ir::Identifier *ident)
{
ES2PANDA_ASSERT(ident != nullptr);
if (ident->Variable() != nullptr && ident->Variable()->Declaration()->Node() == ident) {
return;
}
auto const &name = ident->Name();
if (IsSpecialName(name)) {
return;
}
if (ident->IsErrorPlaceHolder() || IsAnyOrUnknown(this, name, ident->Start())) {
CreateDummyVariable(this, ident);
return;
}
auto *scope = GetScope();
while (scope != nullptr) {
auto options = ResolveBindingOptions::DECLARATION | ResolveBindingOptions::TYPE_ALIASES |
ResolveBindingOptions::STATIC_DECLARATION;
auto res = scope->Find(name, options);
if (res.variable == nullptr) {
break;
}
switch (res.variable->Declaration()->Node()->Type()) {
case ir::AstNodeType::CLASS_DECLARATION:
case ir::AstNodeType::CLASS_DEFINITION:
case ir::AstNodeType::STRUCT_DECLARATION:
case ir::AstNodeType::TS_ENUM_DECLARATION:
case ir::AstNodeType::TS_INTERFACE_DECLARATION:
case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION:
case ir::AstNodeType::ANNOTATION_DECLARATION:
case ir::AstNodeType::IMPORT_NAMESPACE_SPECIFIER: {
ident->SetVariable(res.variable);
return;
}
case ir::AstNodeType::TS_TYPE_PARAMETER: {
CheckAndSetVariableReference(this, ident, res.variable);
return;
}
default: {
scope = scope->Parent();
}
}
}
if (LookupInDebugInfoPlugin(ident)) {
return;
}
ThrowUnresolvableType(ident->Start(), name);
CreateDummyVariable(this, ident);
}
void ETSBinder::ResolveReferencesForScope(ir::AstNode const *const parent, Scope *const scope)
{
parent->Iterate([this, scope](auto *node) { ResolveReferenceForScope(node, scope); });
}
void ETSBinder::ResolveReferenceForScope(ir::AstNode *const node, Scope *const scope)
{
switch (node->Type()) {
case ir::AstNodeType::IDENTIFIER: {
auto *ident = node->AsIdentifier();
if (ident->Variable() != nullptr) {
break;
}
if (auto const res = scope->Find(ident->Name(), ResolveBindingOptions::ALL); res.variable != nullptr) {
ident->SetVariable(res.variable);
}
break;
}
case ir::AstNodeType::VARIABLE_DECLARATOR: {
auto scopeCtx = LexicalScope<Scope>::Enter(this, scope);
BuildVarDeclarator(node->AsVariableDeclarator());
break;
}
case ir::AstNodeType::BLOCK_STATEMENT: {
auto scope_ctx = LexicalScope<Scope>::Enter(this, node->AsBlockStatement()->Scope());
ResolveReferences(node);
break;
}
*/
case ir::AstNodeType::BLOCK_EXPRESSION: {
auto scopeCtx = LexicalScope<Scope>::Enter(this, node->AsBlockExpression()->Scope());
ResolveReferences(node);
break;
}
case ir::AstNodeType::SCRIPT_FUNCTION: {
ResolveReferencesForScope(node, node->AsScriptFunction()->Scope());
break;
}
default: {
ResolveReferencesForScope(node, scope);
break;
}
}
}
void ETSBinder::ResolveReferencesForScopeWithContext(ir::AstNode *node, Scope *scope)
{
auto lexScope = LexicalScope<Scope>::Enter(this, scope);
ResolveReference(node);
}
bool ETSBinder::AddSelectiveExportAlias(parser::ETSParser *parser, util::StringView const &path,
util::StringView const &key, ir::Identifier const *valueIdent,
ir::AstNode const *decl) noexcept
{
const util::StringView &value = valueIdent->Name();
if (auto foundMap = selectiveExportAliasMultimap_->find(path); foundMap != selectiveExportAliasMultimap_->end()) {
auto inserted = foundMap->second.insert({key, std::make_pair(value, decl)}).second;
if (UNLIKELY(!inserted && foundMap->second.find(key)->second.first == value)) {
parser->DiagnosticEngine().LogDiagnostic(diagnostic::DUPLICATE_EXPORT_ALIASES,
util::DiagnosticMessageParams {key}, valueIdent->Start());
return true;
}
return inserted;
}
AliasesByExportedNames map;
bool insertResult = map.insert({key, std::make_pair(value, decl)}).second;
selectiveExportAliasMultimap_->insert({path, map});
return insertResult;
}
util::StringView ETSBinder::FindNameInAliasMap(const util::StringView &pathAsKey, const util::StringView &aliasName)
{
if (auto relatedMap = selectiveExportAliasMultimap_->find(pathAsKey);
relatedMap != selectiveExportAliasMultimap_->end()) {
if (auto item = relatedMap->second.find(aliasName); item != relatedMap->second.end()) {
return item->second.first;
}
}
return "";
}
std::pair<util::StringView, const ir::AstNode *> ETSBinder::FindNameAndNodeInAliasMap(const util::StringView &pathAsKey,
const util::StringView &aliasName)
{
if (auto relatedMap = selectiveExportAliasMultimap_->find(pathAsKey);
relatedMap != selectiveExportAliasMultimap_->end()) {
if (auto item = relatedMap->second.find(aliasName); item != relatedMap->second.end()) {
return item->second;
}
}
return std::pair<util::StringView, const ir::AstNode *>("", nullptr);
}
void ETSBinder::LookupIdentReference(ir::Identifier *ident)
{
if (ident->IsErrorPlaceHolder()) {
return;
}
const auto &name = ident->Name();
auto res = GetScope()->Find(name, ResolveBindingOptions::ALL);
if (res.level != 0) {
ES2PANDA_ASSERT(res.variable != nullptr);
ES2PANDA_ASSERT(GetScope()->EnclosingVariableScope() != nullptr);
auto *outerFunction = GetScope()->EnclosingVariableScope()->Node();
if ((!outerFunction->IsScriptFunction() || !outerFunction->AsScriptFunction()->IsArrow()) &&
!res.variable->IsGlobalVariable() && res.variable->HasFlag(VariableFlags::LOCAL) && res.level > 1) {
ThrowInvalidCapture(ident->Start(), name);
}
}
if (res.variable == nullptr) {
return;
}
if (ident->IsReference(Extension()) && res.variable->Declaration()->IsLetOrConstDecl() &&
!res.variable->HasFlag(VariableFlags::INITIALIZED) &&
!res.variable->HasFlag(VariableFlags::INIT_IN_STATIC_BLOCK)) {
ThrowTDZ(ident->Start(), name);
}
}
void ETSBinder::BuildClassProperty(const ir::ClassProperty *prop)
{
ResolveReferences(prop);
}
void ETSBinder::BuildETSTypeReference(ir::ETSTypeReference *typeRef)
{
auto *baseName = typeRef->BaseName();
ES2PANDA_ASSERT(baseName->IsReference(Extension()));
LookupTypeReference(baseName);
LookupTypeArgumentReferences(typeRef);
}
void ETSBinder::BuildObjectExpression(ir::ObjectExpression *obj)
{
for (auto expr : obj->Properties()) {
if (expr->IsProperty()) {
auto *prop = expr->AsProperty();
ResolveReference(prop->Value());
}
}
if (obj->TypeAnnotation() != nullptr) {
ResolveReference(obj->TypeAnnotation());
}
}
void ETSBinder::InitializeInterfaceIdent(ir::TSInterfaceDeclaration *decl)
{
auto res = GetScope()->Find(decl->Id()->Name());
ES2PANDA_ASSERT(res.variable && res.variable->Declaration()->IsInterfaceDecl());
res.variable->AddFlag(VariableFlags::INITIALIZED);
decl->Id()->SetVariable(res.variable);
}
void ETSBinder::ResolveEnumDeclaration(ir::TSEnumDeclaration *enumDecl)
{
auto enumScopeCtx = LexicalScope<LocalScope>::Enter(this, enumDecl->Scope());
for (auto *member : enumDecl->Members()) {
ResolveReference(member);
}
}
void ETSBinder::ResolveInterfaceDeclaration(ir::TSInterfaceDeclaration *decl)
{
auto boundCtx = BoundContext(recordTable_, decl);
for (auto *extend : decl->Extends()) {
ResolveReference(extend);
}
if (decl->HasAnnotations()) {
for (auto *anno : decl->Annotations()) {
ResolveReference(anno);
}
}
auto scopeCtx = LexicalScope<ClassScope>::Enter(this, decl->Scope()->AsClassScope());
for (auto *stmt : decl->Body()->Body()) {
if (!stmt->IsClassProperty()) {
continue;
}
ResolveReference(stmt);
ES2PANDA_ASSERT(stmt->AsClassProperty()->Id() != nullptr);
auto fieldVar =
ResolvePropertyReference(stmt->AsClassProperty(), decl->Scope()->AsClassScope())
->FindLocal(stmt->AsClassProperty()->Id()->Name(), varbinder::ResolveBindingOptions::BINDINGS);
ES2PANDA_ASSERT(fieldVar != nullptr);
fieldVar->AddFlag(VariableFlags::INITIALIZED);
}
for (auto *stmt : decl->Body()->Body()) {
if (stmt->IsClassProperty()) {
continue;
}
ResolveReference(stmt);
}
}
void ETSBinder::BuildInterfaceDeclaration(ir::TSInterfaceDeclaration *decl)
{
if (decl->TypeParams() != nullptr) {
auto typeParamScopeCtx = LexicalScope<LocalScope>::Enter(this, decl->TypeParams()->Scope());
ResolveReferences(decl->TypeParams());
ResolveInterfaceDeclaration(decl);
return;
}
ResolveInterfaceDeclaration(decl);
}
void ETSBinder::BuildMethodDefinition(ir::MethodDefinition *methodDef)
{
if (methodDef->BaseOverloadMethod() != nullptr &&
methodDef->GetTopStatement()->AsETSModule()->Program() != Program() &&
methodDef->BaseOverloadMethod()->GetTopStatement() != methodDef->GetTopStatement()) {
return;
}
ES2PANDA_ASSERT(methodDef->Function() != nullptr);
if (methodDef->Function()->TypeParams() != nullptr) {
auto scopeCtx = LexicalScope<LocalScope>::Enter(this, methodDef->Function()->TypeParams()->Scope());
ResolveReferences(methodDef->Function()->TypeParams());
}
ResolveMethodDefinition(methodDef);
}
void ETSBinder::BuildAnnotationDeclaration(ir::AnnotationDeclaration *annoDecl)
{
auto boundCtx = BoundContext(recordTable_, annoDecl);
if (annoDecl->Expr()->IsIdentifier()) {
LookupTypeReference(annoDecl->AsAnnotationDeclaration()->Expr()->AsIdentifier());
} else {
ResolveReference(annoDecl->Expr());
}
auto scopeCtx = LexicalScope<LocalScope>::Enter(this, annoDecl->Scope());
for (auto *property : annoDecl->Properties()) {
ResolveReference(property);
}
if (annoDecl->HasAnnotations()) {
for (auto *anno : annoDecl->Annotations()) {
ResolveReference(anno);
}
}
}
void ETSBinder::BuildAnnotationUsage(ir::AnnotationUsage *annoUsage)
{
if (annoUsage->Expr()->IsIdentifier()) {
LookupTypeReference(annoUsage->AsAnnotationUsage()->Expr()->AsIdentifier());
} else {
ResolveReference(annoUsage->Expr());
}
for (auto *property : annoUsage->Properties()) {
ResolveReference(property);
}
}
void ETSBinder::ResolveMethodDefinition(ir::MethodDefinition *methodDef)
{
methodDef->ResolveReferences([this](auto *childNode) { ResolveReference(childNode); });
auto *func = methodDef->Function();
ES2PANDA_ASSERT(func != nullptr);
if (func->HasAnnotations()) {
for (auto *anno : func->Annotations()) {
ResolveReference(anno);
}
}
if (methodDef->IsStatic() || func->IsStaticBlock()) {
return;
}
auto paramScopeCtx = LexicalScope<FunctionParamScope>::Enter(this, func->Scope()->ParamScope());
auto params = func->Scope()->ParamScope()->Params();
if (!params.empty() && params.front()->Name() == MANDATORY_PARAM_THIS && !func->HasReceiver()) {
return;
}
auto *thisParam = AddMandatoryParam(MANDATORY_PARAM_THIS);
ES2PANDA_ASSERT(thisParam != nullptr);
thisParam->Declaration()->BindNode(thisParam_);
}
void ETSBinder::BuildOverloadDeclaration(ir::OverloadDeclaration *overloadDef)
{
overloadDef->ResolveReferences([this](auto *childNode) { ResolveReference(childNode); });
}
void ETSBinder::BuildMemberExpression(ir::MemberExpression *memberExpr)
{
ResolveReference(memberExpr->Object());
if (memberExpr->Kind() == ir::MemberExpressionKind::ELEMENT_ACCESS) {
ResolveReference(memberExpr->Property());
}
}
void ETSBinder::BuildClassDefinition(ir::ClassDefinition *classDef)
{
auto boundCtx = BoundContext(recordTable_, classDef);
if (classDef->TypeParams() != nullptr) {
auto scopeCtx = LexicalScope<LocalScope>::Enter(this, classDef->TypeParams()->Scope());
ResolveReferences(classDef->TypeParams());
BuildClassDefinitionImpl(classDef);
return;
}
BuildClassDefinitionImpl(classDef);
}
LocalScope *ETSBinder::ResolvePropertyReference(ir::ClassProperty *prop, ClassScope *scope)
{
ResolveReferences(prop);
if (prop->IsStatic()) {
return scope->StaticFieldScope();
}
return scope->InstanceFieldScope();
}
void ETSBinder::BuildClassDefinitionImpl(ir::ClassDefinition *classDef)
{
auto classCtx = LexicalScope<ClassScope>::Enter(this, classDef->Scope()->AsClassScope());
if (classDef->Super() != nullptr) {
ResolveReference(classDef->Super());
}
for (auto *impl : classDef->Implements()) {
ResolveReference(impl);
}
if (classDef->HasAnnotations()) {
for (auto *anno : classDef->Annotations()) {
ResolveReference(anno);
}
}
for (auto *stmt : classDef->Body()) {
if (!stmt->IsClassProperty()) {
continue;
}
auto *const prop = stmt->AsClassProperty();
auto fieldScope = ResolvePropertyReference(prop, classDef->Scope()->AsClassScope());
ES2PANDA_ASSERT(prop->Id() != nullptr);
auto fieldName = prop->Id()->Name();
if (auto fieldVar = fieldScope->FindLocal(fieldName, varbinder::ResolveBindingOptions::BINDINGS);
fieldVar != nullptr) {
if (fieldVar->Declaration()->Node()->IsClassProperty() &&
fieldVar->Declaration()->Node()->AsClassProperty()->NeedInitInStaticBlock()) {
fieldVar->AddFlag(VariableFlags::INIT_IN_STATIC_BLOCK);
} else if (!fieldVar->Declaration()->Node()->IsDefinite()) {
fieldVar->AddFlag(VariableFlags::INITIALIZED);
}
if ((fieldVar->Declaration()->IsConstDecl() || fieldVar->Declaration()->IsReadonlyDecl()) &&
prop->Value() == nullptr) {
fieldVar->AddFlag(VariableFlags::EXPLICIT_INIT_REQUIRED);
}
} else {
ES2PANDA_ASSERT(GetContext()->diagnosticEngine->IsAnyError());
auto *checker = GetContext()->GetChecker()->AsETSChecker();
prop->SetTsType(checker->GlobalTypeError());
prop->Id()->SetTsType(checker->GlobalTypeError());
}
}
for (auto *stmt : classDef->Body()) {
if (stmt->IsClassProperty()) {
continue;
}
ResolveReference(stmt);
}
}
void ETSBinder::AddFunctionThisParam(ir::ScriptFunction *func)
{
auto paramScopeCtx = LexicalScope<FunctionParamScope>::Enter(this, func->Scope()->ParamScope());
auto *thisParam = AddMandatoryParam(MANDATORY_PARAM_THIS);
ES2PANDA_ASSERT(thisParam != nullptr);
thisParam->Declaration()->BindNode(thisParam_);
}
void ETSBinder::AddDynamicImport(ir::ETSImportDeclaration *import)
{
ES2PANDA_ASSERT(import->Language().IsDynamic());
dynamicImports_.push_back(import);
}
void ETSBinder::InsertForeignBinding(const util::StringView &name, Variable *var)
{
TopScope()->InsertForeignBinding(name, var);
}
void ETSBinder::InsertOrAssignForeignBinding(const util::StringView &name, Variable *var)
{
TopScope()->InsertOrAssignForeignBinding(name, var);
}
void ETSBinder::ThrowRedeclarationError(const lexer::SourcePosition &pos, const Variable *const var,
const Variable *const variable, util::StringView localName)
{
const bool isNamespace = var->Declaration()->Node()->IsClassDefinition() &&
var->Declaration()->Node()->AsClassDefinition()->IsNamespaceTransformed();
const auto type = isNamespace ? "Namespace"
: var->Declaration()->Node()->IsClassDefinition() ? "Class"
: var->Declaration()->IsFunctionDecl() ? "Function"
: "Variable";
if (variable->Declaration()->Type() == var->Declaration()->Type()) {
ThrowError(pos, diagnostic::REDEFINITION, {type, localName});
} else {
ThrowError(pos, diagnostic::REDEFINITION_DIFF_TYPE, {type, localName});
}
}
void AddOverloadFlag(ArenaAllocator *allocator, bool isStdLib, varbinder::Variable *importedVar,
varbinder::Variable *variable)
{
auto *const currentNode = variable->Declaration()->Node()->AsMethodDefinition();
auto *const method = importedVar->Declaration()->Node()->AsMethodDefinition();
auto const getPackageName = [](Variable *var) {
return var->GetScope()->Node()->GetTopStatement()->AsETSModule()->Program()->ModuleName();
};
if (isStdLib && (getPackageName(importedVar) != getPackageName(variable))) {
return;
}
ES2PANDA_ASSERT(method->Function() != nullptr);
if (!method->Overloads().empty() && !method->HasOverload(currentNode)) {
method->AddOverload(currentNode);
ES2PANDA_ASSERT(currentNode->Function() != nullptr);
currentNode->Function()->Id()->SetVariable(importedVar);
currentNode->Function()->AddFlag(ir::ScriptFunctionFlags::OVERLOAD);
currentNode->Function()->AddFlag(ir::ScriptFunctionFlags::EXTERNAL_OVERLOAD);
util::UString newInternalName(currentNode->Function()->Scope()->Name(), allocator);
currentNode->Function()->Scope()->BindInternalName(newInternalName.View());
return;
}
if (!currentNode->HasOverload(method)) {
currentNode->AddOverload(method);
if (method->Function()->Scope()->InternalName() == "") {
method->Function()->Id()->SetVariable(variable);
method->Function()->AddFlag(ir::ScriptFunctionFlags::OVERLOAD);
method->Function()->AddFlag(ir::ScriptFunctionFlags::EXTERNAL_OVERLOAD);
util::UString newInternalName(method->Function()->Scope()->Name(), allocator);
method->Function()->Scope()->BindInternalName(newInternalName.View());
}
}
}
void ETSBinder::ImportAllForeignBindings(const parser::Program *const importedProgram)
{
const auto *const importGlobalScope = importedProgram->GlobalScope();
bool const isStdLib = util::Helpers::IsStdLib(Program());
for (const auto [bindingName, var] : importGlobalScope->Bindings()) {
if (!var->Declaration()->Node()->IsValidInCurrentPhase()) {
continue;
}
if (util::Helpers::IsGlobalVar(var)) {
continue;
}
if (!importGlobalScope->IsForeignBinding(bindingName) && !var->Declaration()->Node()->IsDefaultExported() &&
(var->AsLocalVariable()->Declaration()->Node()->IsExported())) {
auto variable = Program()->GlobalClassScope()->FindLocal(bindingName, ResolveBindingOptions::ALL);
if (variable == nullptr || var == variable) {
InsertForeignBinding(bindingName, var);
continue;
}
if (variable->Declaration()->IsFunctionDecl() && var->Declaration()->IsFunctionDecl()) {
AddOverloadFlag(Allocator(), isStdLib, var, variable);
continue;
}
if (TopScope()->FindLocal(bindingName, ResolveBindingOptions::ALL) == nullptr) {
InsertForeignBinding(bindingName, var);
}
if (var->HasFlag(varbinder::VariableFlags::BUILTIN_TYPE)) {
TopScope()->CorrectForeignBinding(bindingName, var, variable);
}
ThrowRedeclarationError(variable->Declaration()->Node()->Start(), var, variable, bindingName);
}
}
for (const auto [bindingName, var] : importedProgram->GlobalClassScope()->StaticMethodScope()->Bindings()) {
if (!var->Declaration()->Node()->IsDefaultExported()) {
InsertForeignBinding(bindingName, var);
}
}
for (const auto [bindingName, var] : importedProgram->GlobalClassScope()->StaticFieldScope()->Bindings()) {
if (!var->Declaration()->Node()->IsDefaultExported()) {
InsertForeignBinding(bindingName, var);
}
}
}
void ETSBinder::AddImportNamespaceSpecifiersToTopBindings(parser::Program *const importedProgram,
ir::ImportNamespaceSpecifier *const namespaceSpecifier,
const ir::ETSImportDeclaration *const import)
{
if (namespaceSpecifier->Local()->Name().Empty()) {
ImportAllForeignBindings(importedProgram);
}
const auto &reexportImports = ReExportImports()[GetExternalProgram(import)];
for (auto *reexp : reexportImports) {
for (auto it : reexp->GetETSImportDeclarations()->Specifiers()) {
if (it->IsImportNamespaceSpecifier() && !namespaceSpecifier->Local()->Name().Empty()) {
continue;
}
AddSpecifiersToTopBindings(it, reexp->GetETSImportDeclarations());
}
}
}
Variable *ETSBinder::FindImportSpecifiersVariable(const util::StringView &imported,
parser::Program *const importedProgram)
{
const auto &globalBindings = importedProgram->GlobalScope()->Bindings();
auto foundVar = globalBindings.find(imported);
if (foundVar == globalBindings.end()) {
const auto &staticMethodBindings = importedProgram->GlobalClassScope()->StaticMethodScope()->Bindings();
foundVar = staticMethodBindings.find(imported);
if (foundVar != staticMethodBindings.end()) {
return foundVar->second;
}
const auto &staticDeclBindings = importedProgram->GlobalClassScope()->StaticDeclScope()->Bindings();
foundVar = staticDeclBindings.find(imported);
if (foundVar != staticDeclBindings.end()) {
return foundVar->second;
}
bool found = false;
importedProgram->MaybeIteratePackage([&found, &foundVar, imported](auto *packageFractionOrSelf) {
if (found) {
return;
}
const auto &staticFieldBindings = packageFractionOrSelf->GlobalClassScope()->StaticFieldScope()->Bindings();
foundVar = staticFieldBindings.find(imported);
if (found = foundVar != staticFieldBindings.end(); found) {
foundVar->second->AsLocalVariable()->AddFlag(VariableFlags::INITIALIZED);
}
});
if (!found) {
return nullptr;
}
}
return foundVar->second;
}
static bool IsExportedVariable(varbinder::Variable *const var)
{
return var != nullptr &&
(var->Declaration()->Node()->IsExported() || var->Declaration()->Node()->IsDefaultExported() ||
var->Declaration()->Node()->HasExportAlias());
}
std::pair<ir::ETSImportDeclaration *, ir::AstNode *> ETSBinder::FindImportDeclInExports(
const ir::ETSImportDeclaration *const import, const util::StringView &imported)
{
ir::ETSImportDeclaration *implDecl = nullptr;
ir::AstNode *specifier = nullptr;
std::tie(implDecl, specifier) = FindImportDeclInReExports(import, imported);
if (implDecl != nullptr) {
return std::make_pair(implDecl, specifier);
}
std::tie(implDecl, specifier) = FindImportDeclInNamedExports(import, imported);
return std::make_pair(implDecl, specifier);
}
static std::pair<ir::ETSImportDeclaration *, ir::AstNode *> FindImportDeclInProgram(parser::Program *program,
const util::StringView &imported)
{
for (auto stmt : program->Ast()->AsETSModule()->Statements()) {
if (!stmt->IsETSImportDeclaration()) {
continue;
}
for (auto specifier : stmt->AsETSImportDeclaration()->Specifiers()) {
if (specifier->IsImportSpecifier() && specifier->AsImportSpecifier()->Local()->Name() == imported) {
return std::make_pair(stmt->AsETSImportDeclaration(), specifier);
}
if (specifier->IsImportDefaultSpecifier() &&
specifier->AsImportDefaultSpecifier()->Local()->Name() == imported) {
return std::make_pair(stmt->AsETSImportDeclaration(), specifier);
}
}
}
return std::make_pair(nullptr, nullptr);
}
std::pair<ir::ETSImportDeclaration *, ir::AstNode *> ETSBinder::FindImportDeclInNamedExports(
const ir::ETSImportDeclaration *const import, [[maybe_unused]] const util::StringView &imported)
{
auto sourcePath = import->ImportInfo().HasSpecifiedDeclPath() ? import->DeclPath() : import->ResolvedSource();
auto importMapIter = selectiveExportAliasMultimap_->find(sourcePath);
if (importMapIter == selectiveExportAliasMultimap_->end()) {
return std::make_pair(nullptr, nullptr);
}
auto pairIter = importMapIter->second.find(imported);
if (pairIter == importMapIter->second.end()) {
return std::make_pair(nullptr, nullptr);
}
auto [localName, declNode] = pairIter->second;
auto *currProgram = GetExternalProgram(import);
if (currProgram == nullptr) {
return std::make_pair(nullptr, nullptr);
}
auto [newImportDecl, specfier] = FindImportDeclInProgram(currProgram, localName);
if (newImportDecl == nullptr) {
return std::make_pair(nullptr, nullptr);
}
return std::make_pair(newImportDecl->AsETSImportDeclaration(), specfier);
}
static ir::AstNode *GetSpecifier(const util::StringView &importedLocal, ir::ETSImportDeclaration *decl)
{
for (auto localSpecfier : decl->Specifiers()) {
util::StringView name;
if (localSpecfier->IsImportSpecifier()) {
name = localSpecfier->AsImportSpecifier()->Local()->Name();
} else if (localSpecfier->IsImportNamespaceSpecifier()) {
name = localSpecfier->AsImportNamespaceSpecifier()->Local()->Name();
} else {
name = localSpecfier->AsImportDefaultSpecifier()->Local()->Name();
}
if (name == importedLocal) {
return localSpecfier;
}
}
if (decl->Specifiers().size() == 1 && decl->Specifiers()[0]->IsImportNamespaceSpecifier()) {
return decl->Specifiers()[0];
}
ES2PANDA_UNREACHABLE();
}
std::pair<ir::ETSImportDeclaration *, ir::AstNode *> ETSBinder::FindImportDeclInReExports(
const ir::ETSImportDeclaration *const import, const util::StringView &imported)
{
ir::ETSImportDeclaration *implDecl = nullptr;
ir::AstNode *specifier = nullptr;
const auto &reexportImports = ReExportImports()[GetExternalProgram(import)];
for (auto *item : reexportImports) {
auto specifiers = item->GetETSImportDeclarations()->Specifiers();
if (specifiers.empty()) {
continue;
}
if (specifiers[0]->IsImportSpecifier()) {
if (!std::any_of(specifiers.begin(), specifiers.end(), [&imported](auto it) {
return it->AsImportSpecifier()->Local()->Name().Is(imported.Mutf8());
})) {
continue;
}
implDecl = item->GetETSImportDeclarations();
specifier = GetSpecifier(imported, implDecl);
} else {
auto *importedProgram = GetExternalProgram(item->GetETSImportDeclarations());
if (importedProgram == nullptr) {
continue;
}
auto *const var = FindImportSpecifiersVariable(imported, importedProgram);
if (IsExportedVariable(var)) {
implDecl = item->GetETSImportDeclarations();
specifier = GetSpecifier(imported, implDecl);
return std::make_pair(implDecl, specifier);
}
auto reExportImport = item->GetETSImportDeclarations();
auto [implDeclOrNullptr, localSpecifier] = FindImportDeclInExports(reExportImport, imported);
if (implDeclOrNullptr != nullptr) {
implDecl = implDeclOrNullptr;
specifier = GetSpecifier(imported, implDecl);
}
}
}
return std::make_pair(implDecl, specifier);
}
void ETSBinder::ValidateImportVariable(const ir::AstNode *node, const ir::AstNode *declNode,
const util::StringView &imported, const ir::ETSImportDeclaration *const import)
{
if (node->IsDefaultExported() && !declNode->IsExported() &&
imported != compiler::Signatures::REEXPORT_DEFAULT_ANONYMOUSLY) {
ThrowError(import->Start(), diagnostic::DEFAULT_EXPORT_DIRECT_IMPORTED);
} else if (!node->IsExported() && !node->IsDefaultExported() && !node->HasExportAlias() &&
!node->IsImportSpecifier() && !node->IsETSImportDeclaration()) {
ThrowError(import->Start(), diagnostic::IMPORTED_NOT_EXPORTED, {imported});
}
}
bool ETSBinder::DetectNameConflict(const util::StringView localName, Variable *const var, Variable *const otherVar,
ir::Identifier const *local)
{
if (otherVar == nullptr || var == otherVar) {
return false;
}
if (var->Declaration()->IsFunctionDecl() && otherVar->Declaration()->IsFunctionDecl()) {
ThrowError(local->Start(), diagnostic::OVERLOADED_FUNCS_MUST_BE_IN_SAME_SCOPE);
return true;
}
bool isAmbient = var->Declaration()->Node()->IsDeclare() && !otherVar->Declaration()->Node()->IsDeclare();
if (isAmbient) {
return false;
}
ThrowRedeclarationError(local->Start(), var, otherVar, localName);
return true;
}
Variable *ETSBinder::AddImportSpecifierFromReExport(ir::AstNode *importSpecifier,
const ir::ETSImportDeclaration *const import,
const util::StringView &imported)
{
auto [implDecl, localSpecifier] = FindImportDeclInExports(import, imported);
Variable *localVar = nullptr;
auto insertBinding = [this, importSpecifier](Variable *var) {
if (importSpecifier->IsImportSpecifier()) {
this->InsertOrAssignForeignBinding(importSpecifier->AsImportSpecifier()->Local()->Name(), var);
importSpecifier->AsImportSpecifier()->Local()->SetVariable(var);
importSpecifier->AsImportSpecifier()->Imported()->SetVariable(var);
} else if (importSpecifier->IsImportDefaultSpecifier()) {
this->InsertOrAssignForeignBinding(importSpecifier->AsImportDefaultSpecifier()->Local()->Name(), var);
importSpecifier->AsImportDefaultSpecifier()->Local()->SetVariable(var);
}
};
if (implDecl != nullptr) {
if (localSpecifier->IsImportSpecifier() || localSpecifier->IsImportDefaultSpecifier()) {
std::optional<GlobalScopeContext> scopeCtx;
ir::AstNode *global = localSpecifier;
while (global != nullptr && !global->IsETSModule()) {
global = global->Parent();
}
if (global != nullptr) {
scopeCtx.emplace(this, global->AsETSModule()->Program(), global->Scope()->AsGlobalScope());
}
AddSpecifiersToTopBindings(localSpecifier, implDecl);
if (localSpecifier->IsImportSpecifier()) {
localVar = localSpecifier->AsImportSpecifier()->Imported()->Variable();
insertBinding(localVar);
} else if (localSpecifier->IsImportDefaultSpecifier()) {
localVar = localSpecifier->AsImportDefaultSpecifier()->Local()->Variable();
insertBinding(localVar);
}
} else {
AddSpecifiersToTopBindings(importSpecifier, implDecl);
}
return localVar;
}
ThrowError(importSpecifier->Start(), diagnostic::IMPORT_NOT_FOUND, {imported});
return nullptr;
}
util::StringView ETSBinder::GetAdjustedImportedName(ir::ImportSpecifier *const importSpecifier,
const ir::ETSImportDeclaration *const import)
{
auto imported = importSpecifier->Imported()->Name();
for (auto const item : import->Specifiers()) {
if (item->IsImportSpecifier() && item->AsImportSpecifier()->Local()->Name().Is(imported.Mutf8()) &&
!item->AsImportSpecifier()->Local()->Name().Is(item->AsImportSpecifier()->Imported()->Name().Mutf8())) {
imported = item->AsImportSpecifier()->Imported()->Name();
}
}
return imported;
}
bool ETSBinder::AddImportSpecifiersToTopBindings(parser::Program *const importedProgram,
ir::ImportSpecifier *const importSpecifier,
const ir::ETSImportDeclaration *const import)
{
if (!importSpecifier->Imported()->IsIdentifier()) {
return true;
}
auto imported = GetAdjustedImportedName(importSpecifier, import);
auto sourcePath = import->ImportInfo().HasSpecifiedDeclPath() ? import->DeclPath() : import->ResolvedSource();
auto [nameToSearchFor, exportNode] = FindNameAndNodeInAliasMap(sourcePath, imported);
if (nameToSearchFor.Empty()) {
nameToSearchFor = imported;
}
auto *var = FindImportSpecifiersVariable(nameToSearchFor, importedProgram);
bool varFoundThroughReExport = false;
if (var == nullptr) {
var = AddImportSpecifierFromReExport(importSpecifier, import, imported);
varFoundThroughReExport = var != nullptr;
}
if (var == nullptr) {
return false;
}
importSpecifier->Imported()->SetVariable(var);
ir::Identifier *const local = importSpecifier->Local();
local->SetVariable(var);
auto [implDecl, localSpecifier] = FindImportDeclInReExports(import, imported);
if (localSpecifier != nullptr) {
exportNode = localSpecifier;
}
ValidateImportVariable(exportNode != nullptr ? exportNode : var->Declaration()->Node(), var->Declaration()->Node(),
imported, import);
const auto &localName = local->Name();
auto varInGlobalClassScope = Program()->GlobalClassScope()->FindLocal(localName, ResolveBindingOptions::ALL);
auto previouslyImportedVariable = TopScope()->FindLocal(localName, ResolveBindingOptions::ALL);
if (DetectNameConflict(localName, var, varInGlobalClassScope, local) ||
DetectNameConflict(localName, var, previouslyImportedVariable, local)) {
return true;
}
if (var->Declaration()->Node()->IsAnnotationDeclaration() &&
var->Declaration()->Node()->AsAnnotationDeclaration()->GetBaseName()->Name() != localName) {
ThrowError(import->Start(), diagnostic::IMPORT_RENAMES_ANNOTATION, {var->Declaration()->Name()});
return false;
}
if ((exportNode == nullptr && !varFoundThroughReExport) && var->Declaration()->Node()->HasExportAlias() &&
!var->Declaration()->Node()->IsExported()) {
ThrowError(importSpecifier->Start(), diagnostic::IMPORT_NOT_FOUND, {imported});
return false;
}
InsertOrAssignForeignBinding(localName, var);
return true;
}
using GlobalBindingItem = std::pair<const util::StringView, std::pair<util::StringView, ir::AstNode const *>>;
static bool IsDefaultExported(const GlobalBindingItem &item)
{
if (item.second.second == nullptr) {
return false;
}
if (item.second.second->IsDefaultExported()) {
return true;
}
if (!item.second.second->IsETSImportDeclaration()) {
return false;
}
auto *specifier = item.second.second->AsETSImportDeclaration()->Specifiers()[0];
util::StringView name;
if (specifier->IsImportSpecifier()) {
name = specifier->AsImportSpecifier()->Local()->Name();
} else if (specifier->IsImportNamespaceSpecifier()) {
name = specifier->AsImportNamespaceSpecifier()->Local()->Name();
} else {
ES2PANDA_UNREACHABLE();
}
return name == compiler::Signatures::REEXPORT_DEFAULT_ANONYMOUSLY;
}
void ETSBinder::AddImportDefaultSpecifiersToTopBindings(parser::Program *const importedProgram,
ir::ImportDefaultSpecifier *const importDefaultSpecifier,
const ir::ETSImportDeclaration *const import)
{
ES2PANDA_ASSERT(&importedProgram->VarBinder()->AsETSBinder()->GetSelectiveExportAliasMultimap() ==
&GetSelectiveExportAliasMultimap());
const auto &selectMap = GetSelectiveExportAliasMultimap();
ir::Identifier *const local = importDefaultSpecifier->Local();
const auto &localName = local->Name();
auto selectMap2 = selectMap.find(import->ResolvedSource());
if (selectMap2 != selectMap.end()) {
auto item1 = std::find_if(selectMap2->second.begin(), selectMap2->second.end(), IsDefaultExported);
if (item1 != selectMap2->second.end()) {
auto var = FindImportSpecifiersVariable(item1->first, importedProgram);
if (var == nullptr) {
var = AddImportSpecifierFromReExport(importDefaultSpecifier, import, item1->first);
}
if (var == nullptr) {
return;
}
local->SetVariable(var);
InsertOrAssignForeignBinding(localName, var);
return;
}
}
if (auto var = FindImportSpecifiersVariable(compiler::Signatures::REEXPORT_DEFAULT_ANONYMOUSLY, importedProgram);
var != nullptr) {
local->SetVariable(var);
InsertForeignBinding(localName, var);
return;
}
if (auto var = FindStaticBinding(importedProgram, import); var != nullptr) {
local->SetVariable(var);
var->AsLocalVariable()->AddFlag(VariableFlags::INITIALIZED);
auto varInGlobalClassScope = Program()->GlobalClassScope()->FindLocal(localName, ResolveBindingOptions::ALL);
auto previouslyImportedVariable = TopScope()->FindLocal(localName, ResolveBindingOptions::ALL);
if (DetectNameConflict(localName, var, varInGlobalClassScope, local) ||
DetectNameConflict(localName, var, previouslyImportedVariable, local)) {
return;
}
InsertForeignBinding(localName, var);
return;
}
}
static Variable *FindInStatic(parser::Program *program)
{
auto predicateFunc = [](const auto &item) { return item.second->Declaration()->Node()->IsDefaultExported(); };
const auto &staticMethodBindings = program->GlobalClassScope()->StaticMethodScope()->Bindings();
auto result = std::find_if(staticMethodBindings.begin(), staticMethodBindings.end(), predicateFunc);
if (result == staticMethodBindings.end()) {
const auto &staticFieldBindings = program->GlobalClassScope()->StaticFieldScope()->Bindings();
result = std::find_if(staticFieldBindings.begin(), staticFieldBindings.end(), predicateFunc);
if (result == staticFieldBindings.end()) {
const auto &staticDeclBindings = program->GlobalClassScope()->StaticDeclScope()->Bindings();
result = std::find_if(staticDeclBindings.begin(), staticDeclBindings.end(), predicateFunc);
if (result == staticDeclBindings.end()) {
return nullptr;
}
}
}
return result->second;
}
static Variable *FindInInstance(parser::Program *program)
{
auto predicateFunc = [](const auto &item) {
return item.second->Declaration()->Node()->IsValidInCurrentPhase() &&
item.second->Declaration()->Node()->IsDefaultExported();
};
const auto &instanceMethodBindings = program->GlobalClassScope()->InstanceMethodScope()->Bindings();
auto result = std::find_if(instanceMethodBindings.begin(), instanceMethodBindings.end(), predicateFunc);
if (result == instanceMethodBindings.end()) {
const auto &instanceFieldBindings = program->GlobalClassScope()->InstanceFieldScope()->Bindings();
result = std::find_if(instanceFieldBindings.begin(), instanceFieldBindings.end(), predicateFunc);
if (result == instanceFieldBindings.end()) {
const auto &instanceDeclBindings = program->GlobalClassScope()->InstanceDeclScope()->Bindings();
result = std::find_if(instanceDeclBindings.begin(), instanceDeclBindings.end(), predicateFunc);
if (result == instanceDeclBindings.end()) {
return nullptr;
}
}
}
return result->second;
}
varbinder::Variable *ETSBinder::FindStaticBinding(parser::Program *const importedProgram,
const ir::ETSImportDeclaration *const import)
{
auto result = FindInInstance(importedProgram);
if (result != nullptr) {
return result;
}
result = FindInStatic(importedProgram);
if (result != nullptr) {
return result;
}
ThrowError(import->Start(), diagnostic::DEFAULT_IMPORT_NOT_FOUND);
return nullptr;
}
parser::Program *ETSBinder::GetExternalProgram(const ir::ETSImportDeclaration *import)
{
auto importee = GetContext()->parser->GetImportPathManager()->SearchResolved(import->ImportInfo());
if (importee == nullptr) {
if (ark::os::file::File::IsDirectory(std::string(import->ResolvedSource()))) {
ThrowError(import->Start(), diagnostic::MODULE_INDEX_MISSING, {import->ResolvedSource()});
} else {
ThrowError(import->Start(), diagnostic::IMPORT_NOT_FOUND_2, {import->ResolvedSource()});
}
}
return importee;
}
void ETSBinder::AddSpecifiersToTopBindings(ir::AstNode *const specifier, const ir::ETSImportDeclaration *const import)
{
auto *importedProgram = GetExternalProgram(import);
if (importedProgram == nullptr || importedProgram->Ast() == nullptr || importedProgram->Ast()->Scope() == nullptr) {
return;
}
if (specifier->IsImportNamespaceSpecifier()) {
AddImportNamespaceSpecifiersToTopBindings(importedProgram, specifier->AsImportNamespaceSpecifier(), import);
} else if (specifier->IsImportSpecifier()) {
AddImportSpecifiersToTopBindings(importedProgram, specifier->AsImportSpecifier(), import);
} else if (specifier->IsImportDefaultSpecifier()) {
AddImportDefaultSpecifiersToTopBindings(importedProgram, specifier->AsImportDefaultSpecifier(), import);
}
}
void ETSBinder::HandleCustomNodes(ir::AstNode *childNode)
{
switch (childNode->Type()) {
case ir::AstNodeType::ETS_TYPE_REFERENCE: {
return BuildETSTypeReference(childNode->AsETSTypeReference());
}
case ir::AstNodeType::TS_INTERFACE_DECLARATION: {
return BuildInterfaceDeclaration(childNode->AsTSInterfaceDeclaration());
}
case ir::AstNodeType::TS_ENUM_DECLARATION: {
return ResolveEnumDeclaration(childNode->AsTSEnumDeclaration());
}
case ir::AstNodeType::EXPORT_NAMED_DECLARATION: {
break;
}
case ir::AstNodeType::ETS_IMPORT_DECLARATION: {
return BuildImportDeclaration(childNode->AsETSImportDeclaration());
}
case ir::AstNodeType::MEMBER_EXPRESSION: {
return BuildMemberExpression(childNode->AsMemberExpression());
}
case ir::AstNodeType::METHOD_DEFINITION: {
return BuildMethodDefinition(childNode->AsMethodDefinition());
}
case ir::AstNodeType::OVERLOAD_DECLARATION: {
return BuildOverloadDeclaration(childNode->AsOverloadDeclaration());
}
case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: {
return BuildETSNewClassInstanceExpression(childNode->AsETSNewClassInstanceExpression());
}
case ir::AstNodeType::ETS_FUNCTION_TYPE: {
return BuildSignatureDeclarationBaseParams(childNode);
}
case ir::AstNodeType::OBJECT_EXPRESSION: {
return BuildObjectExpression(childNode->AsObjectExpression());
}
case ir::AstNodeType::ANNOTATION_USAGE: {
return BuildAnnotationUsage(childNode->AsAnnotationUsage());
}
case ir::AstNodeType::ANNOTATION_DECLARATION: {
BuildAnnotationDeclaration(childNode->AsAnnotationDeclaration());
break;
}
default: {
return ResolveReferences(childNode);
}
}
}
bool ETSBinder::BuildInternalName(ir::ScriptFunction *scriptFunc)
{
const bool isExternal = recordTable_->IsExternal();
if (isExternal) {
scriptFunc->AddFlag(ir::ScriptFunctionFlags::EXTERNAL);
}
if (scriptFunc->IsArrow()) {
return true;
}
auto *funcScope = scriptFunc->Scope();
funcScope->BindName(recordTable_->RecordName());
return scriptFunc->Body() != nullptr && !isExternal;
}
bool ETSBinder::BuildInternalNameWithCustomRecordTable(ir::ScriptFunction *const scriptFunc,
RecordTable *const recordTable)
{
const bool isExternal = recordTable->IsExternal();
if (isExternal) {
scriptFunc->AddFlag(ir::ScriptFunctionFlags::EXTERNAL);
}
if (scriptFunc->IsArrow()) {
return true;
}
auto *const funcScope = scriptFunc->Scope();
funcScope->BindName(recordTable->RecordName());
return scriptFunc->Body() != nullptr && !isExternal;
}
void ETSBinder::AddCompilableFunction(ir::ScriptFunction *func)
{
* NOTE(knazarov) Here it is important to leave IsAsyncFunc, since
* for stackless we need to omit compilation of these 'native' funcs
*/
if (!GetContext()->config->options->IsStacklessCoros() && func->IsAsyncFunc()) {
return;
}
if (func->IsArrow()) {
return;
}
if (GetContext()->config->options->GetCompilationMode() >= CompilationMode::SIMULTANEOUS &&
func->Scope()->Name().Is(compiler::Signatures::ETS_GLOBAL)) {
return;
}
AddCompilableFunctionScope(func->Scope());
}
void ETSBinder::BuildFunctionName(const ir::ScriptFunction *func) const
{
auto *funcScope = func->Scope();
std::stringstream ss;
ES2PANDA_ASSERT(func->IsArrow() || !funcScope->Name().Empty());
ss << (func->IsExternalOverload() ? funcScope->InternalName() : funcScope->Name())
<< compiler::Signatures::METHOD_SEPARATOR;
const auto *signature = func->Signature();
const auto funcName = util::Helpers::FunctionName(Allocator(), func);
if (func->IsStaticBlock()) {
ss << compiler::Signatures::CCTOR;
} else if (func->IsConstructor() && funcName.Is(compiler::Signatures::CONSTRUCTOR_NAME)) {
ss << compiler::Signatures::CTOR;
} else {
std::string newName;
if (func->IsGetter()) {
newName = util::NameMangler::GetInstance()->CreateMangledNameByTypeAndName(
util::NameMangler::GET, util::Helpers::FunctionName(Allocator(), func));
ss << newName;
} else if (func->IsSetter()) {
newName = util::NameMangler::GetInstance()->CreateMangledNameByTypeAndName(
util::NameMangler::SET, util::Helpers::FunctionName(Allocator(), func));
ss << newName;
} else {
ss << funcName;
}
}
signature->ToAssemblerType(ss);
auto newName = ss.str();
if (funcScope->InternalName().Utf8() == newName) {
return;
}
funcScope->BindInternalName(util::UString(newName, Allocator()).View());
}
void ETSBinder::InitImplicitThisParam()
{
thisParam_ = Allocator()->New<ir::Identifier>("this", Allocator());
}
inline constexpr std::string_view STD_PREFIX = "std.";
static void TraverseAST(ETSBinder *binder, ArenaVector<ir::ETSImportDeclaration *> &defaultImports)
{
for (auto *defaultImport : defaultImports) {
binder->BuildImportDeclaration(defaultImport);
}
auto &stmts = binder->Program()->Ast()->StatementsForUpdates();
if (binder->Program()->GetImportInfo().ModuleName() == compiler::Signatures::SIMULT_MODULE_NAME) {
stmts.clear();
return;
}
const auto etsGlobal = std::find_if(stmts.begin(), stmts.end(), [](const ir::Statement *stmt) {
return stmt->IsClassDeclaration() && stmt->AsClassDeclaration()->Definition()->IsGlobal();
});
if (etsGlobal != stmts.end()) {
const auto begin = std::find_if(stmts.rbegin(), stmts.rend(), [](const ir::Statement *stmt) {
return stmt->IsETSImportDeclaration() || stmt->IsETSPackageDeclaration();
}).base();
const auto index = std::distance(begin, etsGlobal);
std::rotate(begin, begin + index, begin + index + 1);
}
for (auto *stmt : stmts) {
binder->ResolveReference(stmt);
}
}
void ETSBinder::BuildProgram()
{
Program()->SetRecordTable(globalRecordTable_);
Program()->GetExternalDecls()->Visit([this](auto *extProg) {
if (extProg->ModuleName().substr(0, STD_PREFIX.length()) == STD_PREFIX) {
BuildExternalProgram(extProg);
}
});
Program()->GetExternalDecls()->Visit([this](auto *extProg) {
if (extProg->ModuleName().substr(0, STD_PREFIX.length()) != STD_PREFIX) {
BuildExternalProgram(extProg);
}
});
TraverseAST(this, defaultImports_);
ValidateReexports();
}
void ETSBinder::BuildExternalProgram(parser::Program *extProgram)
{
auto *savedProgram = Program();
auto *savedRecordTable = recordTable_;
auto *savedTopScope = TopScope();
auto flags = Program()->VarBinder()->IsGenStdLib() || (extProgram->IsBuiltSimultaneously())
? RecordTableFlags::NONE
: RecordTableFlags::EXTERNAL;
auto *extRecordTable = Allocator()->New<RecordTable>(Allocator(), extProgram, flags);
extRecordTable->SetClassDefinition(extProgram->GlobalClass());
externalRecordTable_.insert({extProgram, extRecordTable});
ResetTopScope(extProgram->GlobalScope());
recordTable_ = extRecordTable;
extProgram->SetRecordTable(extRecordTable);
SetProgram(extProgram);
if (extProgram->IsASTLowered() || !extProgram->IsProgramModified()) {
extRecordTable->Merge(extProgram->VarBinder()->AsETSBinder()->GetExternalRecordTable().at(extProgram));
} else {
TraverseAST(this, defaultImports_);
}
ValidateReexports();
SetProgram(savedProgram);
recordTable_ = savedRecordTable;
ResetTopScope(savedTopScope);
}
bool ETSBinder::CheckRecordTablesConsistency(parser::Program *program ) const
{
bool ok {true};
auto mainProg = GetContext()->parserProgram;
ok &= (GetExternalRecordTable().find(mainProg) == GetExternalRecordTable().cend());
ok &= (mainProg->GetRecordTable() == GetGlobalRecordTable());
if (program == nullptr) {
mainProg->GetExternalDecls()->Visit([this, &ok](auto *extProgram) {
ok &= (extProgram->GetRecordTable() == GetExternalRecordTable().find(extProgram)->second);
ok &= (extProgram->GetRecordTable() != GetGlobalRecordTable());
});
} else if (program != mainProg) {
ok &= (program->GetRecordTable() == GetExternalRecordTable().find(program)->second);
ok &= (program->GetRecordTable() != GetGlobalRecordTable());
}
return ok;
}
void ETSBinder::BuildETSNewClassInstanceExpression(ir::ETSNewClassInstanceExpression *classInstance)
{
ResolveReference(classInstance->GetTypeRef());
for (auto *arg : classInstance->GetArguments()) {
ResolveReference(arg);
}
}
void ETSBinder::BuildImportDeclaration(ir::ETSImportDeclaration *decl)
{
if (!decl->IsValid()) {
return;
}
const auto &specifiers = decl->Specifiers();
for (auto specifier : specifiers) {
AddSpecifiersToTopBindings(specifier, decl);
}
}
Variable *ETSBinder::ValidateImportSpecifier(const ir::ImportSpecifier *const specifier,
const ir::ETSImportDeclaration *const import)
{
auto *importedProgram = GetExternalProgram(import);
if (importedProgram == nullptr) {
return nullptr;
}
auto imported = specifier->Imported()->Name();
for (const auto *const item : import->Specifiers()) {
if (item->IsImportSpecifier() && item->AsImportSpecifier()->Local()->Name().Is(imported.Mutf8()) &&
!item->AsImportSpecifier()->Local()->Name().Is(item->AsImportSpecifier()->Imported()->Name().Mutf8())) {
imported = item->AsImportSpecifier()->Imported()->Name();
}
if (auto foundAlias = FindNameInAliasMap(importedProgram->AbsoluteName(), imported); !foundAlias.Is("")) {
imported = foundAlias;
}
}
auto *var = FindImportSpecifiersVariable(imported, importedProgram);
if (var != nullptr) {
return var;
}
auto [implDecl, localSpecifier] = FindImportDeclInExports(import, imported);
if (implDecl != nullptr) {
return ValidateImportSpecifier(specifier, implDecl);
}
if (var = FindStaticBinding(importedProgram, import); var != nullptr) {
InsertForeignBinding(specifier->Local()->Name(), var);
return var;
}
return nullptr;
}
void ETSBinder::ValidateReexports()
{
const auto &reexports = ReExportImports()[Program()];
for (auto *reexport : reexports) {
ValidateReexportDeclaration(reexport);
}
reexportedNames_.clear();
}
void ETSBinder::ThrowReexportError(const parser::Program *importedProgram, const ir::ETSImportDeclaration *const import)
{
const auto *const importGlobalScope = importedProgram->GlobalScope();
for (const auto &[bindingName, var] : importGlobalScope->Bindings()) {
if (!var->Declaration()->Node()->IsValidInCurrentPhase() || util::Helpers::IsGlobalVar(var)) {
continue;
}
if (!importGlobalScope->IsForeignBinding(bindingName) && !var->Declaration()->Node()->IsDefaultExported() &&
var->AsLocalVariable()->Declaration()->Node()->IsExported()) {
if (!reexportedNames_.insert(bindingName).second) {
ThrowError(import->Start(), diagnostic::ALREADY_EXPORTED, {bindingName});
}
}
}
}
void ETSBinder::ValidateReexportDeclaration(ir::ETSReExportDeclaration *decl)
{
ES2PANDA_ASSERT(Program() == decl->Program());
const auto *const import = decl->GetETSImportDeclarations();
const auto &specifiers = import->Specifiers();
for (auto const specifier : specifiers) {
if (specifier->IsImportSpecifier()) {
auto importSpecifier = specifier->AsImportSpecifier();
auto imported = importSpecifier->Imported();
auto local = importSpecifier->Local();
auto *const var = ValidateImportSpecifier(importSpecifier, import);
if (var == nullptr) {
ThrowError(import->Start(), diagnostic::EXPORT_INCORRECT, {imported->Name()});
continue;
}
imported->SetVariable(var);
local->SetVariable(var);
if (!reexportedNames_.insert(local->Name()).second) {
ThrowError(import->Start(), diagnostic::AMBIGUOUS_EXPORT, {local->Name()});
continue;
}
}
if (specifier->IsImportNamespaceSpecifier()) {
auto *importedProgram = GetExternalProgram(import);
if (importedProgram == nullptr || !specifier->AsImportNamespaceSpecifier()->Local()->Name().Empty()) {
continue;
}
ThrowReexportError(importedProgram, import);
}
}
}
void ETSBinder::ThrowError(const lexer::SourcePosition &pos, const diagnostic::DiagnosticKind &kind,
const util::DiagnosticMessageParams ¶ms) const
{
GetContext()->diagnosticEngine->LogDiagnostic(kind, params, pos);
}
bool ETSBinder::IsGlobalIdentifier([[maybe_unused]] const util::StringView &str) const
{
return false;
}
}