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