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