• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021 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 <binder/declaration.h>
19 #include <util/helpers.h>
20 #include <binder/tsBinding.h>
21 #include <binder/variable.h>
22 #include <binder/variableFlags.h>
23 #include <ir/astNode.h>
24 #include <ir/base/scriptFunction.h>
25 #include <ir/expressions/identifier.h>
26 #include <ir/module/exportAllDeclaration.h>
27 #include <ir/module/exportNamedDeclaration.h>
28 #include <ir/module/exportSpecifier.h>
29 #include <ir/module/importDeclaration.h>
30 #include <macros.h>
31 #include <util/concurrent.h>
32 #include <util/ustring.h>
33 
34 #include <algorithm>
35 #include <sstream>
36 
37 namespace panda::es2panda::binder {
38 
EnclosingVariableScope()39 VariableScope *Scope::EnclosingVariableScope()
40 {
41     Scope *iter = this;
42 
43     while (iter) {
44         if (iter->IsVariableScope()) {
45             return iter->AsVariableScope();
46         }
47 
48         iter = iter->Parent();
49     }
50 
51     return nullptr;
52 }
53 
EnclosingFunctionVariableScope()54 FunctionScope *Scope::EnclosingFunctionVariableScope()
55 {
56     Scope *iter = this;
57     while (iter) {
58         if (iter->IsFunctionVariableScope()) {
59             return iter->AsFunctionVariableScope();
60         }
61 
62         iter = iter->Parent();
63     }
64 
65     return nullptr;
66 }
67 
FindLocal(const util::StringView & name,ResolveBindingOptions options) const68 Variable *Scope::FindLocal(const util::StringView &name, ResolveBindingOptions options) const
69 {
70     if (options & ResolveBindingOptions::INTERFACES) {
71         util::StringView interfaceNameView(binder::TSBinding::ToTSBinding(name));
72 
73         auto res = bindings_.find(interfaceNameView);
74         if (res != bindings_.end()) {
75             return res->second;
76         }
77 
78         if (!(options & ResolveBindingOptions::BINDINGS)) {
79             return nullptr;
80         }
81     }
82 
83     auto res = bindings_.find(name);
84     if (res == bindings_.end()) {
85         return nullptr;
86     }
87 
88     return res->second;
89 }
90 
Find(const util::StringView & name,ResolveBindingOptions options) const91 ScopeFindResult Scope::Find(const util::StringView &name, ResolveBindingOptions options) const
92 {
93     uint32_t level = 0;
94     uint32_t lexLevel = 0;
95     const auto *iter = this;
96     bool crossConcurrent = false;
97 
98     if (iter->IsFunctionParamScope()) {
99         Variable *v = iter->FindLocal(name, options);
100 
101         if (v != nullptr) {
102             return {name, const_cast<Scope *>(iter), level, lexLevel, v, crossConcurrent};
103         }
104 
105         level++;
106         auto *funcVariableScope = iter->AsFunctionParamScope()->GetFunctionScope();
107 
108         // we may only have function param scope without function scope in TS here
109         if ((funcVariableScope != nullptr) && (funcVariableScope->NeedLexEnv())) {
110             lexLevel++;
111         }
112 
113         iter = iter->Parent();
114     }
115 
116     bool lexical = false;
117 
118     while (iter != nullptr) {
119         Variable *v = iter->FindLocal(name, options);
120 
121         if (v != nullptr) {
122             return {name, const_cast<Scope *>(iter), level, lexLevel, v, crossConcurrent};
123         }
124 
125         if (iter->IsFunctionVariableScope() && !lexical) {
126             lexical = true;
127         }
128 
129         if (iter->IsVariableScope()) {
130             if (lexical) {
131                 level++;
132             }
133 
134             if (iter->IsFunctionScope() && !crossConcurrent) {
135                 crossConcurrent = iter->Node()->AsScriptFunction()->IsConcurrent() ? true : false;
136             }
137 
138             if (iter->AsVariableScope()->NeedLexEnv()) {
139                 lexLevel++;
140             }
141         }
142 
143         iter = iter->Parent();
144     }
145 
146     return {name, nullptr, 0, 0, nullptr, crossConcurrent};
147 }
148 
FindDecl(const util::StringView & name) const149 Decl *Scope::FindDecl(const util::StringView &name) const
150 {
151     for (auto *it : decls_) {
152         if (it->Name() == name) {
153             return it;
154         }
155     }
156 
157     return nullptr;
158 }
159 
HasVarDecl(const util::StringView & name) const160 bool Scope::HasVarDecl(const util::StringView &name) const
161 {
162     for (auto *it : decls_) {
163         if (it->Name() == name && it->IsVarDecl()) {
164             return true;
165         }
166     }
167 
168     return false;
169 }
170 
IterateShadowedVariables(const util::StringView & name,const VariableVisitior & visitor)171 std::tuple<Scope *, bool> Scope::IterateShadowedVariables(const util::StringView &name, const VariableVisitior &visitor)
172 {
173     auto *iter = this;
174 
175     while (true) {
176         auto *v = iter->FindLocal(name);
177 
178         if (v && visitor(v)) {
179             return {iter, true};
180         }
181 
182         if (iter->IsFunctionVariableScope()) {
183             break;
184         }
185 
186         iter = iter->Parent();
187     }
188 
189     return {iter, false};
190 }
191 
AddLocal(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)192 bool Scope::AddLocal(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
193                      [[maybe_unused]] ScriptExtension extension)
194 {
195     VariableFlags flags = VariableFlags::NONE;
196     switch (newDecl->Type()) {
197         case DeclType::VAR: {
198             auto [scope, shadowed] = IterateShadowedVariables(
199                 newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); });
200 
201             if (shadowed) {
202                 return false;
203             }
204 
205             VariableFlags varFlags = VariableFlags::HOIST_VAR;
206             if (scope->IsGlobalScope()) {
207                 scope->Bindings().insert({newDecl->Name(), allocator->New<GlobalVariable>(newDecl, varFlags)});
208             } else {
209                 scope->PropagateBinding<LocalVariable>(allocator, newDecl->Name(), newDecl, varFlags);
210             }
211 
212             return true;
213         }
214         case DeclType::ENUM: {
215             bindings_.insert({newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)});
216             return true;
217         }
218         case DeclType::ENUM_LITERAL: {
219             bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::ENUM_LITERAL)});
220             return true;
221         }
222         case DeclType::INTERFACE: {
223             bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::INTERFACE)});
224             return true;
225         }
226         case DeclType::FUNC: {
227             flags = VariableFlags::HOIST;
228             [[fallthrough]];
229         }
230         default: {
231             if (currentVariable) {
232                 return false;
233             }
234 
235             if (HasVarDecl(newDecl->Name())) {
236                 return false;
237             }
238 
239             bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, flags)});
240             return true;
241         }
242     }
243 }
244 
AddParam(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,VariableFlags flags)245 bool ParamScope::AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags)
246 {
247     ASSERT(newDecl->IsParameterDecl());
248 
249     if (currentVariable) {
250         return false;
251     }
252 
253     auto *param = allocator->New<LocalVariable>(newDecl, flags);
254 
255     params_.push_back(param);
256     bindings_.insert({newDecl->Name(), param});
257     return true;
258 }
259 
AddParamDecl(ArenaAllocator * allocator,const ir::AstNode * param)260 std::tuple<ParameterDecl *, const ir::AstNode *> ParamScope::AddParamDecl(ArenaAllocator *allocator,
261                                                                           const ir::AstNode *param)
262 {
263     const auto [name, pattern] = util::Helpers::ParamName(allocator, param, params_.size());
264 
265     auto *decl = NewDecl<ParameterDecl>(allocator, name);
266 
267     if (!AddParam(allocator, FindLocal(name), decl, VariableFlags::VAR)) {
268         return {decl, param};
269     }
270 
271     if (!pattern) {
272         decl->BindNode(param);
273         return {decl, nullptr};
274     }
275 
276     std::vector<const ir::Identifier *> bindings = util::Helpers::CollectBindingNames(param);
277 
278     for (const auto *binding : bindings) {
279         auto *varDecl = NewDecl<VarDecl>(allocator, binding->Name());
280         varDecl->BindNode(binding);
281 
282         if (FindLocal(varDecl->Name())) {
283             return {decl, binding};
284         }
285 
286         auto *paramVar = allocator->New<LocalVariable>(varDecl, VariableFlags::VAR);
287         bindings_.insert({varDecl->Name(), paramVar});
288     }
289 
290     return {decl, nullptr};
291 }
292 
BindName(ArenaAllocator * allocator,util::StringView name)293 void FunctionParamScope::BindName(ArenaAllocator *allocator, util::StringView name)
294 {
295     nameVar_ = AddDecl<ConstDecl, LocalVariable>(allocator, name, VariableFlags::INITIALIZED);
296     functionScope_->Bindings().insert({name, nameVar_});
297 }
298 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)299 bool FunctionParamScope::AddBinding([[maybe_unused]] ArenaAllocator *allocator,
300                                     [[maybe_unused]] Variable *currentVariable, [[maybe_unused]] Decl *newDecl,
301                                     [[maybe_unused]] ScriptExtension extension)
302 {
303     UNREACHABLE();
304 }
305 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)306 bool FunctionScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
307                                [[maybe_unused]] ScriptExtension extension)
308 {
309     switch (newDecl->Type()) {
310         case DeclType::VAR: {
311             return AddVar<LocalVariable>(allocator, currentVariable, newDecl);
312         }
313         case DeclType::FUNC: {
314             return AddFunction<LocalVariable>(allocator, currentVariable, newDecl, extension);
315         }
316         case DeclType::ENUM: {
317             bindings_.insert({newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)});
318             return true;
319         }
320         case DeclType::ENUM_LITERAL: {
321             return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL);
322         }
323         case DeclType::NAMESPACE: {
324             return AddTSBinding<NamespaceVariable>(allocator, newDecl, VariableFlags::NAMESPACE);
325         }
326         case DeclType::IMPORT_EQUALS: {
327             return AddTSBinding<ImportEqualsVariable>(allocator, newDecl, VariableFlags::IMPORT_EQUALS);
328         }
329         case DeclType::INTERFACE: {
330             return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
331         }
332         default: {
333             return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
334         }
335     }
336 }
337 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)338 bool GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
339                              [[maybe_unused]] ScriptExtension extension)
340 {
341     switch (newDecl->Type()) {
342         case DeclType::VAR: {
343             return AddVar<GlobalVariable>(allocator, currentVariable, newDecl);
344         }
345         case DeclType::FUNC: {
346             return AddFunction<GlobalVariable>(allocator, currentVariable, newDecl, extension);
347         }
348         case DeclType::ENUM: {
349             bindings_.insert({newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)});
350             return true;
351         }
352         case DeclType::ENUM_LITERAL: {
353             return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL);
354         }
355         case DeclType::NAMESPACE: {
356             return AddTSBinding<NamespaceVariable>(allocator, newDecl, VariableFlags::NAMESPACE);
357         }
358         case DeclType::IMPORT_EQUALS: {
359             return AddTSBinding<ImportEqualsVariable>(allocator, newDecl, VariableFlags::IMPORT_EQUALS);
360         }
361         case DeclType::INTERFACE: {
362             return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
363         }
364         default: {
365             return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
366         }
367     }
368 
369     return true;
370 }
371 
372 // ModuleScope
373 
ConvertLocalVariableToModuleVariable(ArenaAllocator * allocator,util::StringView localName)374 void ModuleScope::ConvertLocalVariableToModuleVariable(ArenaAllocator *allocator, util::StringView localName)
375 {
376     auto res = bindings_.find(localName);
377     // Since the module's exported [localName] has been validated before,
378     // [localName] must have a binding now.
379     ASSERT(res != bindings_.end());
380     if (!res->second->IsModuleVariable()) {
381         auto *decl = res->second->Declaration();
382         decl->AddFlag(DeclarationFlags::EXPORT);
383         VariableFlags flags = res->second->Flags();
384         res->second = allocator->New<ModuleVariable>(decl, flags | VariableFlags::LOCAL_EXPORT);
385     }
386 }
387 
AssignIndexToModuleVariable(util::StringView name,uint32_t index)388 void ModuleScope::AssignIndexToModuleVariable(util::StringView name, uint32_t index)
389 {
390     auto *moduleVar = FindLocal(name);
391     ASSERT(moduleVar != nullptr);
392     ASSERT(moduleVar->IsModuleVariable());
393     moduleVar->AsModuleVariable()->AssignIndex(index);
394 }
395 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)396 bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
397                              [[maybe_unused]] ScriptExtension extension)
398 {
399     switch (newDecl->Type()) {
400         case DeclType::VAR: {
401             auto [scope, shadowed] = IterateShadowedVariables(
402                 newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); });
403 
404             if (shadowed) {
405                 return false;
406             }
407             return newDecl->IsImportOrExportDecl() ?
408                    AddVar<ModuleVariable>(allocator, currentVariable, newDecl) :
409                    AddVar<LocalVariable>(allocator, currentVariable, newDecl);
410         }
411         case DeclType::FUNC: {
412             if (currentVariable) {
413                 return false;
414             }
415             return newDecl->IsImportOrExportDecl() ?
416                    AddFunction<ModuleVariable>(allocator, currentVariable, newDecl, extension) :
417                    AddFunction<LocalVariable>(allocator, currentVariable, newDecl, extension);
418         }
419         case DeclType::ENUM: {
420             bindings_.insert({newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)});
421             return true;
422         }
423         case DeclType::ENUM_LITERAL: {
424             return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::ENUM_LITERAL);
425         }
426         case DeclType::NAMESPACE: {
427             return AddTSBinding<NamespaceVariable>(allocator, newDecl, VariableFlags::NAMESPACE);
428         }
429         case DeclType::IMPORT_EQUALS: {
430             return AddTSBinding<ImportEqualsVariable>(allocator, newDecl, VariableFlags::IMPORT_EQUALS);
431         }
432         case DeclType::INTERFACE: {
433             return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
434         }
435         default: {
436             if (currentVariable) {
437                 return false;
438             }
439             return newDecl->IsImportOrExportDecl() ?
440                    AddLexical<ModuleVariable>(allocator, currentVariable, newDecl) :
441                    AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
442         }
443     }
444 }
445 
446 // LocalScope
447 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)448 bool LocalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
449                             [[maybe_unused]] ScriptExtension extension)
450 {
451     return AddLocal(allocator, currentVariable, newDecl, extension);
452 }
453 
InitVariable()454 void LoopScope::InitVariable()
455 {
456     for (const auto &[name, var] : bindings_) {
457         if (!var->Declaration()->IsLetOrConstOrClassDecl()) {
458             continue;
459         }
460 
461         var->AddFlag(VariableFlags::INITIALIZED);
462         if (var->LexicalBound()) {
463             var->AddFlag(VariableFlags::PER_ITERATION);
464         }
465     }
466 }
467 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)468 bool CatchParamScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
469                                  [[maybe_unused]] ScriptExtension extension)
470 {
471     return AddParam(allocator, currentVariable, newDecl, VariableFlags::INITIALIZED);
472 }
473 
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)474 bool CatchScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
475                             [[maybe_unused]] ScriptExtension extension)
476 {
477     if (!newDecl->IsVarDecl() && paramScope_->FindLocal(newDecl->Name())) {
478         return false;
479     }
480 
481     return AddLocal(allocator, currentVariable, newDecl, extension);
482 }
483 
484 }  // namespace panda::es2panda::binder
485