/** * Copyright (c) 2021 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 "scope.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace panda::es2panda::binder { VariableScope *Scope::EnclosingVariableScope() { Scope *iter = this; while (iter) { if (iter->IsVariableScope()) { return iter->AsVariableScope(); } iter = iter->Parent(); } return nullptr; } FunctionScope *Scope::EnclosingFunctionVariableScope() { Scope *iter = this; while (iter) { if (iter->IsFunctionVariableScope()) { return iter->AsFunctionVariableScope(); } iter = iter->Parent(); } return nullptr; } Variable *Scope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const { if (options & ResolveBindingOptions::INTERFACES) { const std::string &interfaceName = binder::TSBinding::ToTSBinding(name); auto res = bindings_.find(util::StringView{interfaceName}); if (res != bindings_.end()) { return res->second; } if (!(options & ResolveBindingOptions::BINDINGS)) { return nullptr; } } auto res = bindings_.find(name); if (res == bindings_.end()) { return nullptr; } return res->second; } void Scope::CalculateLevelInCorrespondingFunctionScope(const FunctionParamScope *scope, uint32_t &lexLevel, uint32_t &sendableLevel) const { auto *funcVariableScope = scope->GetFunctionScope(); // we may only have function param scope without function scope in TS here if (funcVariableScope == nullptr) { return; } if (funcVariableScope->NeedLexEnv()) { lexLevel++; } if (funcVariableScope->NeedSendableEnv()) { sendableLevel++; } } ScopeFindResult Scope::Find(const util::StringView &name, ResolveBindingOptions options) const { uint32_t level = 0; uint32_t lexLevel = 0; uint32_t sendableLevel = 0; const auto *iter = this; ir::ScriptFunction *concurrentFunc = nullptr; // If the first scope is functionParamScope, it means its corresponding functionScope is not // iterated. so by default we set prevScopeNotFunctionScope as true so under such case, // functionScopeNotIterated will be true. bool prevScopeNotFunctionScope = true; bool lexical = false; while (iter != nullptr) { bool functionScopeNotIterated = iter->IsFunctionParamScope() && prevScopeNotFunctionScope; Variable *v = iter->FindLocal(name, options); if (v != nullptr) { return {name, const_cast(iter), level, lexLevel, sendableLevel, v, concurrentFunc}; } if (iter->IsFunctionVariableScope() && !lexical) { lexical = true; } if (iter->IsFunctionScope() && !concurrentFunc) { if (iter->Node()->AsScriptFunction()->IsConcurrent()) { concurrentFunc = const_cast(iter->Node()->AsScriptFunction()); } } if (iter->IsVariableScope()) { if (lexical) { level++; } if (iter->AsVariableScope()->NeedLexEnv()) { lexLevel++; } if (iter->AsVariableScope()->NeedSendableEnv()) { sendableLevel++; } } else if (functionScopeNotIterated) { level++; CalculateLevelInCorrespondingFunctionScope(iter->AsFunctionParamScope(), lexLevel, sendableLevel); } prevScopeNotFunctionScope = !iter->IsFunctionVariableScope(); iter = iter->Parent(); } return {name, nullptr, 0, 0, 0, nullptr, concurrentFunc}; } std::pair Scope::Find(const ir::Expression *expr, bool onlyLevel) const { uint32_t lexLevel = 0; const auto *iter = this; while (iter != nullptr) { if (iter->Type() == ScopeType::CLASS) { if (onlyLevel) { return {lexLevel, 0}; } return {lexLevel, iter->AsClassScope()->GetSlot(expr)}; } if (iter->IsVariableScope()) { if (iter->AsVariableScope()->NeedLexEnv()) { lexLevel++; } } iter = iter->Parent(); } UNREACHABLE(); } bool ClassScope::IsVariableScope() const { // sendable class does not need a lexical env, handle it's scope as a non-variable scope return !node_->AsClassDefinition()->IsSendable(); } Result ClassScope::GetPrivateProperty(const util::StringView &name, bool isSetter) const { if (name.Is("#method")) { return {instanceMethodValidation_, false, false, false, false, 0}; } uint32_t slot{0}; bool setter{false}; bool getter{false}; if (privateNames_.find(name) != privateNames_.end()) { slot = privateNames_.find(name)->second; } else { auto accessor = isSetter ? privateSetters_ : privateGetters_; auto unexpectedAccessor = isSetter ? privateGetters_ : privateSetters_; if (accessor.find(name) != accessor.end()) { setter = isSetter; getter = !setter; slot = accessor.find(name)->second; } else { getter = isSetter; setter = !getter; slot = unexpectedAccessor.find(name)->second; } } uint32_t validateMethodSlot{0}; if (IsMethod(slot)) { validateMethodSlot = IsStaticMethod(slot) ? staticMethodValidation_ : instanceMethodValidation_; } return {slot, IsMethod(slot), IsStaticMethod(slot), getter, setter, validateMethodSlot}; } void ClassScope::AddPrivateName(std::vector privateProperties, uint32_t privateFieldCnt, uint32_t instancePrivateMethodCnt, uint32_t staticPrivateMethodCnt) { privateFieldCnt_ = privateFieldCnt; instancePrivateMethodStartSlot_ = slotIndex_ + privateFieldCnt_; staticPrivateMethodStartSlot_ = instancePrivateMethodStartSlot_ + instancePrivateMethodCnt; uint32_t instancePrivateMethodSlot = instancePrivateMethodStartSlot_; uint32_t staticPrivateMethodSlot = staticPrivateMethodStartSlot_; for (const auto *stmt : privateProperties) { if (stmt->IsClassProperty()) { privateNames_[stmt->AsClassProperty()->Key()->AsPrivateIdentifier()->Name()] = slotIndex_++; continue; } ASSERT(stmt->IsMethodDefinition()); auto *methodDef = stmt->AsMethodDefinition(); uint32_t *start = methodDef->IsStatic() ? &staticPrivateMethodSlot : &instancePrivateMethodSlot; auto name = methodDef->Key()->AsPrivateIdentifier()->Name(); switch (methodDef->Kind()) { case ir::MethodDefinitionKind::GET: { privateGetters_[name] = (*start)++; continue; } case ir::MethodDefinitionKind::SET: { privateSetters_[name] = (*start)++; continue; } default: { privateNames_[name]= (*start)++; continue; } } } slotIndex_ = staticPrivateMethodSlot; privateMethodEndSlot_ = slotIndex_; if (instancePrivateMethodCnt != 0) { instanceMethodValidation_ = slotIndex_++; } if (staticPrivateMethodCnt != 0) { staticMethodValidation_ = slotIndex_++; } } util::StringView ClassScope::GetSelfScopeName() { if (hasSelfScopeNameSet_) { return selfScopeName_; } std::stringstream scopeName; if (node_ && node_->IsClassDefinition() && node_->AsClassDefinition()->Ident()) { util::StringView selfName = node_->AsClassDefinition()->Ident()->Name(); scopeName << selfName; return util::UString(scopeName.str(), allocator_).View(); } // To get the name for anonymous class if (node_ && node_->Parent() && node_->Parent()->Parent()) { scopeName << util::Helpers::GetName(allocator_, node_->Parent()->Parent()); return util::UString(scopeName.str(), allocator_).View(); } return util::UString(scopeName.str(), allocator_).View(); } util::StringView ClassScope::GetScopeTag() { return util::UString(util::Helpers::CLASS_SCOPE_TAG.data(), allocator_).View(); } PrivateNameFindResult Scope::FindPrivateName(const util::StringView &name, bool isSetter) const { uint32_t lexLevel = 0; const auto *iter = this; while (iter != nullptr) { if (iter->Type() == ScopeType::CLASS) { const auto *classScope = iter->AsClassScope(); if (name.Is("#method") || classScope->HasPrivateName(name)) { return {lexLevel, classScope->GetPrivateProperty(name, isSetter)}; } } if (iter->IsVariableScope()) { if (iter->AsVariableScope()->NeedLexEnv()) { lexLevel++; } } iter = iter->Parent(); } UNREACHABLE(); } Decl *Scope::FindDecl(const util::StringView &name) const { for (auto *it : decls_) { if (it->Name() == name) { return it; } } return nullptr; } bool Scope::HasVarDecl(const util::StringView &name) const { for (auto *it : decls_) { if (it->Name() == name && it->IsVarDecl()) { return true; } } return false; } std::tuple Scope::IterateShadowedVariables(const util::StringView &name, const VariableVisitior &visitor) { auto *iter = this; while (true) { auto *v = iter->FindLocal(name); if (v && visitor(v)) { return {iter, true}; } if (iter->IsFunctionVariableScope()) { break; } iter = iter->Parent(); } return {iter, false}; } void Scope::SetFullScopeNames() { if (hasFullScopeNameSet_) { return; } hasFullScopeNameSet_ = true; if (!hasSelfScopeNameSet_) { SetSelfScopeName(GetSelfScopeName()); } std::stringstream selfScopeStream; OptimizeSelfScopeName(selfScopeStream); std::stringstream fullScopeName; Scope *parent = GetParentWithScopeName(); if (parent) { fullScopeName << parent->GetFullScopeName() << GetScopeTag() << selfScopeStream.str(); if (scopeDuplicateIndex_ > 0) { fullScopeName << util::Helpers::DUPLICATED_SEPERATOR << std::hex << scopeDuplicateIndex_; } } fullScopeName_ = util::UString(fullScopeName.str(), allocator_).View(); } void Scope::OptimizeSelfScopeName(std::stringstream &selfScopeStream) { bool useIndex = false; auto it = topScope_->scopeNames_.find(selfScopeName_); if (it == topScope_->scopeNames_.end()) { std::stringstream indexScopeName; indexScopeName << util::Helpers::INDEX_NAME_SPICIFIER << std::hex << topScope_->scopeNames_.size(); if (selfScopeName_.Length() > indexScopeName.str().length()) { topScope_->scopeNames_.insert( {selfScopeName_, (int32_t)topScope_->scopeNames_.size()} ); selfScopeStream << indexScopeName.str(); useIndex = true; } } else { selfScopeStream << util::Helpers::INDEX_NAME_SPICIFIER << std::hex << it->second; useIndex = true; } if (!useIndex) { selfScopeStream << selfScopeName_; } } bool Scope::AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) { VariableFlags flags = VariableFlags::NONE; switch (newDecl->Type()) { case DeclType::VAR: { auto [scope, shadowed] = IterateShadowedVariables( newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); }); if (shadowed) { return false; } VariableFlags varFlags = VariableFlags::HOIST_VAR; if (scope->IsGlobalScope()) { scope->Bindings().insert({newDecl->Name(), allocator->New(newDecl, varFlags)}); } else { scope->PropagateBinding(allocator, newDecl->Name(), newDecl, varFlags); } return true; } case DeclType::ENUM_LITERAL: { return tsBindings_.AddTSVariable( newDecl->Name(), allocator->New(newDecl, VariableFlags::ENUM_LITERAL)); } case DeclType::INTERFACE: { bindings_.insert({newDecl->Name(), allocator->New(newDecl, VariableFlags::INTERFACE)}); return true; } case DeclType::FUNC: { flags = VariableFlags::HOIST; [[fallthrough]]; } default: { if (currentVariable) { return false; } if (HasVarDecl(newDecl->Name())) { return false; } bindings_.insert({newDecl->Name(), allocator->New(newDecl, flags)}); return true; } } } bool ParamScope::AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags) { CHECK_NOT_NULL(newDecl); ASSERT(newDecl->IsParameterDecl()); if (currentVariable) { return false; } auto *param = allocator->New(newDecl, flags); params_.push_back(param); bindings_.insert({newDecl->Name(), param}); return true; } std::tuple ParamScope::AddParamDecl(ArenaAllocator *allocator, const ir::AstNode *param) { const auto [name, pattern] = util::Helpers::ParamName(allocator, param, params_.size()); auto *decl = NewDecl(allocator, name); CHECK_NOT_NULL(decl); if (!AddParam(allocator, FindLocal(name), decl, VariableFlags::VAR)) { return {decl, param}; } if (!pattern) { decl->BindNode(param); return {decl, nullptr}; } std::vector bindings = util::Helpers::CollectBindingNames(param); for (const auto *binding : bindings) { auto *varDecl = NewDecl(allocator, binding->Name()); CHECK_NOT_NULL(varDecl); varDecl->BindNode(binding); if (FindLocal(varDecl->Name())) { return {decl, binding}; } auto *paramVar = allocator->New(varDecl, VariableFlags::VAR); bindings_.insert({varDecl->Name(), paramVar}); } return {decl, nullptr}; } void FunctionParamScope::BindName(ArenaAllocator *allocator, util::StringView name) { nameVar_ = AddDecl(allocator, name, VariableFlags::INITIALIZED); functionScope_->Bindings().insert({name, nameVar_}); } bool FunctionParamScope::AddBinding([[maybe_unused]] ArenaAllocator *allocator, [[maybe_unused]] Variable *currentVariable, [[maybe_unused]] Decl *newDecl, [[maybe_unused]] ScriptExtension extension) { UNREACHABLE(); } const util::StringView &FunctionParamScope::GetFullScopeName() { if (functionScope_) { return functionScope_->GetFullScopeName(); } // FunctionParam should have the same name with FunctionScope // Get scope name from parent in case the functionScope_ is nullptr if (parent_) { return parent_->GetFullScopeName(); } return fullScopeName_; } uint32_t FunctionParamScope::GetDuplicateScopeIndex(const util::StringView &childScopeName) { if (functionScope_) { return functionScope_->GetDuplicateScopeIndex(childScopeName); } if (parent_) { return parent_->GetDuplicateScopeIndex(childScopeName); } return 0; } void FunctionScope::BindNameWithScopeInfo(util::StringView name, util::StringView recordName) { name_ = name; std::stringstream internalName; internalName << recordName << util::Helpers::FUNC_NAME_SEPARATOR; Scope *parent = GetParentWithScopeName(); if (parent != nullptr) { internalName << parent->GetFullScopeName(); } internalName << GetScopeTag() << util::Helpers::FUNC_NAME_SEPARATOR << GetSelfScopeName(); if (scopeDuplicateIndex_ > 0) { internalName << util::Helpers::DUPLICATED_SEPERATOR << std::hex << scopeDuplicateIndex_; } internalName_ = util::UString(internalName.str(), allocator_).View(); } bool FunctionScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) { switch (newDecl->Type()) { case DeclType::VAR: { return AddVar(allocator, currentVariable, newDecl); } case DeclType::FUNC: { return AddFunction(allocator, currentVariable, newDecl, extension); } case DeclType::CLASS: { return AddClass(allocator, currentVariable, newDecl); } case DeclType::ENUM_LITERAL: { return AddTSBinding(allocator, newDecl, VariableFlags::ENUM_LITERAL); } case DeclType::NAMESPACE: { return AddTSBinding(allocator, newDecl, VariableFlags::NAMESPACE); } case DeclType::IMPORT_EQUALS: { return AddTSBinding(allocator, newDecl, VariableFlags::IMPORT_EQUALS); } case DeclType::INTERFACE: { return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::INTERFACE); } default: { return AddLexical(allocator, currentVariable, newDecl); } } } void FunctionScope::SetSelfScopeName(const util::StringView &ident) { Scope::SetSelfScopeName(ident); paramScope_->selfScopeName_ = selfScopeName_; paramScope_->hasSelfScopeNameSet_ = true; hasSelfScopeNameSet_ = true; } util::StringView FunctionScope::GetScopeTag() { if (IsFunctionScope() && (node_->IsScriptFunction() && node_->AsScriptFunction()->IsConstructor())) { return util::UString(util::Helpers::CTOR_TAG.data(), allocator_).View(); } if (parent_ && parent_->Parent() && parent_->Parent()->IsClassScope()) { bool hasNodeParent = node_ && node_->Parent() && node_->Parent()->Parent(); const ir::AstNode *nodeParent = hasNodeParent ? node_->Parent()->Parent() : nullptr; if (nodeParent && nodeParent->IsMethodDefinition() && nodeParent->AsMethodDefinition()->IsStatic()) { return util::UString(util::Helpers::STATIC_METHOD_TAG.data(), allocator_).View(); } return util::UString(util::Helpers::METHOD_TAG.data(), allocator_).View(); } return util::UString(util::Helpers::FUNCTION_TAG.data(), allocator_).View(); } util::StringView FunctionScope::GetSelfScopeName() { if (hasSelfScopeNameSet_) { return selfScopeName_; } if (node_ && node_->IsScriptFunction()) { auto selfName = util::Helpers::FunctionName(allocator_, node_->AsScriptFunction()); if (!util::Helpers::IsSpecialScopeName(selfName)) { return selfName; } } return util::UString(util::Helpers::STRING_EMPTY.data(), allocator_).View(); } util::StringView TSModuleScope::GetSelfScopeName() { if (hasSelfScopeNameSet_) { return selfScopeName_; } throw Error(ErrorType::GENERIC, "namespace or module name should be set in Binder::ResolveReference()"); } util::StringView TSModuleScope::GetScopeTag() { return util::UString(util::Helpers::NAMESPACE_TAG.data(), allocator_).View(); } bool TSEnumScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) { ASSERT(newDecl->Type() == DeclType::ENUM); return enumMemberBindings_->insert({newDecl->Name(), allocator->New(newDecl, false)}).second; } void TSEnumScope::SetSelfScopeName(const util::StringView &ident) { if (!hasSelfScopeNameSet_) { FunctionScope::SetSelfScopeName(GetSelfScopeName()); } } util::StringView TSEnumScope::GetSelfScopeName() { if (hasSelfScopeNameSet_) { return selfScopeName_; } std::stringstream scopeName; if (node_ && node_->IsScriptFunction()) { auto scriptFunction = node_->AsScriptFunction(); if (scriptFunction->Params().size() > 0 && scriptFunction->Params()[0]->IsIdentifier()) { scopeName << scriptFunction->Params()[0]->AsIdentifier()->Name(); } } return util::UString(scopeName.str(), allocator_).View(); } util::StringView TSEnumScope::GetScopeTag() { return util::UString(util::Helpers::ENUM_TAG.data(), allocator_).View(); } bool GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) { switch (newDecl->Type()) { case DeclType::VAR: { return AddVar(allocator, currentVariable, newDecl); } case DeclType::FUNC: { return AddFunction(allocator, currentVariable, newDecl, extension); } case DeclType::CLASS: { return AddClass(allocator, currentVariable, newDecl); } case DeclType::ENUM_LITERAL: { return AddTSBinding(allocator, newDecl, VariableFlags::ENUM_LITERAL); } case DeclType::NAMESPACE: { return AddTSBinding(allocator, newDecl, VariableFlags::NAMESPACE); } case DeclType::IMPORT_EQUALS: { return AddTSBinding(allocator, newDecl, VariableFlags::IMPORT_EQUALS); } case DeclType::INTERFACE: { return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::INTERFACE); } default: { return AddLexical(allocator, currentVariable, newDecl); } } return true; } void GlobalScope::SetSelfScopeName([[maybe_unused]] const util::StringView &ident) { hasSelfScopeNameSet_ = true; } // ModuleScope void ModuleScope::ConvertLocalVariableToModuleVariable(ArenaAllocator *allocator, util::StringView localName) { auto res = bindings_.find(localName); // Since the module's exported [localName] has been validated before, // [localName] must have a binding now. ASSERT(res != bindings_.end()); if (!res->second->IsModuleVariable()) { auto *decl = res->second->Declaration(); decl->AddFlag(DeclarationFlags::EXPORT); VariableFlags flags = res->second->Flags(); res->second = allocator->New(decl, flags | VariableFlags::LOCAL_EXPORT); } } void ModuleScope::AssignIndexToModuleVariable(util::StringView name, uint32_t index) { auto *moduleVar = FindLocal(name); CHECK_NOT_NULL(moduleVar); ASSERT(moduleVar->IsModuleVariable()); moduleVar->AsModuleVariable()->AssignIndex(index); } bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) { switch (newDecl->Type()) { case DeclType::VAR: { auto [scope, shadowed] = IterateShadowedVariables( newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); }); if (shadowed) { return false; } return newDecl->IsImportOrExportDecl() ? AddVar(allocator, currentVariable, newDecl) : AddVar(allocator, currentVariable, newDecl); } case DeclType::FUNC: { if (currentVariable) { auto decl = currentVariable->Declaration(); if (!decl->IsClassDecl() || !decl->AsClassDecl()->IsDeclare()) { return false; } } return newDecl->IsImportOrExportDecl() ? AddFunction(allocator, currentVariable, newDecl, extension) : AddFunction(allocator, currentVariable, newDecl, extension); } case DeclType::CLASS: { return newDecl->IsImportOrExportDecl() ? AddClass(allocator, currentVariable, newDecl) : AddClass(allocator, currentVariable, newDecl); } case DeclType::ENUM_LITERAL: { return AddTSBinding(allocator, newDecl, VariableFlags::ENUM_LITERAL); } case DeclType::NAMESPACE: { return AddTSBinding(allocator, newDecl, VariableFlags::NAMESPACE); } case DeclType::IMPORT_EQUALS: { return AddTSBinding(allocator, newDecl, VariableFlags::IMPORT_EQUALS); } case DeclType::INTERFACE: { return AddTSBinding(allocator, currentVariable, newDecl, VariableFlags::INTERFACE); } default: { if (currentVariable) { return false; } return newDecl->IsImportOrExportDecl() ? AddLexical(allocator, currentVariable, newDecl) : AddLexical(allocator, currentVariable, newDecl); } } } void ModuleScope::SetSelfScopeName([[maybe_unused]] const util::StringView &ident) { hasSelfScopeNameSet_ = true; } // LocalScope bool LocalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) { return AddLocal(allocator, currentVariable, newDecl, extension); } void LoopScope::InitVariable() { for (const auto &[name, var] : bindings_) { if (!var->Declaration()->IsLetOrConstOrClassDecl()) { continue; } var->AddFlag(VariableFlags::INITIALIZED); if (var->LexicalBound()) { var->AddFlag(VariableFlags::PER_ITERATION); } } } bool CatchParamScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) { return AddParam(allocator, currentVariable, newDecl, VariableFlags::INITIALIZED); } bool CatchScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, [[maybe_unused]] ScriptExtension extension) { if (!newDecl->IsVarDecl() && paramScope_->FindLocal(newDecl->Name())) { return false; } return AddLocal(allocator, currentVariable, newDecl, extension); } } // namespace panda::es2panda::binder