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