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