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