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