• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.Apache.Org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "helpers.h"
17 
18 #include <es2panda.h>
19 #include <ir/base/classDefinition.h>
20 #include <ir/base/classProperty.h>
21 #include <ir/base/methodDefinition.h>
22 #include <ir/base/property.h>
23 #include <ir/base/scriptFunction.h>
24 #include <ir/base/spreadElement.h>
25 #include <ir/expressions/arrayExpression.h>
26 #include <ir/expressions/assignmentExpression.h>
27 #include <ir/expressions/functionExpression.h>
28 #include <ir/expressions/identifier.h>
29 #include <ir/expressions/literals/numberLiteral.h>
30 #include <ir/expressions/literals/stringLiteral.h>
31 #include <ir/expressions/objectExpression.h>
32 #include <ir/statements/variableDeclaration.h>
33 #include <ir/statements/variableDeclarator.h>
34 #include <ir/ts/tsParameterProperty.h>
35 #include <parser/module/sourceTextModuleRecord.h>
36 
37 #ifdef ENABLE_BYTECODE_OPT
38 #include <bytecode_optimizer/bytecodeopt_options.h>
39 #include <bytecode_optimizer/optimize_bytecode.h>
40 #else
41 #include <assembly-type.h>
42 #include <assembly-program.h>
43 #include <assembly-emitter.h>
44 #endif
45 
46 #ifdef PANDA_TARGET_WINDOWS
47 #include <windows.h>
48 #undef ERROR
49 #else
50 #include <unistd.h>
51 #endif
52 #include <fstream>
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(const ir::Expression * lit)68 util::StringView Helpers::LiteralToPropName(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 lit->AsNumberLiteral()->Str();
79         }
80         case ir::AstNodeType::NULL_LITERAL: {
81             return "null";
82         }
83         default: {
84             UNREACHABLE();
85         }
86     }
87 }
88 
IsIndex(double number)89 bool Helpers::IsIndex(double number)
90 {
91     if (number >= 0 && number < static_cast<double>(INVALID_INDEX)) {
92         auto intNum = static_cast<uint32_t>(number);
93 
94         if (static_cast<double>(intNum) == number) {
95             return true;
96         }
97     }
98 
99     return false;
100 }
101 
IsDigit(char c)102 static bool IsDigit(char c)
103 {
104     return (c >= '0' && c <= '9');
105 }
106 
GetIndex(const util::StringView & str)107 int64_t Helpers::GetIndex(const util::StringView &str)
108 {
109     const auto &s = str.Utf8();
110 
111     if (s.empty() || (*s.begin() == '0' && s.length() > 1)) {
112         return INVALID_INDEX;
113     }
114 
115     int64_t value = 0;
116     for (const auto c : s) {
117         if (!IsDigit(c)) {
118             return INVALID_INDEX;
119         }
120 
121         constexpr auto MULTIPLIER = 10;
122         value *= MULTIPLIER;
123         value += (c - '0');
124 
125         if (value >= INVALID_INDEX) {
126             return INVALID_INDEX;
127         }
128     }
129 
130     return value;
131 }
132 
ToString(double number)133 std::string Helpers::ToString(double number)
134 {
135     std::string str;
136 
137     if (Helpers::IsInteger<int32_t>(number)) {
138         str = std::to_string(static_cast<int32_t>(number));
139     } else {
140         str = std::to_string(number);
141     }
142 
143     return str;
144 }
145 
ToStringView(ArenaAllocator * allocator,double number)146 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, double number)
147 {
148     util::UString str(ToString(number), allocator);
149     return str.View();
150 }
151 
ToStringView(ArenaAllocator * allocator,uint32_t number)152 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, uint32_t number)
153 {
154     ASSERT(number <= static_cast<uint32_t>(std::numeric_limits<int32_t>::max()));
155     return ToStringView(allocator, static_cast<int32_t>(number));
156 }
157 
ToStringView(ArenaAllocator * allocator,int32_t number)158 util::StringView Helpers::ToStringView(ArenaAllocator *allocator, int32_t number)
159 {
160     util::UString str(ToString(number), allocator);
161     return str.View();
162 }
163 
GetContainingConstructor(const ir::AstNode * node)164 const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::AstNode *node)
165 {
166     const ir::ScriptFunction *iter = GetContainingFunction(node);
167 
168     while (iter != nullptr) {
169         if (iter->IsConstructor()) {
170             return iter;
171         }
172 
173         if (!iter->IsArrow()) {
174             return nullptr;
175         }
176 
177         iter = GetContainingFunction(iter);
178     }
179 
180     return iter;
181 }
182 
GetContainingConstructor(const ir::ClassProperty * node)183 const ir::ScriptFunction *Helpers::GetContainingConstructor(const ir::ClassProperty *node)
184 {
185     for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
186         if (parent->IsClassDefinition()) {
187             return parent->AsClassDefinition()->Ctor()->Function();
188         }
189     }
190 
191     return nullptr;
192 }
193 
GetContainingFunction(const ir::AstNode * node)194 const ir::ScriptFunction *Helpers::GetContainingFunction(const ir::AstNode *node)
195 {
196     for (const auto *parent = node->Parent(); parent != nullptr; parent = parent->Parent()) {
197         if (parent->IsScriptFunction()) {
198             return parent->AsScriptFunction();
199         }
200     }
201 
202     return nullptr;
203 }
204 
GetClassDefiniton(const ir::ScriptFunction * node)205 const ir::ClassDefinition *Helpers::GetClassDefiniton(const ir::ScriptFunction *node)
206 {
207     ASSERT(node->IsConstructor());
208     ASSERT(node->Parent()->IsFunctionExpression());
209     ASSERT(node->Parent()->Parent()->IsMethodDefinition());
210     ASSERT(node->Parent()->Parent()->Parent()->IsClassDefinition());
211 
212     return node->Parent()->Parent()->Parent()->AsClassDefinition();
213 }
214 
IsSpecialPropertyKey(const ir::Expression * expr)215 bool Helpers::IsSpecialPropertyKey(const ir::Expression *expr)
216 {
217     if (!expr->IsStringLiteral()) {
218         return false;
219     }
220 
221     auto *lit = expr->AsStringLiteral();
222     return lit->Str().Is("prototype") || lit->Str().Is("constructor");
223 }
224 
IsConstantPropertyKey(const ir::Expression * expr,bool isComputed)225 bool Helpers::IsConstantPropertyKey(const ir::Expression *expr, bool isComputed)
226 {
227     switch (expr->Type()) {
228         case ir::AstNodeType::IDENTIFIER: {
229             return !isComputed;
230         }
231         case ir::AstNodeType::NUMBER_LITERAL:
232         case ir::AstNodeType::STRING_LITERAL:
233         case ir::AstNodeType::BOOLEAN_LITERAL:
234         case ir::AstNodeType::NULL_LITERAL: {
235             return true;
236         }
237         default:
238             break;
239     }
240 
241     return false;
242 }
243 
IsConstantExpr(const ir::Expression * expr)244 bool Helpers::IsConstantExpr(const ir::Expression *expr)
245 {
246     switch (expr->Type()) {
247         case ir::AstNodeType::NUMBER_LITERAL:
248         case ir::AstNodeType::STRING_LITERAL:
249         case ir::AstNodeType::BOOLEAN_LITERAL:
250         case ir::AstNodeType::NULL_LITERAL: {
251             return true;
252         }
253         default:
254             break;
255     }
256 
257     return false;
258 }
259 
IsBindingPattern(const ir::AstNode * node)260 bool Helpers::IsBindingPattern(const ir::AstNode *node)
261 {
262     return node->IsArrayPattern() || node->IsObjectPattern();
263 }
264 
IsPattern(const ir::AstNode * node)265 bool Helpers::IsPattern(const ir::AstNode *node)
266 {
267     return node->IsArrayPattern() || node->IsObjectPattern() || node->IsAssignmentPattern();
268 }
269 
CollectBindingName(const ir::AstNode * node,std::vector<const ir::Identifier * > * bindings)270 static void CollectBindingName(const ir::AstNode *node, std::vector<const ir::Identifier *> *bindings)
271 {
272     switch (node->Type()) {
273         case ir::AstNodeType::IDENTIFIER: {
274             if (!Helpers::IsGlobalIdentifier(node->AsIdentifier()->Name())) {
275                 bindings->push_back(node->AsIdentifier());
276             }
277 
278             break;
279         }
280         case ir::AstNodeType::OBJECT_PATTERN: {
281             for (const auto *prop : node->AsObjectPattern()->Properties()) {
282                 CollectBindingName(prop, bindings);
283             }
284             break;
285         }
286         case ir::AstNodeType::ARRAY_PATTERN: {
287             for (const auto *element : node->AsArrayPattern()->Elements()) {
288                 CollectBindingName(element, bindings);
289             }
290             break;
291         }
292         case ir::AstNodeType::ASSIGNMENT_PATTERN: {
293             CollectBindingName(node->AsAssignmentPattern()->Left(), bindings);
294             break;
295         }
296         case ir::AstNodeType::PROPERTY: {
297             CollectBindingName(node->AsProperty()->Value(), bindings);
298             break;
299         }
300         case ir::AstNodeType::REST_ELEMENT: {
301             CollectBindingName(node->AsRestElement()->Argument(), bindings);
302             break;
303         }
304         default:
305             break;
306     }
307 }
308 
CollectBindingNames(const ir::AstNode * node)309 std::vector<const ir::Identifier *> Helpers::CollectBindingNames(const ir::AstNode *node)
310 {
311     std::vector<const ir::Identifier *> bindings;
312     CollectBindingName(node, &bindings);
313     return bindings;
314 }
315 
FunctionName(const ir::ScriptFunction * func)316 util::StringView Helpers::FunctionName(const ir::ScriptFunction *func)
317 {
318     if (func->Id()) {
319         return func->Id()->Name();
320     }
321 
322     if (func->Parent()->IsFunctionDeclaration()) {
323         return parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME;
324     }
325 
326     const ir::AstNode *parent = func->Parent()->Parent();
327 
328     if (func->IsConstructor()) {
329         parent = parent->Parent();
330         if (parent->AsClassDefinition()->Ident()) {
331             return parent->AsClassDefinition()->Ident()->Name();
332         }
333 
334         parent = parent->Parent()->Parent();
335     }
336 
337     switch (parent->Type()) {
338         case ir::AstNodeType::VARIABLE_DECLARATOR: {
339             const ir::VariableDeclarator *varDecl = parent->AsVariableDeclarator();
340 
341             if (varDecl->Id()->IsIdentifier()) {
342                 return varDecl->Id()->AsIdentifier()->Name();
343             }
344 
345             break;
346         }
347         case ir::AstNodeType::METHOD_DEFINITION: {
348             const ir::MethodDefinition *methodDef = parent->AsMethodDefinition();
349 
350             if (methodDef->Key()->IsIdentifier()) {
351                 return methodDef->Key()->AsIdentifier()->Name();
352             }
353 
354             break;
355         }
356         case ir::AstNodeType::ASSIGNMENT_EXPRESSION: {
357             const ir::AssignmentExpression *assignment = parent->AsAssignmentExpression();
358 
359             if (assignment->Left()->IsIdentifier()) {
360                 return assignment->Left()->AsIdentifier()->Name();
361             }
362 
363             break;
364         }
365         case ir::AstNodeType::ASSIGNMENT_PATTERN: {
366             const ir::AssignmentExpression *assignment = parent->AsAssignmentPattern();
367 
368             if (assignment->Left()->IsIdentifier()) {
369                 return assignment->Left()->AsIdentifier()->Name();
370             }
371 
372             break;
373         }
374         case ir::AstNodeType::PROPERTY: {
375             const ir::Property *prop = parent->AsProperty();
376 
377             if (prop->Kind() != ir::PropertyKind::PROTO &&
378                 Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed())) {
379                 return Helpers::LiteralToPropName(prop->Key());
380             }
381 
382             break;
383         }
384         case ir::AstNodeType::EXPORT_DEFAULT_DECLARATION: {
385             return parser::SourceTextModuleRecord::DEFAULT_EXTERNAL_NAME;
386         }
387         default:
388             break;
389     }
390 
391     return util::StringView();
392 }
393 
ParamName(ArenaAllocator * allocator,const ir::AstNode * param,uint32_t index)394 std::tuple<util::StringView, bool> Helpers::ParamName(ArenaAllocator *allocator, const ir::AstNode *param,
395                                                       uint32_t index)
396 {
397     switch (param->Type()) {
398         case ir::AstNodeType::IDENTIFIER: {
399             return {param->AsIdentifier()->Name(), false};
400         }
401         case ir::AstNodeType::ASSIGNMENT_PATTERN: {
402             const auto *lhs = param->AsAssignmentPattern()->Left();
403             if (lhs->IsIdentifier()) {
404                 return {param->AsAssignmentPattern()->Left()->AsIdentifier()->Name(), false};
405             }
406             break;
407         }
408         case ir::AstNodeType::REST_ELEMENT: {
409             if (param->AsRestElement()->Argument()->IsIdentifier()) {
410                 return {param->AsRestElement()->Argument()->AsIdentifier()->Name(), false};
411             }
412             break;
413         }
414         case ir::AstNodeType::TS_PARAMETER_PROPERTY: {
415             return ParamName(allocator, param->AsTSParameterProperty()->Parameter(), index);
416         }
417         default:
418             break;
419     }
420 
421     return {Helpers::ToStringView(allocator, index), true};
422 }
423 
IsChild(const ir::AstNode * parent,const ir::AstNode * child)424 bool Helpers::IsChild(const ir::AstNode *parent, const ir::AstNode *child)
425 {
426     while (child) {
427         if (child == parent) {
428             return true;
429         }
430 
431         child = child->Parent();
432     }
433 
434     return false;
435 }
436 
IsObjectPropertyValue(const ir::ObjectExpression * object,const ir::AstNode * ident)437 bool Helpers::IsObjectPropertyValue(const ir::ObjectExpression *object, const ir::AstNode *ident)
438 {
439     for (const auto *prop : object->Properties()) {
440         if (prop->AsProperty()->Value() == ident) {
441             return true;
442         }
443     }
444 
445     return false;
446 }
447 
OptimizeProgram(panda::pandasm::Program * prog,const std::string & inputFile)448 void Helpers::OptimizeProgram(panda::pandasm::Program *prog,  const std::string &inputFile)
449 {
450     std::map<std::string, size_t> stat;
451     std::map<std::string, size_t> *statp = &stat;
452     panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps{};
453     panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp = &maps;
454 
455 #ifdef PANDA_WITH_BYTECODE_OPTIMIZER
456     const uint32_t COMPONENT_MASK = panda::Logger::Component::ASSEMBLER |
457                                     panda::Logger::Component::BYTECODE_OPTIMIZER |
458                                     panda::Logger::Component::COMPILER;
459     panda::Logger::InitializeStdLogging(panda::Logger::Level::ERROR, COMPONENT_MASK);
460 
461     std::string pid;
462 #ifdef PANDA_TARGET_WINDOWS
463     pid = std::to_string(GetCurrentProcessId());
464 #else
465     pid = std::to_string(getpid());
466 #endif
467     const std::string outputSuffix = ".unopt.abc";
468     std::string tempOutput = panda::os::file::File::GetExtendedFilePath(inputFile + pid + outputSuffix);
469     if (panda::pandasm::AsmEmitter::Emit(tempOutput, *prog, statp, mapsp, true)) {
470         panda::bytecodeopt::OptimizeBytecode(prog, mapsp, tempOutput, true, true);
471     }
472 
473     std::remove(tempOutput.c_str());
474 #endif
475 }
476 
ReadFileToBuffer(const std::string & file,std::stringstream & ss)477 bool Helpers::ReadFileToBuffer(const std::string &file, std::stringstream &ss)
478 {
479     std::ifstream inputStream(panda::os::file::File::GetExtendedFilePath(file), std::ios::binary);
480     if (inputStream.fail()) {
481         std::cerr << "Failed to read file to buffer: " << file << std::endl;
482         return false;
483     }
484     ss << inputStream.rdbuf();
485     return true;
486 }
487 
488 }  // namespace panda::es2panda::util
489