• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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 #include "ir/astDump.h"
23 #include "ir/srcDump.h"
24 
25 namespace ark::es2panda::ir {
Identifier(Tag const tag,Identifier const & other,ArenaAllocator * const allocator)26 Identifier::Identifier([[maybe_unused]] Tag const tag, Identifier const &other, ArenaAllocator *const allocator)
27     : AnnotatedExpression(static_cast<AnnotatedExpression const &>(other), allocator), decorators_(allocator->Adapter())
28 {
29     name_ = other.name_;
30     flags_ = other.flags_;
31 
32     for (auto *decorator : other.decorators_) {
33         decorators_.emplace_back(decorator->Clone(allocator, this));
34     }
35 }
36 
Clone(ArenaAllocator * const allocator,AstNode * const parent)37 Identifier *Identifier::Clone(ArenaAllocator *const allocator, AstNode *const parent)
38 {
39     if (auto *const clone = allocator->New<Identifier>(Tag {}, *this, allocator); clone != nullptr) {
40         clone->SetTsType(TsType());
41         if (parent != nullptr) {
42             clone->SetParent(parent);
43         }
44 
45         clone->SetRange(Range());
46 
47         return clone;
48     }
49     throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
50 }
51 
CloneReference(ArenaAllocator * const allocator,AstNode * const parent)52 Identifier *Identifier::CloneReference(ArenaAllocator *const allocator, AstNode *const parent)
53 {
54     if (auto *const clone = allocator->New<Identifier>(Tag {}, *this, allocator); clone != nullptr) {
55         clone->SetTsType(TsType());
56         if (parent != nullptr) {
57             clone->SetParent(parent);
58         }
59 
60         clone->SetRange(Range());
61 
62         if (clone->IsReference(ScriptExtension::ETS) && (clone->TypeAnnotation() != nullptr)) {
63             clone->SetTsTypeAnnotation(nullptr);
64         }
65 
66         return clone;
67     }
68     throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
69 }
70 
TransformChildren(const NodeTransformer & cb,std::string_view const transformationName)71 void Identifier::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
72 {
73     if (auto *typeAnnotation = TypeAnnotation(); typeAnnotation != nullptr) {
74         if (auto *transformedNode = cb(typeAnnotation); typeAnnotation != transformedNode) {
75             typeAnnotation->SetTransformedNode(transformationName, transformedNode);
76             SetTsTypeAnnotation(static_cast<TypeNode *>(transformedNode));
77         }
78     }
79 
80     for (auto *&it : decorators_) {
81         if (auto *transformedNode = cb(it); it != transformedNode) {
82             it->SetTransformedNode(transformationName, transformedNode);
83             it = transformedNode->AsDecorator();
84         }
85     }
86 }
87 
Iterate(const NodeTraverser & cb) const88 void Identifier::Iterate(const NodeTraverser &cb) const
89 {
90     if (TypeAnnotation() != nullptr) {
91         cb(TypeAnnotation());
92     }
93 
94     for (auto *it : decorators_) {
95         cb(it);
96     }
97 }
98 
ValidateExpression()99 ValidationInfo Identifier::ValidateExpression()
100 {
101     if ((flags_ & IdentifierFlags::OPTIONAL) != 0U) {
102         return {"Unexpected token '?'.", Start()};
103     }
104 
105     if (TypeAnnotation() != nullptr) {
106         return {"Unexpected token.", TypeAnnotation()->Start()};
107     }
108 
109     ValidationInfo info;
110     return info;
111 }
112 
Dump(ir::AstDumper * dumper) const113 void Identifier::Dump(ir::AstDumper *dumper) const
114 {
115     dumper->Add({{"type", IsPrivateIdent() ? "PrivateIdentifier" : "Identifier"},
116                  {"name", name_},
117                  {"typeAnnotation", AstDumper::Optional(TypeAnnotation())},
118                  {"optional", AstDumper::Optional(IsOptional())},
119                  {"decorators", decorators_}});
120 }
121 
Dump(ir::SrcDumper * dumper) const122 void Identifier::Dump(ir::SrcDumper *dumper) const
123 {
124     if (IsPrivateIdent()) {
125         dumper->Add("private ");
126     }
127 
128     dumper->Add(std::string(name_));
129     if (IsOptional()) {
130         dumper->Add("?");
131     }
132 }
133 
Compile(compiler::PandaGen * pg) const134 void Identifier::Compile(compiler::PandaGen *pg) const
135 {
136     pg->GetAstCompiler()->Compile(this);
137 }
138 
Compile(compiler::ETSGen * etsg) const139 void Identifier::Compile(compiler::ETSGen *etsg) const
140 {
141     etsg->GetAstCompiler()->Compile(this);
142 }
143 
Check(checker::TSChecker * checker)144 checker::Type *Identifier::Check(checker::TSChecker *checker)
145 {
146     return checker->GetAnalyzer()->Check(this);
147 }
148 
Check(checker::ETSChecker * checker)149 checker::Type *Identifier::Check(checker::ETSChecker *checker)
150 {
151     return checker->GetAnalyzer()->Check(this);
152 }
153 
IsDeclaration(ScriptExtension ext) const154 bool Identifier::IsDeclaration(ScriptExtension ext) const
155 {
156     // GLOBAL class is not a reference
157     if (Name() == "ETSGLOBAL") {
158         return true;
159     }
160 
161     // We can determine reference status from parent node
162     if (Parent() == nullptr) {
163         return false;
164     }
165 
166     auto const *const parent = Parent();
167 
168     // We need two Parts because check is too long to fit in one function
169     if (CheckDeclarationsPart1(parent, ext) || CheckDeclarationsPart2(parent, ext)) {
170         return true;
171     }
172 
173     // This will check other cases that are not related to declarations
174     return CheckNotDeclarations(parent, ext);
175 }
176 
CheckNotDeclarations(const ir::AstNode * parent,ScriptExtension ext) const177 bool Identifier::CheckNotDeclarations(const ir::AstNode *parent, [[maybe_unused]] ScriptExtension ext) const
178 {
179     if (parent->IsTSMethodSignature() && ext == ScriptExtension::TS) {
180         // NOTE(kkonkuznetsov): fix in TS
181         // Example: c(a: (a: number, b: void) => string, b?: number[]): undefined;
182         // a, b should not be references
183         return false;
184     }
185 
186     // Parameters in methods are not references
187     // Example:
188     // function foo(a: int)
189     if (parent->IsETSParameterExpression()) {
190         return true;
191     }
192 
193     if (parent->IsTSPropertySignature() && ext == ScriptExtension::TS) {
194         // Example:
195         //
196         // interface foo {
197         //     a: number,
198         // }
199         auto *prop = parent->AsTSPropertySignature();
200         if (prop->Key() == this) {
201             return true;
202         }
203     }
204 
205     if (parent->IsClassProperty()) {
206         // RHS is a reference
207         // Example: const foo1: (self: Object, x: int) => int = foo;
208         // foo here is a reference
209         auto *prop = parent->AsClassProperty();
210         return !(prop->Value() == this);
211     }
212 
213     // Identifier in catch is not a reference
214     // Example:
215     //
216     // _try_{
217     //   _throw_new_Exception();}_catch_(e)_{}
218     if (parent->IsCatchClause()) {
219         return true;
220     }
221 
222     // Type Parameter is not a reference
223     // Example:
224     // interface X <K> {}
225     if (parent->IsTSTypeParameter()) {
226         return true;
227     }
228 
229     // Rest Parameter is not a reference
230     // Example:
231     // class A {
232     //    constructor(... items :Object[]){}
233     // }
234     if (parent->IsRestElement()) {
235         if (ext == ScriptExtension::TS) {
236             // NOTE(kkonkuznetsov): fix in TS
237             // Example: var func8: { (...c): boolean, (...c: number[]): string };
238             // c is expected to be a reference
239             return false;
240         }
241 
242         if (ext == ScriptExtension::JS) {
243             // NOTE(kkonkuznetsov): some JS tests have the following code:
244             //
245             // let x;
246             // async function* fn() {
247             //   for await ([...x] of [g()]) {
248             //   }
249             // }
250             // Here in `[...x]` x is parsed as Rest Element,
251             // however x is expected to be a reference.
252             // Otherwise the tests fail.
253             return !(parent->Parent() != nullptr && parent->Parent()->IsArrayPattern());
254         }
255 
256         return true;
257     }
258 
259     // Script function identifiers are not references
260     // Example:
261     // public static foo()
262     if (parent->IsScriptFunction()) {
263         if (ext == ScriptExtension::TS) {
264             // NOTE(kkonkuznetsov): fix in TS
265             // Example: let c = (a?: number) => { }
266             return false;
267         }
268 
269         if (ext == ScriptExtension::JS) {
270             // In some JS tests the following code:
271             //
272             // let_f_=_()_=>_o;
273             //
274             // creates AST that has Identifier as Body
275             return !(parent->AsScriptFunction()->Body() == this);
276         }
277 
278         return true;
279     }
280 
281     // Helper function to reduce function size
282     return CheckDefinitions(parent, ext);
283 }
284 
CheckDefinitions(const ir::AstNode * parent,ScriptExtension ext) const285 bool Identifier::CheckDefinitions(const ir::AstNode *parent, [[maybe_unused]] ScriptExtension ext) const
286 {
287     // New methods are not references
288     if (parent->IsMethodDefinition()) {
289         return true;
290     }
291 
292     // New classes are not references
293     if (parent->IsClassDefinition()) {
294         if (ext == ScriptExtension::JS) {
295             // Example from JS tests:
296             // inner Outer is a reference
297             //
298             // class Outer {
299             //   innerclass() {
300             //     return class extends Outer {};
301             //   }
302             // }
303             auto *def = parent->AsClassDefinition();
304             return !(def->Super() == this);
305         }
306 
307         return true;
308     }
309 
310     return false;
311 }
312 
CheckDeclarationsPart1(const ir::AstNode * parent,ScriptExtension ext) const313 bool Identifier::CheckDeclarationsPart1(const ir::AstNode *parent, [[maybe_unused]] ScriptExtension ext) const
314 {
315     // All declarations are not references
316     if (parent->IsVariableDeclarator()) {
317         if (ext == ScriptExtension::TS) {
318             // NOTE(kkonkuznetsov): fix in TS
319             // All variable declarations in TS are expected to be references for some reason
320             return false;
321         }
322 
323         auto *decl = parent->AsVariableDeclarator();
324 
325         // Example: let goo = foo;
326         // RHS is a reference
327         return !(decl->Init() == this);
328     }
329 
330     // All declarations are not references
331     if (parent->IsVariableDeclaration()) {
332         return true;
333     }
334 
335     if (parent->IsClassDeclaration()) {
336         return true;
337     }
338 
339     if (parent->IsETSPackageDeclaration()) {
340         return true;
341     }
342 
343     if (parent->IsFunctionDeclaration()) {
344         return true;
345     }
346 
347     if (parent->IsImportDeclaration()) {
348         return true;
349     }
350 
351     if (parent->IsETSImportDeclaration()) {
352         return true;
353     }
354 
355     if (parent->IsETSStructDeclaration()) {
356         return true;
357     }
358 
359     if (parent->IsExportAllDeclaration()) {
360         return true;
361     }
362 
363     if (parent->IsExportDefaultDeclaration()) {
364         return true;
365     }
366 
367     return false;
368 }
369 
CheckDeclarationsPart2(const ir::AstNode * parent,ScriptExtension ext) const370 bool Identifier::CheckDeclarationsPart2(const ir::AstNode *parent, ScriptExtension ext) const
371 {
372     // All declarations are not references
373     if (parent->IsExportNamedDeclaration()) {
374         return true;
375     }
376 
377     if (parent->IsTSEnumDeclaration()) {
378         // NOTE(kkonkuznetsov): fix in TS
379         // Should not be a reference
380         // However currently some TS tests depend on declaration ident being a reference
381         return !(ext == ScriptExtension::TS);
382     }
383 
384     if (parent->IsTSInterfaceDeclaration()) {
385         // NOTE(kkonkuznetsov): This should not be a reference
386         // Example:
387         //
388         // interface foo {
389         //     a: number,
390         // }
391         //
392         // However currently some TS tests depend on interface ident being a reference
393         return !(ext == ScriptExtension::TS);
394     }
395 
396     if (parent->IsTSModuleDeclaration()) {
397         return true;
398     }
399 
400     if (parent->IsTSSignatureDeclaration()) {
401         // NOTE(kkonkuznetsov): fix in TS
402         // Example: new(a: null, b?: string): { a: number, b: string, c?([a, b]): string }
403         // a, b should not be references
404         return !(ext == ScriptExtension::TS);
405     }
406 
407     if (parent->IsETSReExportDeclaration()) {
408         return true;
409     }
410 
411     if (parent->IsTSImportEqualsDeclaration()) {
412         return true;
413     }
414 
415     if (parent->IsTSTypeAliasDeclaration()) {
416         return true;
417     }
418 
419     if (parent->IsTSTypeParameterDeclaration()) {
420         return true;
421     }
422 
423     if (parent->IsDeclare()) {
424         return true;
425     }
426 
427     if (parent->Parent() != nullptr) {
428         if (parent->Parent()->IsTSEnumDeclaration()) {
429             return true;
430         }
431     }
432 
433     return false;
434 }
435 
436 }  // namespace ark::es2panda::ir
437