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