• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "scope.h"
17 
18 #include "public/public.h"
19 #include "varbinder/tsBinding.h"
20 #include "compiler/lowering/util.h"
21 
22 namespace ark::es2panda::varbinder {
EnclosingVariableScope()23 VariableScope *Scope::EnclosingVariableScope() noexcept
24 {
25     return const_cast<VariableScope *>(const_cast<Scope const *>(this)->EnclosingVariableScope());
26 }
27 
EnclosingVariableScope() const28 const VariableScope *Scope::EnclosingVariableScope() const noexcept
29 {
30     const auto *iter = this;
31 
32     while (iter != nullptr) {
33         if (iter->IsVariableScope()) {
34             return iter->AsVariableScope();
35         }
36 
37         iter = iter->Parent();
38     }
39 
40     return nullptr;
41 }
42 
IsSuperscopeOf(const varbinder::Scope * subscope) const43 bool Scope::IsSuperscopeOf(const varbinder::Scope *subscope) const noexcept
44 {
45     while (subscope != nullptr) {
46         if (subscope == this) {
47             return true;
48         }
49         subscope = ir::AstNode::EnclosingScope(subscope->Node()->Parent());
50     }
51     return false;
52 }
53 
FindLocal(const util::StringView & name,ResolveBindingOptions options) const54 Variable *Scope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const
55 {
56     if ((options & ResolveBindingOptions::INTERFACES) != 0) {
57         std::string tsBindingName = varbinder::TSBinding::ToTSBinding(name);
58         util::StringView interfaceNameView(tsBindingName);
59 
60         auto res = bindings_.find(interfaceNameView);
61         if (res != bindings_.end()) {
62             return res->second;
63         }
64 
65         if ((options & ResolveBindingOptions::BINDINGS) == 0) {
66             return nullptr;
67         }
68     }
69 
70     auto res = bindings_.find(name);
71     if (res == bindings_.end()) {
72         return nullptr;
73     }
74 
75     return res->second;
76 }
77 
CorrectForeignBinding(const util::StringView & name,Variable * builtinVar,Variable * redefinedVar)78 bool Scope::CorrectForeignBinding(const util::StringView &name, Variable *builtinVar, Variable *redefinedVar)
79 {
80     ES2PANDA_ASSERT(builtinVar != redefinedVar);
81     auto bindingTobeErase = bindings_.find(name);
82     if (bindingTobeErase == bindings_.end()) {
83         return false;
84     }
85 
86     Variable *varTobeErase = bindingTobeErase->second;
87     if (varTobeErase != redefinedVar) {
88         return false;
89     }
90 
91     auto declTobeErase = redefinedVar->Declaration();
92     bindings_.erase(name);
93     auto it = std::find_if(decls_.begin(), decls_.end(), [&](auto *decl) { return decl == declTobeErase; });
94     ES2PANDA_ASSERT(it != decls_.end());
95     decls_.erase(it);
96     return Scope::InsertBinding(name, builtinVar).second;
97 }
98 
InsertBinding(const util::StringView & name,Variable * const var)99 Scope::InsertResult Scope::InsertBinding(const util::StringView &name, Variable *const var)
100 {
101     ES2PANDA_ASSERT(var != nullptr);
102     auto insertResult = bindings_.emplace(name, var);
103     if (insertResult.second) {
104         decls_.emplace_back(var->Declaration());
105     }
106     return insertResult;
107 }
108 
InsertOrAssignBinding(const util::StringView & name,Variable * const var)109 Scope::InsertResult Scope::InsertOrAssignBinding(const util::StringView &name, Variable *const var)
110 {
111     ES2PANDA_ASSERT(var != nullptr);
112     auto insertResult = bindings_.insert_or_assign(name, var);
113     if (insertResult.second) {
114         decls_.emplace_back(var->Declaration());
115     }
116     return insertResult;
117 }
118 
TryInsertBinding(const util::StringView & name,Variable * const var)119 Scope::InsertResult Scope::TryInsertBinding(const util::StringView &name, Variable *const var)
120 {
121     ES2PANDA_ASSERT(var != nullptr);
122     return bindings_.try_emplace(name, var);
123 }
124 
MergeBindings(VariableMap const & bindings)125 void Scope::MergeBindings(VariableMap const &bindings)
126 {
127     for (auto &[k, v] : bindings) {
128         bindings_.try_emplace(k, v);
129     }
130 }
131 
EraseBinding(const util::StringView & name)132 Scope::VariableMap::size_type Scope::EraseBinding(const util::StringView &name)
133 {
134     if (auto toBeErased = bindings_.find(name);
135         toBeErased == bindings_.end() ||
136         (toBeErased->second->IsLocalVariable() &&
137          toBeErased->second->AsLocalVariable()->Declaration()->Node()->IsImportNamespaceSpecifier())) {
138         return 0U;
139     }
140 
141     return bindings_.erase(name);
142 }
143 
FindInGlobal(const util::StringView & name,const ResolveBindingOptions options) const144 ConstScopeFindResult Scope::FindInGlobal(const util::StringView &name, const ResolveBindingOptions options) const
145 {
146     const auto *scopeIter = this;
147     Variable *resolved = nullptr;
148     while (scopeIter != nullptr && !scopeIter->IsGlobalScope()) {
149         bool isModule = scopeIter->Node() != nullptr && scopeIter->Node()->IsClassDefinition() &&
150                         scopeIter->Node()->AsClassDefinition()->IsModule();
151         if (isModule) {
152             resolved = scopeIter->FindLocal(name, options);
153             if (resolved != nullptr) {
154                 break;
155             }
156         }
157         scopeIter = scopeIter->Parent();
158     }
159     if (resolved == nullptr && scopeIter != nullptr && scopeIter->IsGlobalScope()) {
160         resolved = scopeIter->FindLocal(name, options);
161     }
162     return {name, scopeIter, 0, 0, resolved};
163 }
164 
FindInFunctionScope(const util::StringView & name,const ResolveBindingOptions options) const165 ConstScopeFindResult Scope::FindInFunctionScope(const util::StringView &name, const ResolveBindingOptions options) const
166 {
167     const auto *scopeIter = this;
168     while (scopeIter != nullptr && !scopeIter->IsGlobalScope()) {
169         if (!scopeIter->IsClassScope()) {
170             if (auto *const resolved = scopeIter->FindLocal(name, options); resolved != nullptr) {
171                 return ConstScopeFindResult(name, scopeIter, 0, 0, resolved);
172             }
173         }
174         scopeIter = scopeIter->Parent();
175     }
176 
177     return ConstScopeFindResult(name, scopeIter, 0, 0, nullptr);
178 }
179 
Find(const util::StringView & name,const ResolveBindingOptions options)180 ScopeFindResult Scope::Find(const util::StringView &name, const ResolveBindingOptions options)
181 {
182     return FindImpl<ScopeFindResult>(this, name, options);
183 }
184 
Find(const util::StringView & name,const ResolveBindingOptions options) const185 ConstScopeFindResult Scope::Find(const util::StringView &name, const ResolveBindingOptions options) const
186 {
187     return FindImpl<ConstScopeFindResult>(this, name, options);
188 }
189 
FindDecl(const util::StringView & name) const190 Decl *Scope::FindDecl(const util::StringView &name) const noexcept
191 {
192     for (auto *it : decls_) {
193         if (it->Name() == name) {
194             return it;
195         }
196     }
197 
198     return nullptr;
199 }
200 
IterateShadowedVariables(const util::StringView & name,const VariableVisitor & visitor)201 std::tuple<Scope *, bool> Scope::IterateShadowedVariables(const util::StringView &name, const VariableVisitor &visitor)
202 {
203     auto *iter = this;
204 
205     while (iter != nullptr) {
206         auto *v = iter->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS);
207 
208         if (v != nullptr && visitor(v)) {
209             return {iter, true};
210         }
211 
212         if (iter->IsFunctionVariableScope()) {
213             break;
214         }
215 
216         iter = iter->Parent();
217     }
218 
219     return {iter, false};
220 }
221 
AddLocalVar(ArenaAllocator * allocator,Decl * newDecl)222 Variable *Scope::AddLocalVar(ArenaAllocator *allocator, Decl *newDecl)
223 {
224     auto [scope, shadowed] =
225         IterateShadowedVariables(newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); });
226 
227     if (shadowed) {
228         return nullptr;
229     }
230 
231     VariableFlags varFlags = VariableFlags::HOIST_VAR | VariableFlags::LEXICAL_VAR;
232     if (scope->IsGlobalScope()) {
233         return scope->InsertBinding(newDecl->Name(), allocator->New<GlobalVariable>(newDecl, varFlags)).first->second;
234     }
235 
236     return scope->PropagateBinding<LocalVariable>(allocator, newDecl->Name(), newDecl, varFlags);
237 }
238 
AddLocalInterfaceVariable(ArenaAllocator * allocator,Decl * newDecl)239 Variable *Scope::AddLocalInterfaceVariable(ArenaAllocator *allocator, Decl *newDecl)
240 {
241     auto *var = bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::INTERFACE)})
242                     .first->second;
243     if (newDecl->Node() != nullptr) {
244         newDecl->Node()->AsTSInterfaceDeclaration()->Id()->SetVariable(var);
245     }
246     return var;
247 }
248 
AddLocalTypeAliasVariable(ArenaAllocator * allocator,Decl * newDecl)249 Variable *Scope::AddLocalTypeAliasVariable(ArenaAllocator *allocator, Decl *newDecl)
250 {
251     auto *var = bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::TYPE_ALIAS)})
252                     .first->second;
253     newDecl->Node()->AsTSTypeAliasDeclaration()->Id()->SetVariable(var);
254     return var;
255 }
256 
AddLocalClassVariable(ArenaAllocator * allocator,Decl * newDecl)257 Variable *Scope::AddLocalClassVariable(ArenaAllocator *allocator, Decl *newDecl)
258 {
259     auto isNamespaceTransformed = newDecl->Node()->AsClassDefinition()->IsNamespaceTransformed();
260     VariableFlags flag = isNamespaceTransformed ? VariableFlags::NAMESPACE : VariableFlags::CLASS;
261     auto *var = bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, flag)}).first->second;
262     newDecl->Node()->AsClassDefinition()->Ident()->SetVariable(var);
263     return var;
264 }
265 
AddLocal(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)266 Variable *Scope::AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
267                           [[maybe_unused]] ScriptExtension extension)
268 {
269     VariableFlags flags = VariableFlags::LEXICAL;
270     switch (newDecl->Type()) {
271         case DeclType::VAR: {
272             return AddLocalVar(allocator, newDecl);
273         }
274         case DeclType::ENUM: {
275             return bindings_.insert({newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)}).first->second;
276         }
277         case DeclType::ENUM_LITERAL: {
278             auto *var =
279                 bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::ENUM_LITERAL)})
280                     .first->second;
281             newDecl->Node()->AsTSEnumDeclaration()->Key()->SetVariable(var);
282             return var;
283         }
284         case DeclType::INTERFACE: {
285             return AddLocalInterfaceVariable(allocator, newDecl);
286         }
287         case DeclType::CLASS: {
288             return AddLocalClassVariable(allocator, newDecl);
289         }
290         case DeclType::TYPE_ALIAS: {
291             return AddLocalTypeAliasVariable(allocator, newDecl);
292         }
293         case DeclType::TYPE_PARAMETER: {
294             return bindings_
295                 .insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::TYPE_PARAMETER)})
296                 .first->second;
297         }
298         case DeclType::FUNC: {
299             flags = VariableFlags::HOIST;
300             [[fallthrough]];
301         }
302         default: {
303             if (currentVariable != nullptr) {
304                 return nullptr;
305             }
306 
307             auto [_, shadowed] = IterateShadowedVariables(
308                 newDecl->Name(), [](const Variable *v) { return v->HasFlag(VariableFlags::LEXICAL_VAR); });
309             (void)_;
310 
311             if (shadowed) {
312                 return nullptr;
313             }
314 
315             return bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, flags)}).first->second;
316         }
317     }
318 }
319 
CheckDirectEval(public_lib::Context * context)320 void VariableScope::CheckDirectEval(public_lib::Context *context)
321 {
322     ES2PANDA_ASSERT(context);
323     const auto &varMap = Bindings();
324 
325     if (!HasFlag(ScopeFlags::NO_REG_STORE) || varMap.empty()) {
326         evalBindings_ = compiler::INVALID_LITERAL_BUFFER_ID;
327         return;
328     }
329 
330     size_t constBindings = 0;
331     for (const auto &[name, var] : varMap) {
332         (void)name;
333         var->SetLexical(this);
334 
335         if (var->LexicalBound() && var->Declaration()->IsConstDecl()) {
336             constBindings++;
337         }
338     }
339 
340     std::vector<compiler::Literal> literals(LexicalSlots() + constBindings, compiler::Literal(util::StringView()));
341 
342     if (constBindings == 0U) {
343         for (const auto &[name, variable] : varMap) {
344             if (!variable->LexicalBound()) {
345                 continue;
346             }
347 
348             literals[variable->AsLocalVariable()->LexIdx()] = compiler::Literal(name);
349         }
350     } else {
351         std::vector<varbinder::Variable *> bindings(LexicalSlots());
352 
353         for (const auto &[name, variable] : varMap) {
354             (void)name;
355             if (!variable->LexicalBound()) {
356                 continue;
357             }
358 
359             bindings[variable->AsLocalVariable()->LexIdx()] = variable;
360         }
361 
362         uint32_t buffIndex = 0;
363         for (const auto *variable : bindings) {
364             if (variable == nullptr) {
365                 ES2PANDA_ASSERT(literals[buffIndex].GetString().empty());
366                 buffIndex++;
367                 continue;
368             }
369             if (variable->Declaration()->IsConstDecl()) {
370                 literals[buffIndex++] = compiler::Literal(true);
371             }
372             literals[buffIndex++] = compiler::Literal(variable->Name());
373         }
374     }
375     context->contextLiterals.emplace_back(literals);
376     evalBindings_ = context->contextLiterals.size() - 1;
377 }
378 
379 template <typename T>
AddVar(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)380 Variable *VariableScope::AddVar(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
381                                 [[maybe_unused]] ScriptExtension extension)
382 {
383     if (!currentVariable) {
384         return InsertBinding(newDecl->Name(), allocator->New<T>(newDecl, VariableFlags::HOIST_VAR)).first->second;
385     }
386 
387     switch (currentVariable->Declaration()->Type()) {
388         case DeclType::VAR: {
389             currentVariable->Reset(newDecl, VariableFlags::HOIST_VAR);
390             [[fallthrough]];
391         }
392         case DeclType::PARAM:
393         case DeclType::FUNC: {
394             return currentVariable;
395         }
396         default: {
397             ES2PANDA_ASSERT(extension == ScriptExtension::JS);
398             return nullptr;
399         }
400     }
401 }
402 
403 template <typename T>
AddFunction(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)404 Variable *VariableScope::AddFunction(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
405                                      ScriptExtension extension)
406 {
407     VariableFlags flags = (extension == ScriptExtension::JS) ? VariableFlags::HOIST_VAR : VariableFlags::HOIST;
408 
409     if (!currentVariable) {
410         return InsertBinding(newDecl->Name(), allocator->New<T>(newDecl, flags)).first->second;
411     }
412 
413     if (extension != ScriptExtension::JS || IsModuleScope()) {
414         return nullptr;
415     }
416 
417     switch (currentVariable->Declaration()->Type()) {
418         case DeclType::VAR:
419         case DeclType::FUNC: {
420             currentVariable->Reset(newDecl, VariableFlags::HOIST_VAR);
421             return currentVariable;
422         }
423         default: {
424             ES2PANDA_ASSERT(extension == ScriptExtension::JS);
425             return nullptr;
426         }
427     }
428 }
429 
430 template <typename T>
AddTSBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,VariableFlags flags)431 Variable *VariableScope::AddTSBinding(ArenaAllocator *allocator, [[maybe_unused]] Variable *currentVariable,
432                                       Decl *newDecl, VariableFlags flags)
433 {
434     ES2PANDA_ASSERT(!currentVariable);
435     return InsertBinding(newDecl->Name(), allocator->New<T>(newDecl, flags)).first->second;
436 }
437 
438 template <typename T>
AddLexical(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl)439 Variable *VariableScope::AddLexical(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl)
440 {
441     if (currentVariable) {
442         return nullptr;
443     }
444 
445     return InsertBinding(newDecl->Name(), allocator->New<T>(newDecl, VariableFlags::NONE)).first->second;
446 }
447 
AddParameter(ArenaAllocator * allocator,Decl * newDecl,VariableFlags flags)448 Variable *ParamScope::AddParameter(ArenaAllocator *allocator, Decl *newDecl, VariableFlags flags)
449 {
450     ES2PANDA_ASSERT(newDecl->IsParameterDecl());
451 
452     auto *param = allocator->New<LocalVariable>(newDecl, flags);
453     ES2PANDA_ASSERT(param != nullptr);
454     param->SetScope(this);
455 
456     params_.emplace_back(param);
457     InsertBinding(newDecl->Name(), param);
458 
459     return param;
460 }
461 
AddParamDecl(ArenaAllocator * allocator,varbinder::VarBinder * vb,ir::Expression * parameter)462 std::tuple<Variable *, ir::Expression *> ParamScope::AddParamDecl(ArenaAllocator *allocator, varbinder::VarBinder *vb,
463                                                                   ir::Expression *parameter)
464 {
465     auto [name, pattern] = util::Helpers::ParamName(allocator, parameter, params_.size());
466     if (name.Is(ERROR_LITERAL)) {
467         name = compiler::GenName(allocator).View();
468     }
469 
470     auto *variable = FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS);
471     if (variable != nullptr) {
472         return std::make_tuple(variable, parameter);
473     }
474 
475     if (pattern) {
476         std::vector<ir::Identifier *> bindings = util::Helpers::CollectBindingNames(vb, parameter);
477 
478         for (auto *binding : bindings) {
479             auto *varDecl = NewDecl<VarDecl>(allocator, binding->Name());
480             varDecl->BindNode(binding);
481 
482             if (variable = FindLocal(varDecl->Name(), varbinder::ResolveBindingOptions::BINDINGS);
483                 variable != nullptr) {
484                 return std::make_tuple(variable, binding);
485             }
486 
487             auto *paramVar = allocator->New<LocalVariable>(varDecl, VariableFlags::VAR | VariableFlags::LOCAL);
488             TryInsertBinding(varDecl->Name(), paramVar);
489         }
490     }
491 
492     auto *const decl = NewDecl<ParameterDecl>(allocator, name);
493     variable = AddParameter(allocator, decl, VariableFlags::VAR | VariableFlags::LOCAL);
494     decl->BindNode(parameter);
495 
496     return std::make_tuple(variable, nullptr);
497 }
498 
BindName(ArenaAllocator * allocator,util::StringView name)499 void FunctionParamScope::BindName(ArenaAllocator *allocator, util::StringView name)
500 {
501     auto [variable, _] = AddDecl<ConstDecl, LocalVariable>(allocator, name, VariableFlags::INITIALIZED);
502     nameVar_ = functionScope_->InsertBinding(name, variable).second ? variable->AsLocalVariable() : nullptr;
503 }
504 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)505 Variable *FunctionParamScope::AddBinding([[maybe_unused]] ArenaAllocator *allocator,
506                                          [[maybe_unused]] Variable *currentVariable, [[maybe_unused]] Decl *newDecl,
507                                          [[maybe_unused]] ScriptExtension extension)
508 {
509     ES2PANDA_UNREACHABLE();
510 }
511 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)512 Variable *AnnotationParamScope::AddBinding([[maybe_unused]] ArenaAllocator *allocator,
513                                            [[maybe_unused]] Variable *currentVariable, [[maybe_unused]] Decl *newDecl,
514                                            [[maybe_unused]] ScriptExtension extension)
515 {
516     auto *ident = newDecl->Node()->AsClassProperty()->Id();
517     ES2PANDA_ASSERT(ident != nullptr);
518     auto annoVar = allocator->New<LocalVariable>(newDecl, VariableFlags::PROPERTY);
519     auto var = InsertBinding(ident->Name(), annoVar).first->second;
520     if (var != nullptr) {
521         var->SetScope(this);
522         ident->SetVariable(var);
523     }
524     return var;
525 }
526 
InsertBindingIfAbsentInScope(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,VariableFlags flag)527 Variable *FunctionScope::InsertBindingIfAbsentInScope(ArenaAllocator *allocator, Variable *currentVariable,
528                                                       Decl *newDecl, VariableFlags flag)
529 {
530     if (currentVariable != nullptr) {
531         return nullptr;
532     }
533     return InsertBinding(newDecl->Name(), allocator->New<LocalVariable>(newDecl, flag)).first->second;
534 }
535 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)536 Variable *FunctionScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
537                                     [[maybe_unused]] ScriptExtension extension)
538 {
539     ir::Identifier *ident {};
540     Variable *var {};
541     switch (newDecl->Type()) {
542         case DeclType::VAR: {
543             return AddVar<LocalVariable>(allocator, currentVariable, newDecl, extension);
544         }
545         case DeclType::FUNC: {
546             return AddFunction<LocalVariable>(allocator, currentVariable, newDecl, extension);
547         }
548         case DeclType::ENUM: {
549             return InsertBinding(newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)).first->second;
550         }
551         case DeclType::ENUM_LITERAL: {
552             var = AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL);
553             ident = newDecl->Node()->AsTSEnumDeclaration()->Key();
554             break;
555         }
556         // NOTE(psiket):Duplication
557         case DeclType::INTERFACE: {
558             ident = newDecl->Node()->AsTSInterfaceDeclaration()->Id();
559             var = InsertBindingIfAbsentInScope(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
560             break;
561         }
562         case DeclType::CLASS: {
563             auto isNamespaceTransformed = newDecl->Node()->AsClassDefinition()->IsNamespaceTransformed();
564             VariableFlags flag = isNamespaceTransformed ? VariableFlags::NAMESPACE : VariableFlags::CLASS;
565             ident = newDecl->Node()->AsClassDefinition()->Ident();
566             var = InsertBindingIfAbsentInScope(allocator, currentVariable, newDecl, flag);
567             break;
568         }
569         case DeclType::TYPE_ALIAS: {
570             ident = newDecl->Node()->AsTSTypeAliasDeclaration()->Id();
571             var = InsertBindingIfAbsentInScope(allocator, currentVariable, newDecl, VariableFlags::TYPE_ALIAS);
572             break;
573         }
574         default: {
575             return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
576         }
577     }
578     if (var != nullptr) {
579         var->SetScope(this);
580         if (ident != nullptr) {
581             ident->SetVariable(var);
582         }
583     }
584     return var;
585 }
586 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)587 Variable *GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
588                                   [[maybe_unused]] ScriptExtension extension)
589 {
590     switch (newDecl->Type()) {
591         case DeclType::VAR: {
592             return AddVar<GlobalVariable>(allocator, currentVariable, newDecl, extension);
593         }
594         case DeclType::FUNC: {
595             return AddFunction<GlobalVariable>(allocator, currentVariable, newDecl, extension);
596         }
597         case DeclType::ENUM: {
598             return InsertBinding(newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)).first->second;
599         }
600         case DeclType::ENUM_LITERAL: {
601             return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL);
602         }
603         case DeclType::INTERFACE: {
604             return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
605         }
606         default: {
607             return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
608         }
609     }
610 }
611 
InsertBinding(const util::StringView & name,Variable * const var)612 Scope::InsertResult GlobalScope::InsertBinding(const util::StringView &name, Variable *const var)
613 {
614     return GlobalScope::InsertImpl(name, var, InsertBindingFlags::NONE);
615 }
616 
TryInsertBinding(const util::StringView & name,Variable * const var)617 Scope::InsertResult GlobalScope::TryInsertBinding(const util::StringView &name, Variable *const var)
618 {
619     const auto insRes = Scope::TryInsertBinding(name, var);
620     if (insRes.second) {
621         [[maybe_unused]] const bool insertSuccess = std::get<1>(foreignBindings_.try_emplace(name, var));
622         ES2PANDA_ASSERT(insertSuccess);
623     }
624 
625     return insRes;
626 }
627 
MergeBindings(const VariableMap & bindings)628 void GlobalScope::MergeBindings([[maybe_unused]] const VariableMap &bindings)
629 {
630     ES2PANDA_UNREACHABLE();
631 }
632 
EraseBinding(const util::StringView & name)633 Scope::VariableMap::size_type GlobalScope::EraseBinding(const util::StringView &name)
634 {
635     const auto erased = Scope::EraseBinding(name);
636     if (erased != 0) {
637         [[maybe_unused]] const auto erasedForeign = foreignBindings_.erase(name);
638         ES2PANDA_ASSERT(erasedForeign != 0);
639     }
640 
641     return erased;
642 }
643 
InsertForeignBinding(const util::StringView & name,Variable * const var)644 Scope::InsertResult GlobalScope::InsertForeignBinding(const util::StringView &name, Variable *const var)
645 {
646     return GlobalScope::InsertImpl(name, var, InsertBindingFlags::FOREIGN);
647 }
648 
InsertOrAssignForeignBinding(const util::StringView & name,Variable * const var)649 Scope::InsertResult GlobalScope::InsertOrAssignForeignBinding(const util::StringView &name, Variable *const var)
650 {
651     return GlobalScope::InsertImpl(name, var, InsertBindingFlags::FOREIGN | InsertBindingFlags::ASSIGN);
652 }
653 
CorrectForeignBinding(const util::StringView & name,Variable * builtinVar,Variable * redefinedVar)654 bool GlobalScope::CorrectForeignBinding(const util::StringView &name, Variable *builtinVar, Variable *redefinedVar)
655 {
656     const bool deleteRes = Scope::CorrectForeignBinding(name, builtinVar, redefinedVar);
657     if (deleteRes) {
658         foreignBindings_[name] = true;
659     }
660     return deleteRes;
661 }
662 
InsertImpl(const util::StringView & name,Variable * const var,const InsertBindingFlags flags)663 Scope::InsertResult GlobalScope::InsertImpl(const util::StringView &name, Variable *const var,
664                                             const InsertBindingFlags flags)
665 {
666     bool isAssign = (flags & InsertBindingFlags::ASSIGN) != 0;
667     bool isDynamic = (flags & InsertBindingFlags::DYNAMIC) != 0;
668     bool isForeign = (flags & InsertBindingFlags::FOREIGN) != 0;
669 
670     if (!isDynamic && isForeign && !var->Declaration()->Name().Is(compiler::Signatures::ETS_GLOBAL)) {
671         ES2PANDA_ASSERT(var->Declaration()->Name().Utf8().find(compiler::Signatures::ETS_GLOBAL) == std::string::npos);
672         const auto *const node = var->Declaration()->Node();
673 
674         if (!(node->IsExported() || node->IsDefaultExported() || node->HasExportAlias())) {
675             return Scope::InsertResult {Bindings().end(), false};
676         }
677     }
678 
679     if (isAssign) {
680         const auto insRes = Scope::InsertOrAssignBinding(name, var);
681         foreignBindings_.insert_or_assign(name, isForeign);
682         return insRes;
683     }
684 
685     const auto insRes = Scope::InsertBinding(name, var);
686     if (insRes.second) {
687         [[maybe_unused]] const bool insertSuccess = std::get<1>(foreignBindings_.emplace(name, isForeign));
688         ES2PANDA_ASSERT(insertSuccess);
689     }
690     return insRes;
691 }
692 
IsForeignBinding(const util::StringView & name) const693 bool GlobalScope::IsForeignBinding(const util::StringView &name) const
694 {
695     // Asserts make sure that the passed in key comes from this scope
696     ES2PANDA_ASSERT(Bindings().find(name) != Bindings().end());
697     ES2PANDA_ASSERT(foreignBindings_.find(name) != foreignBindings_.end());
698 
699     return foreignBindings_.at(name);
700 }
701 
InsertDynamicBinding(const util::StringView & name,Variable * const var)702 Scope::InsertResult GlobalScope::InsertDynamicBinding(const util::StringView &name, Variable *const var)
703 {
704     return InsertImpl(name, var, InsertBindingFlags::FOREIGN | InsertBindingFlags::DYNAMIC);
705 }
706 
707 // ModuleScope
708 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)709 Variable *ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
710                                   [[maybe_unused]] ScriptExtension extension)
711 {
712     switch (newDecl->Type()) {
713         case DeclType::VAR: {
714             return AddVar<LocalVariable>(allocator, currentVariable, newDecl, extension);
715         }
716         case DeclType::FUNC: {
717             return AddFunction<LocalVariable>(allocator, currentVariable, newDecl, extension);
718         }
719         case DeclType::ENUM: {
720             return InsertBinding(newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)).first->second;
721         }
722         case DeclType::ENUM_LITERAL: {
723             return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL);
724         }
725         case DeclType::INTERFACE: {
726             return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
727         }
728         case DeclType::IMPORT: {
729             return AddImport(allocator, currentVariable, newDecl);
730         }
731         case DeclType::EXPORT: {
732             return allocator->New<LocalVariable>(newDecl, VariableFlags::NONE);
733         }
734         default: {
735             return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
736         }
737     }
738 }
739 
AddImportDecl(ir::ImportDeclaration * importDecl,ImportDeclList && decls)740 void ModuleScope::AddImportDecl(ir::ImportDeclaration *importDecl, ImportDeclList &&decls)
741 {
742     auto res = imports_.emplace_back(importDecl, decls);
743 
744     for (auto &decl : res.second) {
745         decl->BindNode(importDecl);
746     }
747 }
748 
AddExportDecl(ir::AstNode * exportDecl,ExportDecl * decl)749 void ModuleScope::AddExportDecl(ir::AstNode *exportDecl, ExportDecl *decl)
750 {
751     ES2PANDA_ASSERT(decl != nullptr);
752     decl->BindNode(exportDecl);
753 
754     ArenaVector<ExportDecl *> decls(allocator_->Adapter());
755     decls.push_back(decl);
756 
757     AddExportDecl(exportDecl, std::move(decls));
758 }
759 
AddExportDecl(ir::AstNode * exportDecl,ExportDeclList && decls)760 void ModuleScope::AddExportDecl(ir::AstNode *exportDecl, ExportDeclList &&decls)
761 {
762     auto res = exports_.emplace_back(exportDecl, decls);
763 
764     for (auto &decl : res.second) {
765         decl->BindNode(exportDecl);
766     }
767 }
768 
AddImport(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl)769 Variable *ModuleScope::AddImport(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl)
770 {
771     if (currentVariable != nullptr && currentVariable->Declaration()->Type() != DeclType::VAR) {
772         return nullptr;
773     }
774 
775     if (newDecl->Node()->IsImportNamespaceSpecifier()) {
776         return InsertBinding(newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::READONLY))
777             .first->second;
778     }
779 
780     auto *variable = allocator->New<ModuleVariable>(newDecl, VariableFlags::NONE);
781     ES2PANDA_ASSERT(variable != nullptr);
782     variable->ExoticName() = newDecl->AsImportDecl()->ImportName();
783     InsertBinding(newDecl->Name(), variable);
784     return variable;
785 }
786 
ExportAnalysis()787 bool ModuleScope::ExportAnalysis()
788 {
789     std::set<util::StringView> exportedNames;
790 
791     for (const auto &[exportDecl, decls] : exports_) {
792         if (exportDecl->IsExportAllDeclaration()) {
793             const auto *exportAllDecl = exportDecl->AsExportAllDeclaration();
794 
795             if (exportAllDecl->Exported() == nullptr) {
796                 continue;
797             }
798 
799             auto result = exportedNames.insert(exportAllDecl->Exported()->Name());
800             if (!result.second) {
801                 return false;
802             }
803 
804             continue;
805         }
806 
807         if (exportDecl->IsExportNamedDeclaration()) {
808             const auto *exportNamedDecl = exportDecl->AsExportNamedDeclaration();
809 
810             if (exportNamedDecl->Source() != nullptr) {
811                 continue;
812             }
813         }
814 
815         for (const auto *decl : decls) {
816             varbinder::Variable *variable = FindLocal(decl->LocalName(), varbinder::ResolveBindingOptions::BINDINGS);
817 
818             if (variable == nullptr) {
819                 continue;
820             }
821 
822             auto result = exportedNames.insert(decl->ExportName());
823             if (!result.second) {
824                 return false;
825             }
826 
827             if (!variable->IsModuleVariable()) {
828                 variable->AddFlag(VariableFlags::LOCAL_EXPORT);
829                 localExports_.insert({variable, decl->ExportName()});
830             }
831         }
832     }
833 
834     return true;
835 }
836 
837 // LocalScope
838 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)839 Variable *LocalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
840                                  [[maybe_unused]] ScriptExtension extension)
841 {
842     return AddLocal(allocator, currentVariable, newDecl, extension);
843 }
844 
FindLocal(const util::StringView & name,ResolveBindingOptions options) const845 Variable *ClassScope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const
846 {
847     if ((options & ResolveBindingOptions::TYPE_ALIASES) != 0) {
848         auto found = typeAliasScope_->Bindings().find(name);
849         if (found != typeAliasScope_->Bindings().end()) {
850             return found->second;
851         }
852     }
853 
854     if ((options & ResolveBindingOptions::VARIABLES) != 0) {
855         auto found = instanceFieldScope_->Bindings().find(name);
856         if (found != instanceFieldScope_->Bindings().end()) {
857             return found->second;
858         }
859     }
860 
861     if ((options & ResolveBindingOptions::STATIC_VARIABLES) != 0) {
862         auto found = staticFieldScope_->Bindings().find(name);
863         if (found != staticFieldScope_->Bindings().end()) {
864             return found->second;
865         }
866     }
867 
868     if ((options & ResolveBindingOptions::DECLARATION) != 0) {
869         auto found = instanceDeclScope_->Bindings().find(name);
870         if (found != instanceDeclScope_->Bindings().end()) {
871             return found->second;
872         }
873     }
874 
875     if ((options & ResolveBindingOptions::STATIC_DECLARATION) != 0) {
876         auto found = staticDeclScope_->Bindings().find(name);
877         if (found != staticDeclScope_->Bindings().end()) {
878             return found->second;
879         }
880     }
881 
882     if ((options & ResolveBindingOptions::METHODS) != 0) {
883         auto found = instanceMethodScope_->Bindings().find(name);
884         if (found != instanceMethodScope_->Bindings().end()) {
885             return found->second;
886         }
887     }
888 
889     if ((options & ResolveBindingOptions::STATIC_METHODS) != 0) {
890         auto found = staticMethodScope_->Bindings().find(name);
891         if (found != staticMethodScope_->Bindings().end()) {
892             return found->second;
893         }
894     }
895 
896     return nullptr;
897 }
898 
SetBindingProps(Decl * newDecl,BindingProps * props,bool isStatic)899 void ClassScope::SetBindingProps(Decl *newDecl, BindingProps *props, bool isStatic)
900 {
901     if (newDecl->IsImportDecl()) {
902         return;
903     }
904 
905     switch (newDecl->Type()) {
906         case DeclType::CONST:
907             [[fallthrough]];
908         case DeclType::READONLY:
909             [[fallthrough]];
910         case DeclType::LET:
911             if (newDecl->Node()->IsClassProperty()) {
912                 props->SetBindingProps(VariableFlags::PROPERTY, newDecl->Node()->AsClassProperty()->Id(),
913                                        isStatic ? staticFieldScope_ : instanceFieldScope_);
914             }
915             break;
916 
917         case DeclType::INTERFACE:
918             props->SetBindingProps(VariableFlags::INTERFACE, newDecl->Node()->AsTSInterfaceDeclaration()->Id(),
919                                    isStatic ? staticDeclScope_ : instanceDeclScope_);
920             break;
921 
922         case DeclType::CLASS: {
923             VariableFlags flag = VariableFlags::CLASS;
924             if (newDecl->Node()->AsClassDefinition()->IsNamespaceTransformed()) {
925                 flag = VariableFlags::NAMESPACE;
926             }
927             props->SetBindingProps(flag, newDecl->Node()->AsClassDefinition()->Ident(),
928                                    isStatic ? staticDeclScope_ : instanceDeclScope_);
929             break;
930         }
931 
932         case DeclType::ENUM_LITERAL:
933             props->SetBindingProps(VariableFlags::ENUM_LITERAL, newDecl->Node()->AsTSEnumDeclaration()->Key(),
934                                    isStatic ? staticDeclScope_ : instanceDeclScope_);
935             break;
936 
937         case DeclType::TYPE_ALIAS:
938             props->SetBindingProps(VariableFlags::TYPE_ALIAS, newDecl->Node()->AsTSTypeAliasDeclaration()->Id(),
939                                    typeAliasScope_);
940             break;
941 
942         case DeclType::ANNOTATIONDECL:
943             props->SetBindingProps(VariableFlags::ANNOTATIONDECL,
944                                    newDecl->Node()->AsAnnotationDeclaration()->GetBaseName(),
945                                    isStatic ? staticDeclScope_ : instanceDeclScope_);
946             break;
947 
948         case DeclType::ANNOTATIONUSAGE:
949             props->SetBindingProps(VariableFlags::ANNOTATIONUSAGE, newDecl->Node()->AsAnnotationUsage()->GetBaseName(),
950                                    isStatic ? staticDeclScope_ : instanceDeclScope_);
951             break;
952 
953         default:
954             ES2PANDA_UNREACHABLE();
955     }
956 }
957 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)958 Variable *ClassScope::AddBinding(ArenaAllocator *allocator, [[maybe_unused]] Variable *currentVariable, Decl *newDecl,
959                                  [[maybe_unused]] ScriptExtension extension)
960 {
961     bool isStatic = newDecl->Node()->IsStatic();
962     BindingProps props;
963 
964     if (isStatic) {
965         props.SetFlagsType(VariableFlags::STATIC);
966     }
967 
968     SetBindingProps(newDecl, &props, isStatic);
969     if (props.GetTargetScope() == nullptr) {
970         return nullptr;
971     }
972 
973     // First search
974     const auto *foundVar = FindLocal(newDecl->Name(), ResolveBindingOptions::ALL);
975     if (foundVar != nullptr) {
976         // Found potential conflict in the current scope
977         if (!newDecl->IsLetOrConstDecl() && !newDecl->Node()->IsStatement()) {
978             return nullptr;
979         }
980 
981         // Non-static variables can coexist with static variables and static methods and vice versa.
982         // Run second search with filtered options.
983         const auto excludeOptions =
984             isStatic ? ResolveBindingOptions::VARIABLES | ResolveBindingOptions::METHODS
985                      : ResolveBindingOptions::STATIC_VARIABLES | ResolveBindingOptions::STATIC_METHODS;
986         const auto options = ResolveBindingOptions::ALL ^ excludeOptions;
987         foundVar = FindLocal(newDecl->Name(), options);
988         if (foundVar != nullptr) {
989             // Conflict in bindings
990             return nullptr;
991         }
992     }
993 
994     auto *var = props.GetTargetScope()->AddBinding(allocator, nullptr, newDecl, extension);
995     if (var == nullptr) {
996         return nullptr;
997     }
998 
999     if (auto node = newDecl->Node(); node->IsStatement() && (node->AsStatement()->IsMethodDefinition() ||
1000                                                              node->IsClassProperty() || node->IsClassStaticBlock())) {
1001         if (node->AsStatement()->AsClassElement()->Value() != nullptr) {
1002             props.SetFlagsType(VariableFlags::INITIALIZED);
1003         }
1004 
1005         if (node->IsClassProperty() && node->AsClassProperty()->NeedInitInStaticBlock()) {
1006             props.SetFlagsType(VariableFlags::INIT_IN_STATIC_BLOCK);
1007         }
1008     }
1009 
1010     var->SetScope(this);
1011     var->AddFlag(props.GetFlags());
1012 
1013     if (props.GetIdent() != nullptr) {
1014         props.GetIdent()->SetVariable(var);
1015     }
1016 
1017     return var;
1018 }
1019 
ConvertToVariableScope(ArenaAllocator * allocator)1020 void LoopDeclarationScope::ConvertToVariableScope(ArenaAllocator *allocator)
1021 {
1022     if (NeedLexEnv()) {
1023         return;
1024     }
1025 
1026     const auto &bindings = Bindings();
1027     for (auto &[name, var] : bindings) {
1028         if (!var->LexicalBound() || !var->Declaration()->IsLetOrConstDecl()) {
1029             continue;
1030         }
1031 
1032         slotIndex_++;
1033         loopType_ = ScopeType::LOOP_DECL;
1034         auto *copiedVar = var->AsLocalVariable()->Copy(allocator, var->Declaration());
1035         copiedVar->AddFlag(VariableFlags::INITIALIZED | VariableFlags::PER_ITERATION);
1036         var->AddFlag(VariableFlags::LOOP_DECL);
1037         loopScope_->InsertBinding(name, copiedVar);
1038     }
1039 
1040     if (loopType_ == ScopeType::LOOP_DECL) {
1041         auto *parentVarScope = Parent()->EnclosingVariableScope();
1042         ES2PANDA_ASSERT(parentVarScope != nullptr);
1043         slotIndex_ = std::max(slotIndex_, parentVarScope->LexicalSlots());
1044         evalBindings_ = parentVarScope->EvalBindings();
1045         initScope_ = allocator->New<LocalScope>(allocator, Parent());
1046         initScope_->BindNode(Node());
1047         initScope_->MergeBindings(bindings);
1048     }
1049 }
1050 
ConvertToVariableScope(ArenaAllocator * allocator)1051 void LoopScope::ConvertToVariableScope(ArenaAllocator *allocator)
1052 {
1053     declScope_->ConvertToVariableScope(allocator);
1054 
1055     if (loopType_ != ScopeType::LOCAL) {
1056         return;
1057     }
1058 
1059     for (const auto &[_, var] : Bindings()) {
1060         (void)_;
1061         if (var->LexicalBound() && var->Declaration()->IsLetDecl()) {
1062             ES2PANDA_ASSERT(declScope_->NeedLexEnv());
1063             loopType_ = ScopeType::LOOP;
1064             break;
1065         }
1066     }
1067 
1068     if (loopType_ == ScopeType::LOOP) {
1069         slotIndex_ = std::max(slotIndex_, declScope_->LexicalSlots());
1070         evalBindings_ = declScope_->EvalBindings();
1071     }
1072 }
1073 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)1074 Variable *CatchParamScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
1075                                       [[maybe_unused]] ScriptExtension extension)
1076 {
1077     return currentVariable != nullptr ? nullptr : AddParameter(allocator, newDecl, VariableFlags::INITIALIZED);
1078 }
1079 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)1080 Variable *CatchScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
1081                                  [[maybe_unused]] ScriptExtension extension)
1082 {
1083     if (!newDecl->IsVarDecl() &&
1084         (paramScope_->FindLocal(newDecl->Name(), varbinder::ResolveBindingOptions::BINDINGS) != nullptr)) {
1085         return nullptr;
1086     }
1087 
1088     return AddLocal(allocator, currentVariable, newDecl, extension);
1089 }
1090 
FindLocal(const util::StringView & name,ResolveBindingOptions options) const1091 Variable *CatchScope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const
1092 {
1093     auto res = Bindings().find(name);
1094     if (res == Bindings().end()) {
1095         return paramScope_->FindLocal(name, options);
1096     }
1097     return res->second;
1098 }
1099 
1100 template <typename T, typename... Args>
PropagateBinding(ArenaAllocator * allocator,util::StringView name,Args &&...args)1101 Variable *Scope::PropagateBinding(ArenaAllocator *allocator, util::StringView name, Args &&...args)
1102 {
1103     auto res = bindings_.find(name);
1104     if (res == bindings_.end()) {
1105         return bindings_.insert({name, allocator->New<T>(std::forward<Args>(args)...)}).first->second;
1106     }
1107 
1108     res->second->Reset(std::forward<Args>(args)...);
1109     return res->second;
1110 }
1111 }  // namespace ark::es2panda::varbinder
1112