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