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