• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "identifier.h"
17 
18 #include "checker/ETSchecker.h"
19 #include "checker/TSchecker.h"
20 #include "compiler/core/pandagen.h"
21 #include "compiler/core/ETSGen.h"
22 
23 namespace ark::es2panda::ir {
Identifier(Tag const tag,Identifier const & other,ArenaAllocator * const allocator)24 Identifier::Identifier([[maybe_unused]] Tag const tag, Identifier const &other, ArenaAllocator *const allocator)
25     : AnnotatedExpression(static_cast<AnnotatedExpression const &>(other), allocator), decorators_(allocator->Adapter())
26 {
27     name_ = other.name_;
28     flags_ = other.flags_;
29 
30     for (auto *decorator : other.decorators_) {
31         decorators_.emplace_back(decorator->Clone(allocator, this));
32     }
33 }
34 
Identifier(ArenaAllocator * const allocator)35 Identifier::Identifier(ArenaAllocator *const allocator) : Identifier(ERROR_LITERAL, allocator)
36 {
37     flags_ |= IdentifierFlags::ERROR_PLACEHOLDER;
38 }
39 
Identifier(util::StringView const name,ArenaAllocator * const allocator)40 Identifier::Identifier(util::StringView const name, ArenaAllocator *const allocator)
41     : AnnotatedExpression(AstNodeType::IDENTIFIER), name_(name), decorators_(allocator->Adapter())
42 {
43     if (name == ERROR_LITERAL) {
44         flags_ |= IdentifierFlags::ERROR_PLACEHOLDER;
45     }
46 }
47 
Identifier(util::StringView const name,TypeNode * const typeAnnotation,ArenaAllocator * const allocator)48 Identifier::Identifier(util::StringView const name, TypeNode *const typeAnnotation, ArenaAllocator *const allocator)
49     : AnnotatedExpression(AstNodeType::IDENTIFIER, typeAnnotation), name_(name), decorators_(allocator->Adapter())
50 {
51     if (name == ERROR_LITERAL) {
52         flags_ |= IdentifierFlags::ERROR_PLACEHOLDER;
53     }
54 }
55 
SetName(const util::StringView & newName)56 void Identifier::SetName(const util::StringView &newName) noexcept
57 {
58     ES2PANDA_ASSERT(newName != ERROR_LITERAL);
59     name_ = newName;
60 }
61 
Clone(ArenaAllocator * const allocator,AstNode * const parent)62 Identifier *Identifier::Clone(ArenaAllocator *const allocator, AstNode *const parent)
63 {
64     auto *const clone = allocator->New<Identifier>(Tag {}, *this, allocator);
65     ES2PANDA_ASSERT(clone != nullptr);
66 
67     clone->SetTsType(TsType());
68     if (parent != nullptr) {
69         clone->SetParent(parent);
70     }
71 
72     clone->SetRange(Range());
73     return clone;
74 }
75 
CloneReference(ArenaAllocator * const allocator,AstNode * const parent)76 Identifier *Identifier::CloneReference(ArenaAllocator *const allocator, AstNode *const parent)
77 {
78     auto *const clone = Clone(allocator, parent);
79     ES2PANDA_ASSERT(clone != nullptr);
80     if (clone->IsReference(ScriptExtension::ETS)) {
81         clone->SetTsTypeAnnotation(nullptr);
82     }
83     return clone;
84 }
85 
TransformChildren(const NodeTransformer & cb,std::string_view const transformationName)86 void Identifier::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
87 {
88     if (auto *typeAnnotation = TypeAnnotation(); typeAnnotation != nullptr) {
89         if (auto *transformedNode = cb(typeAnnotation); typeAnnotation != transformedNode) {
90             typeAnnotation->SetTransformedNode(transformationName, transformedNode);
91             SetTsTypeAnnotation(static_cast<TypeNode *>(transformedNode));
92         }
93     }
94 
95     for (auto *&it : VectorIterationGuard(decorators_)) {
96         if (auto *transformedNode = cb(it); it != transformedNode) {
97             it->SetTransformedNode(transformationName, transformedNode);
98             it = transformedNode->AsDecorator();
99         }
100     }
101 }
102 
Iterate(const NodeTraverser & cb) const103 void Identifier::Iterate(const NodeTraverser &cb) const
104 {
105     if (TypeAnnotation() != nullptr) {
106         cb(TypeAnnotation());
107     }
108 
109     for (auto *it : VectorIterationGuard(decorators_)) {
110         cb(it);
111     }
112 }
113 
ValidateExpression()114 ValidationInfo Identifier::ValidateExpression()
115 {
116     if ((flags_ & IdentifierFlags::OPTIONAL) != 0U) {
117         return {"Unexpected token '?'.", Start()};
118     }
119 
120     if (TypeAnnotation() != nullptr) {
121         return {"Unexpected token.", TypeAnnotation()->Start()};
122     }
123 
124     ValidationInfo info;
125     return info;
126 }
127 
Dump(ir::AstDumper * dumper) const128 void Identifier::Dump(ir::AstDumper *dumper) const
129 {
130     dumper->Add({{"type", IsPrivateIdent() ? "PrivateIdentifier" : "Identifier"},
131                  {"name", name_},
132                  {"typeAnnotation", AstDumper::Optional(TypeAnnotation())},
133                  {"optional", AstDumper::Optional(IsOptional())},
134                  {"decorators", decorators_}});
135 }
136 
Dump(ir::SrcDumper * dumper) const137 void Identifier::Dump(ir::SrcDumper *dumper) const
138 {
139     if (IsReceiver()) {
140         dumper->Add("this");
141         return;
142     }
143 
144     if (IsPrivateIdent()) {
145         dumper->Add("private ");
146     }
147 
148     auto name = std::string(name_);
149     std::string propertyStr = compiler::Signatures::PROPERTY.data();
150     if (UNLIKELY(name.find(propertyStr) != std::string::npos)) {
151         name.replace(name.find(propertyStr), propertyStr.length(), "_$property$_");
152     }
153     dumper->Add(name);
154     if (IsOptional()) {
155         dumper->Add("?");
156     }
157 
158     dumper->PushTask([dumper, name = std::string(name_)] { dumper->DumpNode(name); });
159 }
160 
Compile(compiler::PandaGen * pg) const161 void Identifier::Compile(compiler::PandaGen *pg) const
162 {
163     pg->GetAstCompiler()->Compile(this);
164 }
165 
Compile(compiler::ETSGen * etsg) const166 void Identifier::Compile(compiler::ETSGen *etsg) const
167 {
168     etsg->GetAstCompiler()->Compile(this);
169 }
170 
Check(checker::TSChecker * checker)171 checker::Type *Identifier::Check(checker::TSChecker *checker)
172 {
173     return checker->GetAnalyzer()->Check(this);
174 }
175 
Check(checker::ETSChecker * checker)176 checker::VerifiedType Identifier::Check(checker::ETSChecker *checker)
177 {
178     return {this, checker->GetAnalyzer()->Check(this)};
179 }
180 
IsDeclaration(ScriptExtension ext) const181 bool Identifier::IsDeclaration(ScriptExtension ext) const
182 {
183     // We can determine reference status from parent node
184     if (Parent() == nullptr) {
185         return false;
186     }
187 
188     auto const *const parent = Parent();
189 
190     // We need two Parts because check is too long to fit in one function
191     if (CheckDeclarationsPart1(parent, ext) || CheckDeclarationsPart2(parent, ext)) {
192         return true;
193     }
194 
195     // This will check other cases that are not related to declarations
196     return CheckNotDeclarations(parent, ext);
197 }
198 
CheckNotDeclarations(const ir::AstNode * parent,ScriptExtension ext) const199 bool Identifier::CheckNotDeclarations(const ir::AstNode *parent, [[maybe_unused]] ScriptExtension ext) const
200 {
201     if (parent->IsTSMethodSignature() && ext == ScriptExtension::TS) {
202         // NOTE(kkonkuznetsov): fix in TS
203         // Example: c(a: (a: number, b: void) => string, b?: number[]): undefined;
204         // a, b should not be references
205         return false;
206     }
207 
208     // Parameters in methods are not references
209     // Example:
210     // function foo(a: int)
211     if (parent->IsETSParameterExpression()) {
212         return true;
213     }
214 
215     if (parent->IsTSPropertySignature() && ext == ScriptExtension::TS) {
216         // Example:
217         //
218         // interface foo {
219         //     a: number,
220         // }
221         auto *prop = parent->AsTSPropertySignature();
222         if (prop->Key() == this) {
223             return true;
224         }
225     }
226 
227     if (parent->IsClassProperty()) {
228         // RHS is a reference
229         // Example: const foo1: (self: Object, x: int) => int = foo;
230         // foo here is a reference
231         auto *prop = parent->AsClassProperty();
232         return !(prop->Value() == this);
233     }
234 
235     // Identifier in catch is not a reference
236     // Example:
237     //
238     // _try_{
239     //   _throw_new_Exception();}_catch_(e)_{}
240     if (parent->IsCatchClause()) {
241         return true;
242     }
243 
244     // Type Parameter is not a reference
245     // Example:
246     // interface X <K> {}
247     if (parent->IsTSTypeParameter()) {
248         return true;
249     }
250 
251     // Rest Parameter is not a reference
252     // Example:
253     // class A {
254     //    constructor(... items :Object[]){}
255     // }
256     if (parent->IsRestElement()) {
257         if (ext == ScriptExtension::TS) {
258             // NOTE(kkonkuznetsov): fix in TS
259             // Example: var func8: { (...c): boolean, (...c: number[]): string };
260             // c is expected to be a reference
261             return false;
262         }
263 
264         if (ext == ScriptExtension::JS) {
265             // NOTE(kkonkuznetsov): some JS tests have the following code:
266             //
267             // let x;
268             // async function* fn() {
269             //   for await ([...x] of [g()]) {
270             //   }
271             // }
272             // Here in `[...x]` x is parsed as Rest Element,
273             // however x is expected to be a reference.
274             // Otherwise the tests fail.
275             return !(parent->Parent() != nullptr && parent->Parent()->IsArrayPattern());
276         }
277 
278         return true;
279     }
280 
281     // Script function identifiers are not references
282     // Example:
283     // public static foo()
284     if (parent->IsScriptFunction()) {
285         if (ext == ScriptExtension::TS) {
286             // NOTE(kkonkuznetsov): fix in TS
287             // Example: let c = (a?: number) => { }
288             return false;
289         }
290 
291         if (ext == ScriptExtension::JS) {
292             // In some JS tests the following code:
293             //
294             // let_f_=_()_=>_o;
295             //
296             // creates AST that has Identifier as Body
297             return !(parent->AsScriptFunction()->Body() == this);
298         }
299 
300         return true;
301     }
302 
303     // Helper function to reduce function size
304     return CheckDefinitions(parent, ext);
305 }
306 
CheckDefinitions(const ir::AstNode * parent,ScriptExtension ext) const307 bool Identifier::CheckDefinitions(const ir::AstNode *parent, [[maybe_unused]] ScriptExtension ext) const
308 {
309     // New methods are not references
310     if (parent->IsMethodDefinition()) {
311         return true;
312     }
313 
314     // New classes are not references
315     if (parent->IsClassDefinition()) {
316         if (ext == ScriptExtension::JS) {
317             // Example from JS tests:
318             // inner Outer is a reference
319             //
320             // class Outer {
321             //   innerclass() {
322             //     return class extends Outer {};
323             //   }
324             // }
325             auto *def = parent->AsClassDefinition();
326             return !(def->Super() == this);
327         }
328 
329         return true;
330     }
331 
332     return false;
333 }
334 
CheckDeclarationsPart1(const ir::AstNode * parent,ScriptExtension ext) const335 bool Identifier::CheckDeclarationsPart1(const ir::AstNode *parent, [[maybe_unused]] ScriptExtension ext) const
336 {
337     // All declarations are not references
338     if (parent->IsVariableDeclarator()) {
339         if (ext == ScriptExtension::TS) {
340             // NOTE(kkonkuznetsov): fix in TS
341             // All variable declarations in TS are expected to be references for some reason
342             return false;
343         }
344 
345         auto *decl = parent->AsVariableDeclarator();
346 
347         // Example: let goo = foo;
348         // RHS is a reference
349         return !(decl->Init() == this);
350     }
351 
352     // All declarations are not references
353     if (parent->IsVariableDeclaration()) {
354         return true;
355     }
356 
357     if (parent->IsClassDeclaration()) {
358         return true;
359     }
360 
361     if (parent->IsETSPackageDeclaration()) {
362         return true;
363     }
364 
365     if (parent->IsFunctionDeclaration()) {
366         return true;
367     }
368 
369     if (parent->IsImportDeclaration()) {
370         return true;
371     }
372 
373     if (parent->IsETSImportDeclaration()) {
374         return true;
375     }
376 
377     if (parent->IsETSStructDeclaration()) {
378         return true;
379     }
380 
381     if (parent->IsExportAllDeclaration()) {
382         return true;
383     }
384 
385     if (parent->IsExportDefaultDeclaration()) {
386         return true;
387     }
388 
389     if (Parent()->IsClassDefinition()) {
390         return Parent()->AsClassDefinition()->IsGlobal();
391     }
392 
393     return false;
394 }
395 
CheckDeclarationsPart2(const ir::AstNode * parent,ScriptExtension ext) const396 bool Identifier::CheckDeclarationsPart2(const ir::AstNode *parent, ScriptExtension ext) const
397 {
398     // All declarations are not references
399     if (parent->IsExportNamedDeclaration()) {
400         return true;
401     }
402 
403     if (parent->IsTSEnumDeclaration()) {
404         // NOTE(kkonkuznetsov): fix in TS
405         // Should not be a reference
406         // However currently some TS tests depend on declaration ident being a reference
407         return !(ext == ScriptExtension::TS);
408     }
409 
410     if (parent->IsTSInterfaceDeclaration()) {
411         // NOTE(kkonkuznetsov): This should not be a reference
412         // Example:
413         //
414         // interface foo {
415         //     a: number,
416         // }
417         //
418         // However currently some TS tests depend on interface ident being a reference
419         return !(ext == ScriptExtension::TS);
420     }
421 
422     if (parent->IsTSModuleDeclaration()) {
423         return true;
424     }
425 
426     if (parent->IsTSSignatureDeclaration()) {
427         // NOTE(kkonkuznetsov): fix in TS
428         // Example: new(a: null, b?: string): { a: number, b: string, c?([a, b]): string }
429         // a, b should not be references
430         return !(ext == ScriptExtension::TS);
431     }
432 
433     if (parent->IsETSReExportDeclaration()) {
434         return true;
435     }
436 
437     if (parent->IsTSImportEqualsDeclaration()) {
438         return true;
439     }
440 
441     if (parent->IsTSTypeAliasDeclaration()) {
442         return true;
443     }
444 
445     if (parent->IsTSTypeParameterDeclaration()) {
446         return true;
447     }
448 
449     if (parent->IsDeclare()) {
450         return true;
451     }
452 
453     if (parent->Parent() != nullptr) {
454         if (parent->Parent()->IsTSEnumDeclaration()) {
455             return true;
456         }
457     }
458 
459     return false;
460 }
461 
ToString() const462 std::string Identifier::ToString() const
463 {
464     if (!Name().Empty() && !Name().Is(ERROR_LITERAL)) {
465         return std::string {Name().Utf8()};
466     }
467 
468     return AnnotatedExpression::ToString();
469 }
470 }  // namespace ark::es2panda::ir
471