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