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 const std::string &interfaceName = binder::TSBinding::ToTSBinding(name);
72
73 auto res = bindings_.find(util::StringView{interfaceName});
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
HasLexEnvInCorrespondingFunctionScope(const FunctionParamScope * scope) const91 bool Scope::HasLexEnvInCorrespondingFunctionScope(const FunctionParamScope *scope) const
92 {
93 auto *funcVariableScope = scope->GetFunctionScope();
94 // we may only have function param scope without function scope in TS here
95 if ((funcVariableScope != nullptr) && (funcVariableScope->NeedLexEnv())) {
96 return true;
97 }
98 return false;
99 }
100
Find(const util::StringView & name,ResolveBindingOptions options) const101 ScopeFindResult Scope::Find(const util::StringView &name, ResolveBindingOptions options) const
102 {
103 uint32_t level = 0;
104 uint32_t lexLevel = 0;
105 const auto *iter = this;
106 bool crossConcurrent = false;
107 // If the first scope is functionParamScope, it means its corresponding functionScope is not
108 // iterated. so by default we set prevScopeNotFunctionScope as true so under such case,
109 // functionScopeNotIterated will be true.
110 bool prevScopeNotFunctionScope = true;
111 bool lexical = false;
112
113 while (iter != nullptr) {
114 bool functionScopeNotIterated = iter->IsFunctionParamScope() && prevScopeNotFunctionScope;
115 Variable *v = iter->FindLocal(name, options);
116
117 if (v != nullptr) {
118 return {name, const_cast<Scope *>(iter), level, lexLevel, v, crossConcurrent};
119 }
120
121 if (iter->IsFunctionVariableScope() && !lexical) {
122 lexical = true;
123 }
124
125 if (iter->IsVariableScope()) {
126 if (lexical) {
127 level++;
128 }
129
130 if (iter->IsFunctionScope() && !crossConcurrent) {
131 crossConcurrent = iter->Node()->AsScriptFunction()->IsConcurrent() ? true : false;
132 }
133 if (iter->AsVariableScope()->NeedLexEnv()) {
134 lexLevel++;
135 }
136 } else if (functionScopeNotIterated) {
137 level++;
138 if (HasLexEnvInCorrespondingFunctionScope(iter->AsFunctionParamScope())) {
139 lexLevel++;
140 }
141 }
142 prevScopeNotFunctionScope = !iter->IsFunctionVariableScope();
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_LITERAL: {
215 return tsBindings_.AddTSVariable<TSBindingType::ENUMLITERAL>(
216 newDecl->Name(), allocator->New<EnumLiteralVariable>(newDecl, VariableFlags::ENUM_LITERAL));
217 }
218 case DeclType::INTERFACE: {
219 bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, VariableFlags::INTERFACE)});
220 return true;
221 }
222 case DeclType::FUNC: {
223 flags = VariableFlags::HOIST;
224 [[fallthrough]];
225 }
226 default: {
227 if (currentVariable) {
228 return false;
229 }
230
231 if (HasVarDecl(newDecl->Name())) {
232 return false;
233 }
234
235 bindings_.insert({newDecl->Name(), allocator->New<LocalVariable>(newDecl, flags)});
236 return true;
237 }
238 }
239 }
240
AddParam(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,VariableFlags flags)241 bool ParamScope::AddParam(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl, VariableFlags flags)
242 {
243 ASSERT(newDecl->IsParameterDecl());
244
245 if (currentVariable) {
246 return false;
247 }
248
249 auto *param = allocator->New<LocalVariable>(newDecl, flags);
250
251 params_.push_back(param);
252 bindings_.insert({newDecl->Name(), param});
253 return true;
254 }
255
AddParamDecl(ArenaAllocator * allocator,const ir::AstNode * param)256 std::tuple<ParameterDecl *, const ir::AstNode *> ParamScope::AddParamDecl(ArenaAllocator *allocator,
257 const ir::AstNode *param)
258 {
259 const auto [name, pattern] = util::Helpers::ParamName(allocator, param, params_.size());
260
261 auto *decl = NewDecl<ParameterDecl>(allocator, name);
262
263 if (!AddParam(allocator, FindLocal(name), decl, VariableFlags::VAR)) {
264 return {decl, param};
265 }
266
267 if (!pattern) {
268 decl->BindNode(param);
269 return {decl, nullptr};
270 }
271
272 std::vector<const ir::Identifier *> bindings = util::Helpers::CollectBindingNames(param);
273
274 for (const auto *binding : bindings) {
275 auto *varDecl = NewDecl<VarDecl>(allocator, binding->Name());
276 varDecl->BindNode(binding);
277
278 if (FindLocal(varDecl->Name())) {
279 return {decl, binding};
280 }
281
282 auto *paramVar = allocator->New<LocalVariable>(varDecl, VariableFlags::VAR);
283 bindings_.insert({varDecl->Name(), paramVar});
284 }
285
286 return {decl, nullptr};
287 }
288
BindName(ArenaAllocator * allocator,util::StringView name)289 void FunctionParamScope::BindName(ArenaAllocator *allocator, util::StringView name)
290 {
291 nameVar_ = AddDecl<ConstDecl, LocalVariable>(allocator, name, VariableFlags::INITIALIZED);
292 functionScope_->Bindings().insert({name, nameVar_});
293 }
294
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)295 bool FunctionParamScope::AddBinding([[maybe_unused]] ArenaAllocator *allocator,
296 [[maybe_unused]] Variable *currentVariable, [[maybe_unused]] Decl *newDecl,
297 [[maybe_unused]] ScriptExtension extension)
298 {
299 UNREACHABLE();
300 }
301
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)302 bool FunctionScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
303 [[maybe_unused]] ScriptExtension extension)
304 {
305 switch (newDecl->Type()) {
306 case DeclType::VAR: {
307 return AddVar<LocalVariable>(allocator, currentVariable, newDecl);
308 }
309 case DeclType::FUNC: {
310 return AddFunction<LocalVariable>(allocator, currentVariable, newDecl, extension);
311 }
312 case DeclType::CLASS: {
313 return AddClass<LocalVariable>(allocator, currentVariable, newDecl);
314 }
315 case DeclType::ENUM_LITERAL: {
316 return AddTSBinding<EnumLiteralVariable>(allocator, newDecl, VariableFlags::ENUM_LITERAL);
317 }
318 case DeclType::NAMESPACE: {
319 return AddTSBinding<NamespaceVariable>(allocator, newDecl, VariableFlags::NAMESPACE);
320 }
321 case DeclType::IMPORT_EQUALS: {
322 return AddTSBinding<ImportEqualsVariable>(allocator, newDecl, VariableFlags::IMPORT_EQUALS);
323 }
324 case DeclType::INTERFACE: {
325 return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
326 }
327 default: {
328 return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
329 }
330 }
331 }
332
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)333 bool TSEnumScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
334 [[maybe_unused]] ScriptExtension extension)
335 {
336 ASSERT(newDecl->Type() == DeclType::ENUM);
337 return enumMemberBindings_->insert({newDecl->Name(), allocator->New<EnumVariable>(newDecl, false)}).second;
338 }
339
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)340 bool GlobalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
341 [[maybe_unused]] ScriptExtension extension)
342 {
343 switch (newDecl->Type()) {
344 case DeclType::VAR: {
345 return AddVar<GlobalVariable>(allocator, currentVariable, newDecl);
346 }
347 case DeclType::FUNC: {
348 return AddFunction<GlobalVariable>(allocator, currentVariable, newDecl, extension);
349 }
350 case DeclType::CLASS: {
351 return AddClass<LocalVariable>(allocator, currentVariable, newDecl);
352 }
353 case DeclType::ENUM_LITERAL: {
354 return AddTSBinding<EnumLiteralVariable>(allocator, newDecl, VariableFlags::ENUM_LITERAL);
355 }
356 case DeclType::NAMESPACE: {
357 return AddTSBinding<NamespaceVariable>(allocator, newDecl, VariableFlags::NAMESPACE);
358 }
359 case DeclType::IMPORT_EQUALS: {
360 return AddTSBinding<ImportEqualsVariable>(allocator, newDecl, VariableFlags::IMPORT_EQUALS);
361 }
362 case DeclType::INTERFACE: {
363 return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
364 }
365 default: {
366 return AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
367 }
368 }
369
370 return true;
371 }
372
373 // ModuleScope
374
ConvertLocalVariableToModuleVariable(ArenaAllocator * allocator,util::StringView localName)375 void ModuleScope::ConvertLocalVariableToModuleVariable(ArenaAllocator *allocator, util::StringView localName)
376 {
377 auto res = bindings_.find(localName);
378 // Since the module's exported [localName] has been validated before,
379 // [localName] must have a binding now.
380 ASSERT(res != bindings_.end());
381 if (!res->second->IsModuleVariable()) {
382 auto *decl = res->second->Declaration();
383 decl->AddFlag(DeclarationFlags::EXPORT);
384 VariableFlags flags = res->second->Flags();
385 res->second = allocator->New<ModuleVariable>(decl, flags | VariableFlags::LOCAL_EXPORT);
386 }
387 }
388
AssignIndexToModuleVariable(util::StringView name,uint32_t index)389 void ModuleScope::AssignIndexToModuleVariable(util::StringView name, uint32_t index)
390 {
391 auto *moduleVar = FindLocal(name);
392 ASSERT(moduleVar != nullptr);
393 ASSERT(moduleVar->IsModuleVariable());
394 moduleVar->AsModuleVariable()->AssignIndex(index);
395 }
396
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)397 bool ModuleScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
398 [[maybe_unused]] ScriptExtension extension)
399 {
400 switch (newDecl->Type()) {
401 case DeclType::VAR: {
402 auto [scope, shadowed] = IterateShadowedVariables(
403 newDecl->Name(), [](const Variable *v) { return !v->HasFlag(VariableFlags::VAR); });
404
405 if (shadowed) {
406 return false;
407 }
408 return newDecl->IsImportOrExportDecl() ?
409 AddVar<ModuleVariable>(allocator, currentVariable, newDecl) :
410 AddVar<LocalVariable>(allocator, currentVariable, newDecl);
411 }
412 case DeclType::FUNC: {
413 if (currentVariable) {
414 auto decl = currentVariable->Declaration();
415 if (!decl->IsClassDecl() || !decl->AsClassDecl()->IsDeclare()) {
416 return false;
417 }
418 }
419 return newDecl->IsImportOrExportDecl() ?
420 AddFunction<ModuleVariable>(allocator, currentVariable, newDecl, extension) :
421 AddFunction<LocalVariable>(allocator, currentVariable, newDecl, extension);
422 }
423 case DeclType::CLASS: {
424 return newDecl->IsImportOrExportDecl() ?
425 AddClass<ModuleVariable>(allocator, currentVariable, newDecl) :
426 AddClass<LocalVariable>(allocator, currentVariable, newDecl);
427 }
428 case DeclType::ENUM_LITERAL: {
429 return AddTSBinding<EnumLiteralVariable>(allocator, newDecl, VariableFlags::ENUM_LITERAL);
430 }
431 case DeclType::NAMESPACE: {
432 return AddTSBinding<NamespaceVariable>(allocator, newDecl, VariableFlags::NAMESPACE);
433 }
434 case DeclType::IMPORT_EQUALS: {
435 return AddTSBinding<ImportEqualsVariable>(allocator, newDecl, VariableFlags::IMPORT_EQUALS);
436 }
437 case DeclType::INTERFACE: {
438 return AddTSBinding<LocalVariable>(allocator, currentVariable, newDecl, VariableFlags::INTERFACE);
439 }
440 default: {
441 if (currentVariable) {
442 return false;
443 }
444 return newDecl->IsImportOrExportDecl() ?
445 AddLexical<ModuleVariable>(allocator, currentVariable, newDecl) :
446 AddLexical<LocalVariable>(allocator, currentVariable, newDecl);
447 }
448 }
449 }
450
451 // LocalScope
452
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)453 bool LocalScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
454 [[maybe_unused]] ScriptExtension extension)
455 {
456 return AddLocal(allocator, currentVariable, newDecl, extension);
457 }
458
InitVariable()459 void LoopScope::InitVariable()
460 {
461 for (const auto &[name, var] : bindings_) {
462 if (!var->Declaration()->IsLetOrConstOrClassDecl()) {
463 continue;
464 }
465
466 var->AddFlag(VariableFlags::INITIALIZED);
467 if (var->LexicalBound()) {
468 var->AddFlag(VariableFlags::PER_ITERATION);
469 }
470 }
471 }
472
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)473 bool CatchParamScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
474 [[maybe_unused]] ScriptExtension extension)
475 {
476 return AddParam(allocator, currentVariable, newDecl, VariableFlags::INITIALIZED);
477 }
478
AddBinding(ArenaAllocator * allocator,Variable * currentVariable,Decl * newDecl,ScriptExtension extension)479 bool CatchScope::AddBinding(ArenaAllocator *allocator, Variable *currentVariable, Decl *newDecl,
480 [[maybe_unused]] ScriptExtension extension)
481 {
482 if (!newDecl->IsVarDecl() && paramScope_->FindLocal(newDecl->Name())) {
483 return false;
484 }
485
486 return AddLocal(allocator, currentVariable, newDecl, extension);
487 }
488
489 } // namespace panda::es2panda::binder
490