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 "helpers.h"
17
18 #include "generated/signatures.h"
19 #include "varbinder/privateBinding.h"
20 #include "checker/types/ets/types.h"
21 #include "ir/astNode.h"
22 #include "ir/base/classDefinition.h"
23 #include "ir/base/classProperty.h"
24 #include "ir/base/methodDefinition.h"
25 #include "ir/base/property.h"
26 #include "ir/base/scriptFunction.h"
27 #include "ir/base/spreadElement.h"
28 #include "ir/expressions/arrayExpression.h"
29 #include "ir/expressions/assignmentExpression.h"
30 #include "ir/expressions/callExpression.h"
31 #include "ir/expressions/functionExpression.h"
32 #include "ir/expressions/identifier.h"
33 #include "ir/expressions/literals/numberLiteral.h"
34 #include "ir/expressions/literals/stringLiteral.h"
35 #include "ir/expressions/literals/booleanLiteral.h"
36 #include "ir/expressions/literals/nullLiteral.h"
37 #include "ir/expressions/objectExpression.h"
38 #include "ir/statements/returnStatement.h"
39 #include "ir/statements/variableDeclaration.h"
40 #include "ir/statements/variableDeclarator.h"
41 #include "ir/module/importSpecifier.h"
42 #include "ir/ets/etsImportDeclaration.h"
43 #include "ir/ts/tsParameterProperty.h"
44 #include "ir/ts/tsInterfaceDeclaration.h"
45 #include "ir/ts/tsEnumDeclaration.h"
46 #include "ir/ets/etsParameterExpression.h"
47 #include "ir/module/importDeclaration.h"
48 #include "lexer/token/letters.h"
49 #include "libpandabase/utils/utf.h"
50 #include "libpandabase/os/filesystem.h"
51
52 namespace ark::es2panda::util {
53 // Helpers
54
IsGlobalIdentifier(const util::StringView & str)55 bool Helpers::IsGlobalIdentifier(const util::StringView &str)
56 {
57 return (str.Is("NaN") || str.Is("undefined") || str.Is("Infinity"));
58 }
59
ContainSpreadElement(const ArenaVector<ir::Expression * > & args)60 bool Helpers::ContainSpreadElement(const ArenaVector<ir::Expression *> &args)
61 {
62 return std::any_of(args.begin(), args.end(), [](const auto *it) { return it->IsSpreadElement(); });
63 }
64
LiteralToPropName(const ir::Expression * lit)65 util::StringView Helpers::LiteralToPropName(const ir::Expression *lit)
66 {
67 switch (lit->Type()) {
68 case ir::AstNodeType::IDENTIFIER: {
69 return lit->AsIdentifier()->Name();
70 }
71 case ir::AstNodeType::STRING_LITERAL: {
72 return lit->AsStringLiteral()->Str();
73 }
74 case ir::AstNodeType::NUMBER_LITERAL: {
75 return lit->AsNumberLiteral()->Str();
76 }
77 case ir::AstNodeType::NULL_LITERAL: {
78 return "null";
79 }
80 case ir::AstNodeType::UNDEFINED_LITERAL: {
81 return "undefined";
82 }
83 default: {
84 UNREACHABLE();
85 }
86 }
87 }
88
IsIndex(double number)89 bool Helpers::IsIndex(double number)
90 {
91 if (number >= 0 && number < static_cast<double>(INVALID_INDEX)) {
92 auto intNum = static_cast<uint32_t>(number);
93
94 if (static_cast<double>(intNum) == number) {
95 return true;
96 }
97 }
98
99 return false;
100 }
101
IsDigit(char c)102 static bool IsDigit(char c)
103 {
104 return (c >= '0' && c <= '9');
105 }
106
GetIndex(const util::StringView & str)107 int64_t Helpers::GetIndex(const util::StringView &str)
108 {
109 const auto &s = str.Utf8();
110
111 if (s.empty() || (*s.begin() == '0' && s.length() > 1)) {
112 return INVALID_INDEX;
113 }
114
115 int64_t value = 0;
116 for (const auto c : s) {
117 if (!IsDigit(c)) {
118 return INVALID_INDEX;
119 }
120
121 constexpr auto MULTIPLIER = 10;
122 value *= MULTIPLIER;
123 value += (c - '0');
124
125 if (value >= INVALID_INDEX) {
126 return INVALID_INDEX;
127 }
128 }
129
130 return value;
131 }
132
ToString(double number)133 std::string Helpers::ToString(double number)
134 {
135 std::string str;
136
137 if (Helpers::IsInteger<int32_t>(number)) {
138 str = std::to_string(static_cast<int32_t>(number));
139 } else {
140 str = std::to_string(number);
141 }
142
143 return str;
144 }
145
ToStringView(ArenaAllocator * allocator,double number)146 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, double number)
147 {
148 util::UString str(ToString(number), allocator);
149 return str.View();
150 }
151
ToStringView(ArenaAllocator * allocator,uint32_t number)152 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, uint32_t number)
153 {
154 ASSERT(number <= static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
155 return ToStringView(allocator, static_cast<int32_t>(number));
156 }
157
ToStringView(ArenaAllocator * allocator,int32_t number)158 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, int32_t number)
159 {
160 util::UString str(ToString(number), allocator);
161 return str.View();
162 }
163
EndsWith(const std::string & str,const std::string & suffix)164 bool Helpers::EndsWith(const std::string &str, const std::string &suffix)
165 {
166 if (str.length() < suffix.length()) {
167 return false;
168 }
169 size_t expectPos = str.length() - suffix.length();
170 return str.find(suffix, expectPos) == expectPos;
171 }
172
GetContainingConstructor(const ir::AstNode * node)173 const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::AstNode *node)
174 {
175 const ir::ScriptFunction *iter = GetContainingFunction(node);
176
177 while (iter != nullptr) {
178 if (iter->IsConstructor()) {
179 return iter;
180 }
181
182 if (!iter->IsArrow()) {
183 return nullptr;
184 }
185
186 iter = GetContainingFunction(iter);
187 }
188
189 return iter;
190 }
191
GetContainingEnumDeclaration(const ir::AstNode * node)192 const ir::TSEnumDeclaration *Helpers::GetContainingEnumDeclaration(const ir::AstNode *node)
193 {
194 auto *iter = node;
195
196 while (iter != nullptr) {
197 if (iter->IsTSEnumDeclaration()) {
198 return iter->AsTSEnumDeclaration();
199 }
200
201 iter = iter->Parent();
202 }
203
204 return nullptr;
205 }
206
GetContainingObjectType(const ir::AstNode * node)207 const checker::ETSObjectType *Helpers::GetContainingObjectType(const ir::AstNode *node)
208 {
209 const auto *iter = node;
210
211 while (iter != nullptr) {
212 if (iter->IsClassDefinition()) {
213 auto *ret = iter->AsClassDefinition()->TsType();
214 return ret != nullptr ? ret->AsETSObjectType() : nullptr;
215 }
216
217 if (iter->IsTSInterfaceDeclaration()) {
218 auto *ret = iter->AsTSInterfaceDeclaration()->TsType();
219 return ret != nullptr ? ret->AsETSObjectType() : nullptr;
220 }
221
222 if (iter->IsTSEnumDeclaration()) {
223 auto *ret = iter->AsTSEnumDeclaration()->TsType();
224 return ret != nullptr ? ret->AsETSObjectType() : nullptr;
225 }
226
227 iter = iter->Parent();
228 }
229
230 return nullptr;
231 }
232
GetContainingClassDefinition(const ir::AstNode * node)233 const ir::ClassDefinition *Helpers::GetContainingClassDefinition(const ir::AstNode *node)
234 {
235 const auto *iter = node;
236
237 while (iter != nullptr) {
238 if (iter->IsClassDefinition()) {
239 return iter->AsClassDefinition();
240 }
241
242 iter = iter->Parent();
243 }
244
245 return nullptr;
246 }
247
GetContainingInterfaceDeclaration(const ir::AstNode * node)248 const ir::TSInterfaceDeclaration *Helpers::GetContainingInterfaceDeclaration(const ir::AstNode *node)
249 {
250 const auto *iter = node;
251
252 while (iter != nullptr) {
253 if (iter->IsTSInterfaceDeclaration()) {
254 return iter->AsTSInterfaceDeclaration();
255 }
256
257 iter = iter->Parent();
258 }
259
260 return nullptr;
261 }
262
GetContainingClassMethodDefinition(const ir::AstNode * node)263 const ir::MethodDefinition *Helpers::GetContainingClassMethodDefinition(const ir::AstNode *node)
264 {
265 const auto *iter = node;
266
267 while (iter != nullptr) {
268 if (iter->IsMethodDefinition()) {
269 return iter->AsMethodDefinition();
270 }
271
272 if (iter->IsClassDefinition()) {
273 break;
274 }
275
276 iter = iter->Parent();
277 }
278
279 return nullptr;
280 }
281
GetContainingClassStaticBlock(const ir::AstNode * node)282 const ir::ClassStaticBlock *Helpers::GetContainingClassStaticBlock(const ir::AstNode *node)
283 {
284 const auto *iter = node;
285
286 while (iter != nullptr) {
287 if (iter->IsClassStaticBlock()) {
288 return iter->AsClassStaticBlock();
289 }
290
291 if (iter->IsClassDefinition()) {
292 break;
293 }
294
295 iter = iter->Parent();
296 }
297
298 return nullptr;
299 }
300
GetContainingConstructor(const ir::ClassProperty * node)301 const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::ClassProperty *node)
302 {
303 for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
304 if (parent->IsClassDefinition()) {
305 ASSERT(parent->AsClassDefinition()->Ctor() != nullptr);
306 return parent->AsClassDefinition()->Ctor()->Function();
307 }
308 }
309
310 return nullptr;
311 }
312
GetContainingFunction(const ir::AstNode * node)313 const ir::ScriptFunction *Helpers::GetContainingFunction(const ir::AstNode *node)
314 {
315 for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
316 if (parent->IsScriptFunction()) {
317 return parent->AsScriptFunction();
318 }
319 }
320
321 return nullptr;
322 }
323
GetClassDefiniton(const ir::ScriptFunction * node)324 const ir::ClassDefinition *Helpers::GetClassDefiniton(const ir::ScriptFunction *node)
325 {
326 ASSERT(node->IsConstructor());
327 ASSERT(node->Parent()->IsFunctionExpression());
328 ASSERT(node->Parent()->Parent()->IsMethodDefinition());
329 ASSERT(node->Parent()->Parent()->Parent()->IsClassDefinition());
330
331 return node->Parent()->Parent()->Parent()->AsClassDefinition();
332 }
333
IsSpecialPropertyKey(const ir::Expression * expr)334 bool Helpers::IsSpecialPropertyKey(const ir::Expression *expr)
335 {
336 if (!expr->IsStringLiteral()) {
337 return false;
338 }
339
340 auto *lit = expr->AsStringLiteral();
341 return lit->Str().Is("prototype") || lit->Str().Is("constructor");
342 }
343
IsConstantPropertyKey(const ir::Expression * expr,bool isComputed)344 bool Helpers::IsConstantPropertyKey(const ir::Expression *expr, bool isComputed)
345 {
346 switch (expr->Type()) {
347 case ir::AstNodeType::IDENTIFIER: {
348 return !isComputed;
349 }
350 case ir::AstNodeType::NUMBER_LITERAL:
351 case ir::AstNodeType::STRING_LITERAL:
352 case ir::AstNodeType::BOOLEAN_LITERAL:
353 case ir::AstNodeType::NULL_LITERAL: {
354 return true;
355 }
356 default:
357 break;
358 }
359
360 return false;
361 }
362
ToConstantLiteral(const ir::Expression * expr)363 compiler::Literal Helpers::ToConstantLiteral(const ir::Expression *expr)
364 {
365 switch (expr->Type()) {
366 case ir::AstNodeType::NUMBER_LITERAL: {
367 auto *lit = expr->AsNumberLiteral();
368 if (util::Helpers::IsInteger<uint32_t>(lit->Number().GetDouble())) {
369 return compiler::Literal(static_cast<uint32_t>(lit->Number().GetDouble()));
370 }
371 return compiler::Literal(lit->Number().GetDouble());
372 }
373 case ir::AstNodeType::STRING_LITERAL: {
374 auto *lit = expr->AsStringLiteral();
375 return compiler::Literal(lit->Str());
376 }
377 case ir::AstNodeType::BOOLEAN_LITERAL: {
378 auto *lit = expr->AsBooleanLiteral();
379 return compiler::Literal(lit->Value());
380 }
381 case ir::AstNodeType::NULL_LITERAL: {
382 return compiler::Literal::NullLiteral();
383 }
384 case ir::AstNodeType::UNDEFINED_LITERAL: {
385 return compiler::Literal::UndefinedLiteral();
386 }
387 default:
388 break;
389 }
390
391 return compiler::Literal();
392 }
393
IsBindingPattern(const ir::AstNode * node)394 bool Helpers::IsBindingPattern(const ir::AstNode *node)
395 {
396 return node->IsArrayPattern() || node->IsObjectPattern();
397 }
398
IsPattern(const ir::AstNode * node)399 bool Helpers::IsPattern(const ir::AstNode *node)
400 {
401 return node->IsArrayPattern() || node->IsObjectPattern() || node->IsAssignmentPattern();
402 }
403
CollectBindingName(ir::AstNode * node,std::vector<ir::Identifier * > * bindings)404 static void CollectBindingName(ir::AstNode *node, std::vector<ir::Identifier *> *bindings)
405 {
406 switch (node->Type()) {
407 case ir::AstNodeType::IDENTIFIER: {
408 if (!Helpers::IsGlobalIdentifier(node->AsIdentifier()->Name())) {
409 bindings->push_back(node->AsIdentifier());
410 }
411
412 break;
413 }
414 case ir::AstNodeType::OBJECT_PATTERN: {
415 for (auto *prop : node->AsObjectPattern()->Properties()) {
416 CollectBindingName(prop, bindings);
417 }
418 break;
419 }
420 case ir::AstNodeType::ARRAY_PATTERN: {
421 for (auto *element : node->AsArrayPattern()->Elements()) {
422 CollectBindingName(element, bindings);
423 }
424 break;
425 }
426 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
427 CollectBindingName(node->AsAssignmentPattern()->Left(), bindings);
428 break;
429 }
430 case ir::AstNodeType::PROPERTY: {
431 CollectBindingName(node->AsProperty()->Value(), bindings);
432 break;
433 }
434 case ir::AstNodeType::REST_ELEMENT: {
435 CollectBindingName(node->AsRestElement()->Argument(), bindings);
436 break;
437 }
438 default:
439 break;
440 }
441 }
442
CollectBindingNames(ir::AstNode * node)443 std::vector<ir::Identifier *> Helpers::CollectBindingNames(ir::AstNode *node)
444 {
445 std::vector<ir::Identifier *> bindings;
446 CollectBindingName(node, &bindings);
447 return bindings;
448 }
449
CheckImportedName(const ArenaVector<ir::ImportSpecifier * > & specifiers,const ir::ImportSpecifier * specifier,const std::string & fileName)450 void Helpers::CheckImportedName(const ArenaVector<ir::ImportSpecifier *> &specifiers,
451 const ir::ImportSpecifier *specifier, const std::string &fileName)
452 {
453 auto newIdentName = specifier->Imported()->Name();
454 auto newAliasName = specifier->Local()->Name();
455 std::stringstream message {};
456
457 for (auto *it : specifiers) {
458 auto savedIdentName = it->Imported()->Name();
459 auto savedAliasName = it->Local()->Name();
460 if (savedIdentName == savedAliasName && savedAliasName == newIdentName) {
461 message << "Warning: '" << newIdentName << "' has already imported ";
462 break;
463 }
464 if (savedIdentName == newIdentName && newAliasName != savedAliasName) {
465 message << "Warning: '" << newIdentName << "' is explicitly used with alias several times ";
466 break;
467 }
468 }
469
470 if (message.rdbuf()->in_avail() > 0) {
471 std::cerr << message.str() << "[" << fileName.c_str() << ":" << specifier->Start().line << ":"
472 << specifier->Start().index << "]" << std::endl;
473 }
474 }
475
FunctionNameFromParent(const ir::AstNode * parent,ArenaAllocator * allocator)476 static util::StringView FunctionNameFromParent(const ir::AstNode *parent, ArenaAllocator *allocator)
477 {
478 switch (parent->Type()) {
479 case ir::AstNodeType::VARIABLE_DECLARATOR: {
480 const ir::VariableDeclarator *varDecl = parent->AsVariableDeclarator();
481
482 if (varDecl->Id()->IsIdentifier()) {
483 return varDecl->Id()->AsIdentifier()->Name();
484 }
485
486 break;
487 }
488 case ir::AstNodeType::METHOD_DEFINITION: {
489 const ir::MethodDefinition *methodDef = parent->AsMethodDefinition();
490
491 if (methodDef->Key()->IsIdentifier()) {
492 auto *ident = methodDef->Id();
493 ASSERT(ident != nullptr);
494
495 if (!ident->IsPrivateIdent()) {
496 return ident->Name();
497 }
498
499 return util::UString(varbinder::PrivateBinding::ToPrivateBinding(ident->Name()), allocator).View();
500 }
501
502 break;
503 }
504 case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
505 const ir::AssignmentExpression *assignment = parent->AsAssignmentExpression();
506
507 if (assignment->Left()->IsIdentifier()) {
508 return assignment->Left()->AsIdentifier()->Name();
509 }
510
511 break;
512 }
513 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
514 const ir::AssignmentExpression *assignment = parent->AsAssignmentPattern();
515
516 if (assignment->Left()->IsIdentifier()) {
517 return assignment->Left()->AsIdentifier()->Name();
518 }
519
520 break;
521 }
522 case ir::AstNodeType::PROPERTY: {
523 const ir::Property *prop = parent->AsProperty();
524
525 if (prop->Kind() != ir::PropertyKind::PROTO &&
526 Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) {
527 return Helpers::LiteralToPropName(prop->Key());
528 }
529
530 break;
531 }
532 default:
533 break;
534 }
535
536 return util::StringView();
537 }
538
FunctionName(ArenaAllocator * allocator,const ir::ScriptFunction * func)539 util::StringView Helpers::FunctionName(ArenaAllocator *allocator, const ir::ScriptFunction *func)
540 {
541 if (func->Id() != nullptr) {
542 return func->Id()->Name();
543 }
544
545 if (func->Parent()->IsFunctionDeclaration()) {
546 return "*default*";
547 }
548
549 const ir::AstNode *parent = func->Parent()->Parent();
550
551 if (func->IsConstructor()) {
552 parent = parent->Parent();
553 if (parent->AsClassDefinition()->Ident() != nullptr) {
554 return parent->AsClassDefinition()->Ident()->Name();
555 }
556
557 parent = parent->Parent()->Parent();
558 }
559
560 return FunctionNameFromParent(parent, allocator);
561 }
562
ParamName(ArenaAllocator * allocator,const ir::AstNode * param,uint32_t index)563 std::tuple<util::StringView, bool> Helpers::ParamName(ArenaAllocator *allocator, const ir::AstNode *param,
564 uint32_t index)
565 {
566 switch (param->Type()) {
567 case ir::AstNodeType::IDENTIFIER: {
568 return {param->AsIdentifier()->Name(), false};
569 }
570 case ir::AstNodeType::ASSIGNMENT_PATTERN: {
571 const auto *lhs = param->AsAssignmentPattern()->Left();
572 if (lhs->IsIdentifier()) {
573 return {param->AsAssignmentPattern()->Left()->AsIdentifier()->Name(), false};
574 }
575 break;
576 }
577 case ir::AstNodeType::REST_ELEMENT: {
578 if (param->AsRestElement()->Argument()->IsIdentifier()) {
579 return {param->AsRestElement()->Argument()->AsIdentifier()->Name(), false};
580 }
581 break;
582 }
583 case ir::AstNodeType::TS_PARAMETER_PROPERTY: {
584 return ParamName(allocator, param->AsTSParameterProperty()->Parameter(), index);
585 }
586 case ir::AstNodeType::ETS_PARAMETER_EXPRESSION: {
587 return {param->AsETSParameterExpression()->Ident()->Name(), false};
588 }
589 default:
590 break;
591 }
592
593 return {Helpers::ToStringView(allocator, index), true};
594 }
595
CreateEscapedString(const std::string & str)596 std::string Helpers::CreateEscapedString(const std::string &str)
597 {
598 std::string ret {};
599 ret.reserve(str.size());
600
601 for (std::string::value_type c : str) {
602 switch (c) {
603 case lexer::LEX_CHAR_BS: {
604 ret.append("\\b");
605 break;
606 }
607 case lexer::LEX_CHAR_TAB: {
608 ret.append("\\t");
609 break;
610 }
611 case lexer::LEX_CHAR_LF: {
612 ret.append("\\n");
613 break;
614 }
615 case lexer::LEX_CHAR_VT: {
616 ret.append("\\v");
617 break;
618 }
619 case lexer::LEX_CHAR_FF: {
620 ret.append("\\f");
621 break;
622 }
623 case lexer::LEX_CHAR_CR: {
624 ret.append("\\r");
625 break;
626 }
627 default: {
628 ret += c;
629 break;
630 }
631 }
632 }
633
634 return ret;
635 }
636
UTF16toUTF8(const char16_t c)637 std::string Helpers::UTF16toUTF8(const char16_t c)
638 {
639 const utf::Utf8Char utf8Ch = utf::ConvertUtf16ToUtf8(c, 0, false);
640 return std::string(reinterpret_cast<const char *>(utf8Ch.ch.data()), utf8Ch.n);
641 }
642
SplitSignature(std::string_view signature)643 std::pair<std::string_view, std::string_view> Helpers::SplitSignature(std::string_view signature)
644 {
645 auto idx = signature.find_last_of(':');
646 auto stripped = signature.substr(0, idx);
647 idx = stripped.find_last_of('.');
648 auto fullClassName = stripped.substr(0, idx);
649 auto methodName = stripped.substr(idx + 1);
650 idx = fullClassName.find_last_of('.');
651 auto className = fullClassName.substr(idx + 1);
652 return {className, methodName};
653 }
654
StdLib()655 std::vector<std::string> &Helpers::StdLib()
656 {
657 static std::vector<std::string> stdlib {"std/core", "std/math", "std/containers", "std/time",
658 "std/interop/js", "std/debug", "std/debug/concurrency", "escompat"};
659 return stdlib;
660 }
661
IsStdLib(const parser::Program * program)662 bool Helpers::IsStdLib(const parser::Program *program)
663 {
664 const auto &stdlib = StdLib();
665
666 // NOTE(rsipka): early check: if program is not a package module then it is not part of the stdlib either
667 if (!program->IsPackageModule()) {
668 return false;
669 }
670
671 auto fileFolder = program->ModuleName().Mutf8();
672 std::replace(fileFolder.begin(), fileFolder.end(), *compiler::Signatures::METHOD_SEPARATOR.begin(),
673 *compiler::Signatures::NAMESPACE_SEPARATOR.begin());
674 return std::count(stdlib.begin(), stdlib.end(), fileFolder) != 0;
675 }
676
677 } // namespace ark::es2panda::util
678