• 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 "helpers.h"
17 
18 #include <abc2program/common/abc_file_utils.h>
19 #include <ir/base/classDefinition.h>
20 #include <ir/base/property.h>
21 #include <ir/base/scriptFunction.h>
22 #include <ir/base/spreadElement.h>
23 #include <ir/expressions/arrayExpression.h>
24 #include <ir/expressions/assignmentExpression.h>
25 #include <ir/expressions/functionExpression.h>
26 #include <ir/expressions/literals/booleanLiteral.h>
27 #include <ir/expressions/literals/numberLiteral.h>
28 #include <ir/expressions/literals/stringLiteral.h>
29 #include <ir/expressions/objectExpression.h>
30 #include <ir/expressions/unaryExpression.h>
31 #include <ir/statements/blockStatement.h>
32 #include <ir/statements/expressionStatement.h>
33 #include <ir/statements/variableDeclarator.h>
34 #include <ir/ts/tsParameterProperty.h>
35 #include <util/commonUtil.h>
36 #include <util/concurrent.h>
37 
38 #ifdef ENABLE_BYTECODE_OPT
39 #include <bytecode_optimizer/bytecode_analysis_results.h>
40 #include <bytecode_optimizer/optimize_bytecode.h>
41 #else
42 #include <assembly-type.h>
43 #include <assembly-program.h>
44 #include <assembly-emitter.h>
45 #endif
46 
47 #ifdef PANDA_TARGET_WINDOWS
48 #include <windows.h>
49 #undef ERROR
50 #else
51 #include <unistd.h>
52 #endif
53 
54 namespace panda::es2panda::util {
55 
56 // Helpers
57 
IsGlobalIdentifier(const util::StringView & str)58 bool Helpers::IsGlobalIdentifier(const util::StringView &str)
59 {
60     return (str.Is("NaN") || str.Is("undefined") || str.Is("Infinity"));
61 }
62 
ContainSpreadElement(const ArenaVector<ir::Expression * > & args)63 bool Helpers::ContainSpreadElement(const ArenaVector<ir::Expression *> &args)
64 {
65     return std::any_of(args.begin(), args.end(), [](const auto *it) { return it->IsSpreadElement(); });
66 }
67 
LiteralToPropName(ArenaAllocator * allocator,const ir::Expression * lit)68 util::StringView Helpers::LiteralToPropName(ArenaAllocator *allocator, const ir::Expression *lit)
69 {
70     switch (lit->Type()) {
71         case ir::AstNodeType::IDENTIFIER: {
72             return lit->AsIdentifier()->Name();
73         }
74         case ir::AstNodeType::STRING_LITERAL: {
75             return lit->AsStringLiteral()->Str();
76         }
77         case ir::AstNodeType::NUMBER_LITERAL: {
78             return Helpers::ToStringView(allocator, lit->AsNumberLiteral()->Number());
79         }
80         case ir::AstNodeType::NULL_LITERAL: {
81             return "null";
82         }
83         case ir::AstNodeType::BOOLEAN_LITERAL: {
84             if (lit->AsBooleanLiteral()->Value()) {
85                 return "true";
86             }
87             return "false";
88         }
89         default: {
90             UNREACHABLE();
91         }
92     }
93 }
94 
IsIndex(double number)95 bool Helpers::IsIndex(double number)
96 {
97     if (number >= 0 && number < static_cast<double>(INVALID_INDEX)) {
98         auto intNum = static_cast<uint32_t>(number);
99 
100         if (static_cast<double>(intNum) == number) {
101             return true;
102         }
103     }
104 
105     return false;
106 }
107 
IsDigit(char c)108 static bool IsDigit(char c)
109 {
110     return (c >= '0' && c <= '9');
111 }
112 
GetIndex(const util::StringView & str)113 int64_t Helpers::GetIndex(const util::StringView &str)
114 {
115     const auto &s = str.Utf8();
116 
117     if (s.empty() || (*s.begin() == '0' && s.length() > 1)) {
118         return INVALID_INDEX;
119     }
120 
121     int64_t value = 0;
122     for (const auto c : s) {
123         if (!IsDigit(c)) {
124             return INVALID_INDEX;
125         }
126 
127         constexpr auto MULTIPLIER = 10;
128         value *= MULTIPLIER;
129         value += (c - '0');
130 
131         if (value >= INVALID_INDEX) {
132             return INVALID_INDEX;
133         }
134     }
135 
136     return value;
137 }
138 
FileExtensionIs(std::string_view filePath,std::string_view extension)139 bool Helpers::FileExtensionIs(std::string_view filePath, std::string_view extension)
140 {
141     return filePath.length() > extension.length() && Helpers::EndsWith(filePath, extension);
142 }
143 
EndsWith(std::string_view str,std::string_view suffix)144 bool Helpers::EndsWith(std::string_view str, std::string_view suffix)
145 {
146     if (str.length() < suffix.length()) {
147         return false;
148     }
149     size_t expectPos = str.length() - suffix.length();
150     return str.find(suffix, expectPos) == expectPos;
151 }
152 
GetScientificNotationForDouble(double number,uint32_t significandBitCount,int32_t & numberBitCount,char * significandArray,char * sciNotationArray,uint32_t size)153 void Helpers::GetScientificNotationForDouble(double number, uint32_t significandBitCount, int32_t &numberBitCount,
154                                              char *significandArray, char *sciNotationArray, uint32_t size)
155 {
156     if (size < MAX_DOUBLE_DIGIT) {
157         std::cerr << "Failed to set the size of buffer, the buffer size provided (" << size
158                   << ") is less than the required minimum size (" << MAX_DOUBLE_DIGIT
159                   << ") for formatting the number in scientific notation." << std::endl;
160         return;
161     }
162     if (snprintf_s(sciNotationArray, size, size - 1, "%.*e", significandBitCount - 1, number) == FAIL_SNPRINTF_S) {
163         std::cerr << "Failed to format the number " << number
164                   << " into scientific notation using snprintf_s. Please check if the buffer size (" << size
165                   << ") and significand bit count (" << significandBitCount << ") are appropriate." << std::endl;
166         return;
167     }
168 
169     // sciNotationArray includes significand, '.' and 'e'
170     // If significandBitCount == 1, sciNotationArray does not contain '.'
171     int32_t exponent = atoi(sciNotationArray + significandBitCount + 1 + (significandBitCount > 1));
172     numberBitCount = exponent + 1;
173 
174     // Get the significand of the current sciNotationArray
175     if (significandBitCount > 1) {
176         for (uint32_t i = 0; i < significandBitCount + 1; i++) {
177             significandArray[i] = sciNotationArray[i];
178         }
179     }
180 
181     significandArray[significandBitCount + 1] = '\0';
182 }
183 
GetIntegerSignificandBitCount(double number,int32_t & numberBitCount,char * significandArray)184 int32_t Helpers::GetIntegerSignificandBitCount(double number, int32_t &numberBitCount, char *significandArray)
185 {
186     uint32_t bitPos = 0;
187     uint32_t minBitPos = 1;
188     uint32_t maxBitPos = MAX_DOUBLE_PRECISION_DIGIT;
189     uint32_t integerAndPointBitCount = 2;
190     char sciNotationArray[MAX_DOUBLE_DIGIT] = {0};
191 
192     while (minBitPos < maxBitPos) {
193         bitPos = (minBitPos + maxBitPos) / 2; // 2: binary search
194         GetScientificNotationForDouble(number, bitPos, numberBitCount, significandArray,
195                                        sciNotationArray, sizeof(sciNotationArray));
196 
197         // Update bitPos
198         if (std::strtod(sciNotationArray, nullptr) == number) {
199             while (bitPos >= integerAndPointBitCount && significandArray[bitPos] == '0') {
200                 bitPos--;
201             }
202             maxBitPos = bitPos;
203         } else {
204             minBitPos = bitPos + 1;
205         }
206     }
207 
208     // minBitPos == maxBitPos
209     bitPos = maxBitPos;
210     GetScientificNotationForDouble(number, bitPos, numberBitCount, significandArray,
211                                    sciNotationArray, sizeof(sciNotationArray));
212 
213     return bitPos;
214 }
215 
DoubleToString(double number)216 std::string Helpers::DoubleToString(double number)
217 {
218     // In Scientific notation, number is expressed in the form of significand multiplied by exponent-th power of 10.
219     // The range of significand is: 1 <= |significand| < 10
220     // Scientific notation of number: sciNotationArray = significand * (10 ** exponent)
221     // number 1.23e25 => sciNotationArray: 1.23e+25, significand: 1.23, exponent: 25,
222 
223     // In the ECMAScript, integerSignificand, integerSignificandBitCount and numberBitCount are defined as an integer:
224     // 1. integerSignificand is an integer in the Decimal representation of Scientific notation.
225     // 2. integerSignificandBitCount is the number of bits in the Decimal representation of significand.
226     // 3. numberBitCount is the number of bits in the Decimal representation of number.
227     // Scientific notation of number in the ECMAScript of Number::toString (number):
228     // integerSciNotationArray = integerSignificand * (10 ** (numberBitCount - integerSignificandBitCount))
229     // number 1.23e25 => integerSciNotationArray: 123e+23, integerSignificand: 123, integerExponent: 23,
230     //                   integerSignificandBitCount: 3, numberBitCount: 26
231     std::string result;
232     int32_t numberBitCount = 0;
233     char significandArray[MAX_DOUBLE_DIGIT] = {0};
234 
235     if (number < 0) {
236         result += "-";
237         number = -number;
238     }
239 
240     // The number of bits of significand in integer form
241     int32_t integerSignificandBitCount = GetIntegerSignificandBitCount(number, numberBitCount, significandArray);
242 
243     std::string significand = significandArray;
244     std::string integerSignificand;
245     if (numberBitCount > 0 && numberBitCount <= MAX_DECIMAL_EXPONENT) {
246         integerSignificand = significand.erase(1, 1);
247         if (numberBitCount >= integerSignificandBitCount) {
248             // If integerSignificandBitCount ≤ numberBitCount ≤ 21, return the string represented by Decimal,
249             // integerSignificand followed by (numberBitCount - integerSignificandBitCount) digit zeros.
250             integerSignificand += std::string(numberBitCount - integerSignificandBitCount, '0');
251         } else {
252             // If 0 < numberBitCount < integerSignificandBitCount, return the string represented by Decimal,
253             // integerSignificand followed by point on the (numberBitCount + 1) digit.
254             integerSignificand.insert(numberBitCount, 1, '.');
255         }
256     } else if (numberBitCount <= 0 && numberBitCount > MIN_DECIMAL_EXPONENT) {
257         // If -6 < numberBitCount ≤ 0, return the string consisting of "0." and digit zeros represented by Decimal,
258         // string followed by integerSignificand.
259         integerSignificand = significand.erase(1, 1);
260         integerSignificand = std::string("0.") + std::string(-numberBitCount, '0') + integerSignificand;
261     } else {
262         // If integerSignificandBitCount == 1, return the string consisting of the single digit of significand.
263         if (integerSignificandBitCount == 1) {
264             significand = significand.erase(1, 1);
265         }
266         // If numberBitCount ≤ -6 or numberBitCount > 21, return the string represented by Scientific notation,
267         // integerSignificand followed by "e", symbol and (numberBitCount - 1) digit zeros.
268         significand += 'e' + (numberBitCount >= 1 ? std::string("+") : "") + std::to_string(numberBitCount - 1);
269 
270         result += significand;
271         return result;
272     }
273 
274     result += integerSignificand;
275     return result;
276 }
277 
ToString(double number)278 std::string Helpers::ToString(double number)
279 {
280     if (std::isnan(number)) {
281         return "NaN";
282     }
283     if (number == 0.0) {
284         return "0";
285     }
286     if (std::isinf(number)) {
287         return "Infinity";
288     }
289 
290     std::string str;
291     if (Helpers::IsInteger<int32_t>(number)) {
292         str = std::to_string(static_cast<int32_t>(number));
293     } else {
294         str = DoubleToString(number);
295     }
296 
297     return str;
298 }
299 
ToStringView(ArenaAllocator * allocator,double number)300 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, double number)
301 {
302     util::UString str(ToString(number), allocator);
303     return str.View();
304 }
305 
ToStringView(ArenaAllocator * allocator,uint32_t number)306 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, uint32_t number)
307 {
308     ASSERT(number <= static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
309     return ToStringView(allocator, static_cast<int32_t>(number));
310 }
311 
ToStringView(ArenaAllocator * allocator,int32_t number)312 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, int32_t number)
313 {
314     util::UString str(ToString(number), allocator);
315     return str.View();
316 }
317 
GetContainingConstructor(const ir::AstNode * node)318 const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::AstNode *node)
319 {
320     const ir::ScriptFunction *iter = GetContainingFunction(node);
321 
322     while (iter != nullptr) {
323         if (iter->IsConstructor()) {
324             return iter;
325         }
326 
327         if (!iter->IsArrow()) {
328             return nullptr;
329         }
330 
331         iter = GetContainingFunction(iter);
332     }
333 
334     return iter;
335 }
336 
GetContainingConstructor(const ir::ClassProperty * node)337 const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::ClassProperty *node)
338 {
339     for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
340         if (parent->IsClassDefinition()) {
341             return parent->AsClassDefinition()->Ctor()->Function();
342         }
343     }
344 
345     return nullptr;
346 }
347 
GetContainingFunction(const ir::AstNode * node)348 const ir::ScriptFunction *Helpers::GetContainingFunction(const ir::AstNode *node)
349 {
350     for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
351         if (parent->IsScriptFunction()) {
352             return parent->AsScriptFunction();
353         }
354     }
355 
356     return nullptr;
357 }
358 
GetClassDefiniton(const ir::ScriptFunction * node)359 const ir::ClassDefinition *Helpers::GetClassDefiniton(const ir::ScriptFunction *node)
360 {
361     CHECK_NOT_NULL(node);
362     ASSERT(node->IsConstructor() || node->IsMethod());
363     ASSERT(node->Parent()->IsFunctionExpression());
364     ASSERT(node->Parent()->Parent()->IsMethodDefinition());
365     ASSERT(node->Parent()->Parent()->Parent()->IsClassDefinition());
366 
367     return node->Parent()->Parent()->Parent()->AsClassDefinition();
368 }
369 
IsSpecialPropertyKey(const ir::Expression * expr)370 bool Helpers::IsSpecialPropertyKey(const ir::Expression *expr)
371 {
372     if (!expr->IsStringLiteral()) {
373         return false;
374     }
375 
376     auto *lit = expr->AsStringLiteral();
377     return lit->Str().Is("prototype") || lit->Str().Is("constructor");
378 }
379 
IsConstantPropertyKey(const ir::Expression * expr,bool isComputed)380 bool Helpers::IsConstantPropertyKey(const ir::Expression *expr, bool isComputed)
381 {
382     switch (expr->Type()) {
383         case ir::AstNodeType::IDENTIFIER: {
384             return !isComputed;
385         }
386         case ir::AstNodeType::NUMBER_LITERAL:
387         case ir::AstNodeType::STRING_LITERAL:
388         case ir::AstNodeType::BOOLEAN_LITERAL:
389         case ir::AstNodeType::NULL_LITERAL: {
390             return true;
391         }
392         default:
393             break;
394     }
395 
396     return false;
397 }
398 
IsConstantExpr(const ir::Expression * expr)399 bool Helpers::IsConstantExpr(const ir::Expression *expr)
400 {
401     switch (expr->Type()) {
402         case ir::AstNodeType::NUMBER_LITERAL:
403         case ir::AstNodeType::STRING_LITERAL:
404         case ir::AstNodeType::BOOLEAN_LITERAL:
405         case ir::AstNodeType::NULL_LITERAL: {
406             return true;
407         }
408         default:
409             break;
410     }
411 
412     return false;
413 }
414 
IsBindingPattern(const ir::AstNode * node)415 bool Helpers::IsBindingPattern(const ir::AstNode *node)
416 {
417     return node->IsArrayPattern() || node->IsObjectPattern();
418 }
419 
IsPattern(const ir::AstNode * node)420 bool Helpers::IsPattern(const ir::AstNode *node)
421 {
422     return node->IsArrayPattern() || node->IsObjectPattern() || node->IsAssignmentPattern();
423 }
424 
CollectBindingName(const ir::AstNode * node,std::vector<const ir::Identifier * > * bindings)425 static void CollectBindingName(const ir::AstNode *node, std::vector<const ir::Identifier *> *bindings)
426 {
427     switch (node->Type()) {
428         case ir::AstNodeType::IDENTIFIER: {
429             if (!Helpers::IsGlobalIdentifier(node->AsIdentifier()->Name())) {
430                 bindings->push_back(node->AsIdentifier());
431             }
432 
433             break;
434         }
435         case ir::AstNodeType::OBJECT_PATTERN: {
436             for (const auto *prop : node->AsObjectPattern()->Properties()) {
437                 CollectBindingName(prop, bindings);
438             }
439             break;
440         }
441         case ir::AstNodeType::ARRAY_PATTERN: {
442             for (const auto *element : node->AsArrayPattern()->Elements()) {
443                 CollectBindingName(element, bindings);
444             }
445             break;
446         }
447         case ir::AstNodeType::ASSIGNMENT_PATTERN: {
448             CollectBindingName(node->AsAssignmentPattern()->Left(), bindings);
449             break;
450         }
451         case ir::AstNodeType::PROPERTY: {
452             CollectBindingName(node->AsProperty()->Value(), bindings);
453             break;
454         }
455         case ir::AstNodeType::REST_ELEMENT: {
456             CollectBindingName(node->AsRestElement()->Argument(), bindings);
457             break;
458         }
459         default:
460             break;
461     }
462 }
463 
CollectBindingNames(const ir::AstNode * node)464 std::vector<const ir::Identifier *> Helpers::CollectBindingNames(const ir::AstNode *node)
465 {
466     std::vector<const ir::Identifier *> bindings;
467     CollectBindingName(node, &bindings);
468     return bindings;
469 }
470 
FunctionName(ArenaAllocator * allocator,const ir::ScriptFunction * func)471 util::StringView Helpers::FunctionName(ArenaAllocator *allocator, const ir::ScriptFunction *func)
472 {
473     if (func->Id()) {
474         return func->Id()->Name();
475     }
476 
477     if (func->Parent()->IsFunctionDeclaration()) {
478         return parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME;
479     }
480 
481     const ir::AstNode *parent = func->Parent()->Parent();
482 
483     if (func->IsConstructor()) {
484         parent = parent->Parent();
485         if (parent->AsClassDefinition()->Ident()) {
486             return parent->AsClassDefinition()->Ident()->Name();
487         }
488 
489         parent = parent->Parent()->Parent();
490     }
491     return GetName(allocator, parent);
492 }
493 
GetName(ArenaAllocator * allocator,const ir::AstNode * node)494 util::StringView Helpers::GetName(ArenaAllocator *allocator, const ir::AstNode *node)
495 {
496     switch (node->Type()) {
497         case ir::AstNodeType::VARIABLE_DECLARATOR: {
498             const ir::VariableDeclarator *varDecl = node->AsVariableDeclarator();
499 
500             if (varDecl->Id()->IsIdentifier()) {
501                 return varDecl->Id()->AsIdentifier()->Name();
502             }
503 
504             break;
505         }
506         case ir::AstNodeType::METHOD_DEFINITION: {
507             const ir::MethodDefinition *methodDef = node->AsMethodDefinition();
508 
509             if (methodDef->Key()->IsIdentifier()) {
510                 return methodDef->Key()->AsIdentifier()->Name();
511             }
512 
513             break;
514         }
515         case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
516             const ir::AssignmentExpression *assignment = node->AsAssignmentExpression();
517 
518             if (assignment->Left()->IsIdentifier()) {
519                 return assignment->Left()->AsIdentifier()->Name();
520             }
521 
522             break;
523         }
524         case ir::AstNodeType::ASSIGNMENT_PATTERN: {
525             const ir::AssignmentExpression *assignment = node->AsAssignmentPattern();
526 
527             if (assignment->Left()->IsIdentifier()) {
528                 return assignment->Left()->AsIdentifier()->Name();
529             }
530 
531             break;
532         }
533         case ir::AstNodeType::PROPERTY: {
534             const ir::Property *prop = node->AsProperty();
535 
536             if (prop->Kind() != ir::PropertyKind::PROTO &&
537                 Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) {
538                 return Helpers::LiteralToPropName(allocator, prop->Key());
539             }
540 
541             break;
542         }
543         case ir::AstNodeType::CLASS_PROPERTY: {
544             const ir::ClassProperty *prop = node->AsClassProperty();
545             if (Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) {
546                 return Helpers::LiteralToPropName(allocator, prop->Key());
547             }
548 
549             break;
550         }
551         case ir::AstNodeType::EXPORT_DEFAULT_DECLARATION: {
552             return parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME;
553         }
554         default:
555             break;
556     }
557 
558     return util::StringView();
559 }
560 
ParamName(ArenaAllocator * allocator,const ir::AstNode * param,uint32_t index)561 std::tuple<util::StringView, bool> Helpers::ParamName(ArenaAllocator *allocator, const ir::AstNode *param,
562                                                       uint32_t index)
563 {
564     switch (param->Type()) {
565         case ir::AstNodeType::IDENTIFIER: {
566             return {param->AsIdentifier()->Name(), false};
567         }
568         case ir::AstNodeType::ASSIGNMENT_PATTERN: {
569             const auto *lhs = param->AsAssignmentPattern()->Left();
570             if (lhs->IsIdentifier()) {
571                 return {param->AsAssignmentPattern()->Left()->AsIdentifier()->Name(), false};
572             }
573             break;
574         }
575         case ir::AstNodeType::REST_ELEMENT: {
576             if (param->AsRestElement()->Argument()->IsIdentifier()) {
577                 return {param->AsRestElement()->Argument()->AsIdentifier()->Name(), false};
578             }
579             break;
580         }
581         case ir::AstNodeType::TS_PARAMETER_PROPERTY: {
582             return ParamName(allocator, param->AsTSParameterProperty()->Parameter(), index);
583         }
584         default:
585             break;
586     }
587 
588     return {Helpers::ToStringView(allocator, index), true};
589 }
590 
IsChild(const ir::AstNode * parent,const ir::AstNode * child)591 bool Helpers::IsChild(const ir::AstNode *parent, const ir::AstNode *child)
592 {
593     while (child) {
594         if (child == parent) {
595             return true;
596         }
597 
598         child = child->Parent();
599     }
600 
601     return false;
602 }
603 
IsChildScope(const binder::Scope * parent,const binder::Scope * child)604 bool Helpers::IsChildScope(const binder::Scope *parent, const binder::Scope *child)
605 {
606     while (child) {
607         if (child == parent) {
608             return true;
609         }
610 
611         child = child->Parent();
612     }
613 
614     return false;
615 }
616 
IsObjectPropertyValue(const ArenaVector<ir::Expression * > & properties,const ir::AstNode * ident)617 bool Helpers::IsObjectPropertyValue(const ArenaVector<ir::Expression *> &properties, const ir::AstNode *ident)
618 {
619     for (const auto *prop : properties) {
620         ASSERT(prop->IsProperty() || prop->IsSpreadElement());
621         if (prop->IsProperty() && (prop->AsProperty()->Value() == ident)) {
622             return true;
623         }
624 
625         if (prop->IsSpreadElement() && (prop->AsSpreadElement()->Argument() == ident)) {
626             return true;
627         }
628     }
629 
630     return false;
631 }
632 
GetSignedNumberLiteral(const ir::Expression * expr)633 SignedNumberLiteral Helpers::GetSignedNumberLiteral(const ir::Expression *expr)
634 {
635     if (!expr->IsUnaryExpression()) {
636         return SignedNumberLiteral::UNRECOGNIZED;
637     }
638 
639     auto unaryExpression = expr->AsUnaryExpression();
640     if (!unaryExpression->Argument()->IsNumberLiteral()) {
641         return SignedNumberLiteral::UNRECOGNIZED;
642     }
643 
644     // TODO(hxw): Here we return different value for positive and nagative number literal in UnaryExpression.
645     // Because when we access a computed property by MemberExpression, the compiler should emit different instruction.
646     // Now es2abc always emits the instruction `loadObjByValue` whether the computed property is literal or not.
647     // It can be optimized. For positive integer literal, the instruction should be `loadObjByIndex`.
648     // While for negative number literal, the instruction should be `loadObjByName`.
649     // So I add this util api and return different value for future use.
650     if (unaryExpression->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS) {
651         return SignedNumberLiteral::POSITIVE;
652     } else if (unaryExpression->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) {
653         return SignedNumberLiteral::NEGATIVE;
654     }
655 
656     return SignedNumberLiteral::UNRECOGNIZED;
657 }
658 
SetConstantLocalExportSlots(const std::string & record,const std::unordered_set<uint32_t> & slots)659 void Helpers::SetConstantLocalExportSlots(const std::string &record, const std::unordered_set<uint32_t> &slots)
660 {
661     bool ignored;
662     auto &result = panda::bytecodeopt::BytecodeAnalysisResults::GetOrCreateBytecodeAnalysisResult(record, ignored);
663     result.SetConstantLocalExportSlots(slots);
664 }
665 
GetTempOutputName(const std::string & inputFile)666 static std::string GetTempOutputName(const std::string &inputFile)
667 {
668     std::string pid;
669 #ifdef PANDA_TARGET_WINDOWS
670     pid = std::to_string(GetCurrentProcessId());
671 #else
672     pid = std::to_string(getpid());
673 #endif
674     const std::string outputSuffix = ".unopt.abc";
675     return panda::os::file::File::GetExtendedFilePath(inputFile + pid + outputSuffix);
676 }
677 
AnalysisProgram(panda::pandasm::Program * prog,const std::string & inputFile)678 void Helpers::AnalysisProgram(panda::pandasm::Program *prog, const std::string &inputFile)
679 {
680 #ifdef PANDA_WITH_BYTECODE_OPTIMIZER
681     std::map<std::string, size_t> stat;
682     std::map<std::string, size_t> *statp = &stat;
683     auto tempOutput = GetTempOutputName(inputFile);
684     bool exists = false;
685     auto mapsp = &panda::bytecodeopt::BytecodeAnalysisResults::GetOrCreateBytecodeMaps(tempOutput, exists);
686     ASSERT(!exists);
687 
688     const uint32_t COMPONENT_MASK = panda::Logger::Component::ASSEMBLER |
689                                     panda::Logger::Component::BYTECODE_OPTIMIZER |
690                                     panda::Logger::Component::COMPILER;
691     panda::Logger::InitializeStdLogging(panda::Logger::Level::ERROR, COMPONENT_MASK);
692 
693     if (panda::pandasm::AsmEmitter::Emit(tempOutput, *prog, statp, mapsp, true)) {
694         panda::bytecodeopt::AnalysisBytecode(prog, mapsp, tempOutput, true, true);
695     } else {
696         panda::bytecodeopt::BytecodeAnalysisResults::DeleteBytecodeMaps(tempOutput);
697     }
698 #endif
699 }
700 
OptimizeProgram(panda::pandasm::Program * prog,const std::string & inputFile)701 void Helpers::OptimizeProgram(panda::pandasm::Program *prog, const std::string &inputFile)
702 {
703     auto tempOutput = GetTempOutputName(inputFile);
704 
705 #ifdef PANDA_WITH_BYTECODE_OPTIMIZER
706     std::map<std::string, size_t> stat;
707     std::map<std::string, size_t> *statp = &stat;
708     const uint32_t COMPONENT_MASK = panda::Logger::Component::ASSEMBLER |
709                                     panda::Logger::Component::BYTECODE_OPTIMIZER |
710                                     panda::Logger::Component::COMPILER;
711     panda::Logger::InitializeStdLogging(panda::Logger::Level::ERROR, COMPONENT_MASK);
712 
713     bool exists = false;
714     auto mapsp = &panda::bytecodeopt::BytecodeAnalysisResults::GetOrCreateBytecodeMaps(tempOutput, exists);
715     if (!exists) {
716         exists = panda::pandasm::AsmEmitter::Emit(tempOutput, *prog, statp, mapsp, true);
717     }
718     if (exists) {
719         panda::bytecodeopt::OptimizeBytecode(prog, mapsp, tempOutput, true, true);
720         std::remove(tempOutput.c_str());
721     }
722     panda::bytecodeopt::BytecodeAnalysisResults::DeleteBytecodeMaps(tempOutput);
723 
724 #endif
725 }
726 
CheckAopTransformPath(const std::string & libPath)727 bool Helpers::CheckAopTransformPath(const std::string &libPath)
728 {
729     std::string lowerLibPath = libPath;
730     std::transform(libPath.begin(), libPath.end(), lowerLibPath.begin(), ::tolower);
731     bool isValidSuffix = false;
732 #ifdef PANDA_TARGET_WINDOWS
733     isValidSuffix = FileExtensionIs(lowerLibPath, FileSuffix::DLL);
734     std::string supportSuffix = std::string(FileSuffix::DLL);
735 #else
736     isValidSuffix = FileExtensionIs(lowerLibPath, FileSuffix::SO)
737         || FileExtensionIs(lowerLibPath, FileSuffix::DYLIB);
738     std::string supportSuffix = std::string(FileSuffix::SO) + "|" + std::string(FileSuffix::DYLIB);
739 #endif
740     //check file suffix(.so|.dll)
741     if (!isValidSuffix) {
742         std::string msg = "aop transform file suffix support " + supportSuffix + ", error file: " + libPath;
743         std::cout << msg << std::endl;
744         return false;
745     }
746     return true;
747 }
748 
LoadAopTransformLibFunc(const std::string & libPath,const std::string & funcName,os::library_loader::LibraryHandle & handler)749 AopTransformFuncDef Helpers::LoadAopTransformLibFunc(const std::string &libPath,
750     const std::string &funcName, os::library_loader::LibraryHandle &handler)
751 {
752     auto loadRes = os::library_loader::Load(libPath);
753     if (!loadRes.HasValue()) {
754         std::string msg = "os::library_loader::Load error: " + loadRes.Error().ToString();
755         std::cout << msg << std::endl;
756         return nullptr;
757     }
758     handler = std::move(loadRes.Value());
759 
760     auto initRes = os::library_loader::ResolveSymbol(handler, funcName);
761     if (!initRes.HasValue()) {
762         std::string msg = "os::library_loader::ResolveSymbol get func Transform error: " + initRes.Error().ToString();
763         std::cout << msg << std::endl;
764         return nullptr;
765     }
766 
767     return reinterpret_cast<AopTransformFuncDef>(initRes.Value());
768 }
769 
AopTransform(const std::string & inputFile,const std::string & libPath)770 bool Helpers::AopTransform(const std::string &inputFile, const std::string &libPath)
771 {
772     if (!CheckAopTransformPath(libPath)) {
773         return false;
774     }
775 
776     os::library_loader::LibraryHandle handler(nullptr);
777     AopTransformFuncDef transform = LoadAopTransformLibFunc(libPath, "Transform", handler);
778     if (transform == nullptr) {
779         return false;
780     }
781 
782     //invoke Transform, untransformed ABC to transformed ABC, result define: 0:success, other:fail
783     int res = transform(inputFile.c_str());
784     if (res != 0) {
785         std::string msg = "Transform exec fail: " + libPath;
786         std::cout << msg << std::endl;
787         return false;
788     }
789     return true;
790 }
791 
ReadFileToBuffer(const std::string & file,std::stringstream & ss)792 bool Helpers::ReadFileToBuffer(const std::string &file, std::stringstream &ss)
793 {
794     std::ifstream inputStream = Helpers::FileStream<std::ifstream>(
795         panda::os::file::File::GetExtendedFilePath(file), std::ios::binary);
796     if (inputStream.fail()) {
797         std::cerr << "Failed to read file to buffer: " << file << std::endl <<
798                      "Solutions: > Check whether the above file exists." <<
799                      "> Check whether you have the correct access permissions for the file.";
800         return false;
801     }
802     ss << inputStream.rdbuf();
803     return true;
804 }
805 
ScanDirectives(ir::ScriptFunction * func,const DirectiveScanConfig & scanConfig)806 void Helpers::ScanDirectives(ir::ScriptFunction *func, const DirectiveScanConfig &scanConfig)
807 {
808     auto *body = func->Body();
809     if (!body || body->IsExpression()) {
810         return;
811     }
812 
813     auto &statements = body->AsBlockStatement()->Statements();
814     if (statements.empty()) {
815         return;
816     }
817 
818     bool keepScan = true;
819     auto iter = statements.begin();
820     while (keepScan && (iter != statements.end())) {
821         auto *stmt = *iter++;
822         if (!stmt->IsExpressionStatement()) {
823             return;
824         }
825 
826         auto *expr = stmt->AsExpressionStatement()->GetExpression();
827         if (!expr->IsStringLiteral()) {
828             return;
829         }
830 
831         keepScan = SetFuncFlagsForDirectives(expr->AsStringLiteral(), func, scanConfig);
832     }
833 
834     return;
835 }
836 
SetFuncFlagsForDirectives(const ir::StringLiteral * strLit,ir::ScriptFunction * func,const DirectiveScanConfig & scanConfig)837 bool Helpers::SetFuncFlagsForDirectives(const ir::StringLiteral *strLit, ir::ScriptFunction *func,
838                                         const DirectiveScanConfig &scanConfig)
839 {
840     if (strLit->Str().Is(USE_CONCURRENT)) {
841         util::Concurrent::SetConcurrent(func, strLit, scanConfig.lineIndex);
842         return true;
843     }
844 
845     if (strLit->Str().Is(USE_SENDABLE)) {
846         if (func->IsConstructor()) {
847             if (scanConfig.enableSendableClass) {
848                 auto *classDef = const_cast<ir::ClassDefinition*>(GetClassDefiniton(func));
849                 classDef->SetSendable();
850             }
851         } else if (scanConfig.enableSendableFunc) {
852             func->AddFlag(ir::ScriptFunctionFlags::SENDABLE);
853         }
854         return true;
855     }
856 
857     if (scanConfig.enableEtsImplements && func->IsConstructor() && strLit->Str().StartsWith(IMPLEMENTS_STATIC)) {
858         auto *classDef = const_cast<ir::ClassDefinition*>(GetClassDefiniton(func));
859         classDef->SetImplementFromEts();
860         classDef->SetEtsImplementsMessage(strLit->Str().Substr(IMPLEMENTS_STATIC.size(), strLit->Str().Length()));
861     }
862 
863     return false;
864 }
865 
GetHashString(const std::string & str)866 std::string Helpers::GetHashString(const std::string &str)
867 {
868     uint64_t result = FNV_OFFSET;
869 
870     const uint8_t *input = reinterpret_cast<const uint8_t *>(str.c_str());
871     // FNV-1a 64-bit Algorithm
872     for (size_t i = 0; i < str.size(); i++) {
873         result ^= input[i];
874         result *= FNV_PRIME;
875     }
876 
877     return std::to_string(result);
878 }
879 
880 #ifdef PANDA_TARGET_WINDOWS
Utf8ToUtf16(const std::string & utf8)881 std::wstring Helpers::Utf8ToUtf16(const std::string &utf8)
882 {
883     std::wstring utf16;
884     if (utf8.empty()) {
885         return utf16;
886     }
887 
888     if (utf8.length() > static_cast<size_t>(std::numeric_limits<int>::max())) {
889         std::cerr << "Length of filename: " << utf8 << " is too long" << std::endl;
890         return utf16;
891     }
892 
893     const int utf8Length = static_cast<int>(utf8.length());
894     constexpr DWORD kFlags = MB_ERR_INVALID_CHARS;
895     const int utf16Length = MultiByteToWideChar(CP_UTF8, kFlags, utf8.data(), utf8Length, nullptr, 0);
896     if (utf16Length == 0) {
897         std::cerr << "The filename: " << utf8 << " is not a valid utf8 encoding string" << std::endl;
898         return utf16;
899     }
900 
901     utf16.resize(utf16Length);
902     MultiByteToWideChar(CP_UTF8, kFlags, utf8.data(), utf8Length, &utf16[0], utf16Length);
903     return utf16;
904 }
905 #endif
906 
ThrowError(ErrorType type,const parser::Program * program,const lexer::SourcePosition & pos,const std::string_view & msg)907 void Helpers::ThrowError(ErrorType type, const parser::Program *program, const lexer::SourcePosition &pos,
908     const std::string_view &msg)
909 {
910     lexer::LineIndex index(program->SourceCode());
911     lexer::SourceLocation loc = index.GetLocation(pos);
912 
913     throw Error {type, msg, loc.line, loc.col};
914 }
915 
IsUseShared(const ir::Statement * statement)916 bool Helpers::IsUseShared(const ir::Statement *statement)
917 {
918     if (!statement->IsExpressionStatement()) {
919         return false;
920     }
921 
922     if (!statement->AsExpressionStatement()->GetExpression()->IsStringLiteral()) {
923         return false;
924     }
925 
926     return statement->AsExpressionStatement()->GetExpression()->AsStringLiteral()->Str().Is(USE_SHARED);
927 }
928 
GetContainingSendableClass(const ir::AstNode * node)929 const ir::ClassDefinition *Helpers::GetContainingSendableClass(const ir::AstNode *node)
930 {
931     while (node != nullptr) {
932         if (node->IsClassDefinition() && node->AsClassDefinition()->IsSendable()) {
933             return node->AsClassDefinition();
934         }
935 
936         node = node->Parent();
937     }
938 
939     return nullptr;
940 }
941 
IsSpecialScopeName(const util::StringView & name)942 bool Helpers::IsSpecialScopeName(const util::StringView &name)
943 {
944     return name.Find(Helpers::DOT.data()) != std::string::npos ||
945            name.Find(Helpers::BACKSLASH.data()) != std::string::npos;
946 }
947 
BelongingToRecords(const std::string & name,const std::unordered_set<std::string> & retainRecordSet,const std::string & delimiter)948 bool Helpers::BelongingToRecords(const std::string &name, const std::unordered_set<std::string> &retainRecordSet,
949                                  const std::string &delimiter)
950 {
951     size_t pos = name.rfind(delimiter);
952     if (pos == std::string::npos) {
953         std::cerr << "The input name: " << name << " is illegal, it should contain the delimiter character '" <<
954                      delimiter << "'" << std::endl;
955         return false;
956     }
957 
958     auto recordName = name.substr(0, pos);
959     return retainRecordSet.find(recordName) != retainRecordSet.end();
960 }
961 
IsInnerAnnotationRecordName(const std::string_view & name)962 bool Helpers::IsInnerAnnotationRecordName(const std::string_view &name)
963 {
964     return name == panda::abc2program::CONCURRENT_MODULE_REQUEST_RECORD_NAME ||
965            name == panda::abc2program::SLOT_NUMBER_RECORD_NAME ||
966            name == panda::abc2program::EXPECTED_PROPERTY_COUNT_RECORD_NAME;
967 }
968 
RemoveRecordSuffixAnnotationName(std::string & name)969 size_t Helpers::RemoveRecordSuffixAnnotationName(std::string &name)
970 {
971     auto pos = name.rfind(abc2program::RECORD_ANNOTATION_SEPARATOR);
972     if (pos != std::string::npos) {
973         name = name.substr(0, pos);
974     }
975     return pos;
976 }
977 
RemoveProgramsRedundantData(std::map<std::string,panda::es2panda::util::ProgramCache * > & progsInfo,const std::map<std::string,std::unordered_set<std::string>> & resolvedDepsRelation)978 void Helpers::RemoveProgramsRedundantData(std::map<std::string, panda::es2panda::util::ProgramCache*> &progsInfo,
979     const std::map<std::string, std::unordered_set<std::string>> &resolvedDepsRelation)
980 {
981     auto progInfoIter = progsInfo.begin();
982     while (progInfoIter != progsInfo.end()) {
983         std::string name = progInfoIter->first;
984         auto &program = progInfoIter->second->program;
985         auto &recordMetaData = program.record_table.begin()->second.metadata;
986         /**
987          * The record name of a user-defined annotation is the annotation name concatenated with the record name of the
988          * file where it is declared. In order to preserve the annotated record, it is necessary to remove the
989          * concatenated annotation name from the record name before perform dependency matching.
990          */
991         if (!util::RecordNotGeneratedFromBytecode(name) && (recordMetaData->GetAccessFlags() & ACC_ANNOTATION) != 0 &&
992             !IsInnerAnnotationRecordName(program.record_table.begin()->first)) {
993             RemoveRecordSuffixAnnotationName(name);
994         }
995 
996         /**
997          * Imported user-defined annotations will generate external records in the abc files. Its naming convention is
998          * consistent with the definition of annotations. But importing annotations supports qualified names,
999          * such as 'ns.Anno'. so it may need to remove suffixes multiple times to ensure that record name is found.
1000          */
1001         if (!util::RecordNotGeneratedFromBytecode(name) && recordMetaData->IsForeign()) {
1002             auto versionStart = name.rfind(util::NORMALIZED_OHMURL_SEPARATOR);
1003             size_t pos = name.length();
1004             while (pos != std::string::npos && pos > versionStart &&
1005                    resolvedDepsRelation.find(name) == resolvedDepsRelation.end()) {
1006                 pos = RemoveRecordSuffixAnnotationName(name);
1007             }
1008         }
1009 
1010         // remove redundant sourcefiles and bytecodefile data which are not dependant in compilation
1011         if (resolvedDepsRelation.find(name) == resolvedDepsRelation.end()) {
1012             progInfoIter = progsInfo.erase(progInfoIter);
1013             continue;
1014         }
1015 
1016         progInfoIter++;
1017     }
1018 }
1019 
IsDefaultApiVersion(int apiVersion,std::string subApiVersion)1020 bool Helpers::IsDefaultApiVersion(int apiVersion, std::string subApiVersion)
1021 {
1022     return apiVersion < DEFAULT_TARGET_API_VERSION || ((apiVersion == DEFAULT_TARGET_API_VERSION) &&
1023         (subApiVersion == SUB_API_VERSION_1 || subApiVersion == SUB_API_VERSION_2));
1024 }
1025 
IsSupportLazyImportVersion(int apiVersion,std::string subApiVersion)1026 bool Helpers::IsSupportLazyImportVersion(int apiVersion, std::string subApiVersion)
1027 {
1028     return !(apiVersion < LAZY_IMPORT_MIN_SUPPORTED_API_VERSION ||
1029            ((apiVersion == LAZY_IMPORT_MIN_SUPPORTED_API_VERSION) &&
1030            (subApiVersion == SUB_API_VERSION_1 || subApiVersion == SUB_API_VERSION_2)));
1031 }
1032 
IsEnableExpectedPropertyCountApiVersion(int apiVersion)1033 bool Helpers::IsEnableExpectedPropertyCountApiVersion(int apiVersion)
1034 {
1035     return !(apiVersion < ENABLE_EXPECTED_PROPERTY_COUNT_MIN_SUPPORTED_API_VERSION);
1036 }
1037 
IsSupportAnnotationVersion(int apiVersion)1038 bool Helpers::IsSupportAnnotationVersion(int apiVersion)
1039 {
1040     return !(apiVersion < ANNOTATION_SUPPORTED_API_VERSION);
1041 }
1042 
1043 }  // namespace panda::es2panda::util
1044