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 "JSCompiler.h"
17
18 #include "varbinder/varbinder.h"
19 #include "compiler/base/catchTable.h"
20 #include "compiler/base/condition.h"
21 #include "compiler/base/lreference.h"
22 #include "compiler/core/pandagen.h"
23 #include "compiler/core/switchBuilder.h"
24 #include "compiler/function/functionBuilder.h"
25 #include "util/bitset.h"
26 #include "util/helpers.h"
27 namespace ark::es2panda::compiler {
28
GetPandaGen() const29 PandaGen *JSCompiler::GetPandaGen() const
30 {
31 return static_cast<PandaGen *>(GetCodeGen());
32 }
33
34 // from base folder
Compile(const ir::CatchClause * st) const35 void JSCompiler::Compile(const ir::CatchClause *st) const
36 {
37 PandaGen *pg = GetPandaGen();
38 compiler::LocalRegScope lrs(pg, st->Scope()->ParamScope());
39
40 if (st->Param() != nullptr) {
41 auto lref = compiler::JSLReference::Create(pg, st->Param(), true);
42 lref.SetValue();
43 }
44
45 ES2PANDA_ASSERT(st->Scope() == st->Body()->Scope());
46 st->Body()->Compile(pg);
47 }
48
CompileHeritageClause(compiler::PandaGen * pg,const ir::ClassDefinition * node)49 static compiler::VReg CompileHeritageClause(compiler::PandaGen *pg, const ir::ClassDefinition *node)
50 {
51 compiler::VReg baseReg = pg->AllocReg();
52
53 if (node->Super() != nullptr) {
54 node->Super()->Compile(pg);
55 } else {
56 pg->LoadConst(node, compiler::Constant::JS_HOLE);
57 }
58
59 pg->StoreAccumulator(node, baseReg);
60 return baseReg;
61 }
62
CreatePrivateElement(const ir::ClassElement * prop,const ir::MethodDefinition * propMethod,compiler::LiteralBuffer & privateBuf,util::StringView name)63 static void CreatePrivateElement(const ir::ClassElement *prop, const ir::MethodDefinition *propMethod,
64 compiler::LiteralBuffer &privateBuf, util::StringView name)
65 {
66 privateBuf.emplace_back(static_cast<uint32_t>(prop->ToPrivateFieldKind(propMethod->IsStatic())));
67 privateBuf.emplace_back(name);
68
69 const ir::ScriptFunction *func = propMethod->Value()->AsFunctionExpression()->Function();
70 compiler::LiteralTag tag = compiler::LiteralTag::METHOD;
71 bool isAsyncFunc = func->IsAsyncFunc();
72 if (isAsyncFunc && func->IsGenerator()) {
73 tag = compiler::LiteralTag::ASYNC_GENERATOR_METHOD;
74 } else if (isAsyncFunc && !func->IsGenerator()) {
75 tag = compiler::LiteralTag::ASYNC_METHOD;
76 } else if (!isAsyncFunc && func->IsGenerator()) {
77 tag = compiler::LiteralTag::GENERATOR_METHOD;
78 }
79
80 privateBuf.emplace_back(tag, func->Scope()->InternalName());
81 }
82
PropertyMethodKind(const ir::MethodDefinition * propMethod,util::BitSet & compiled,size_t i)83 compiler::Literal PropertyMethodKind(const ir::MethodDefinition *propMethod, util::BitSet &compiled, size_t i)
84 {
85 compiler::Literal value {};
86 switch (propMethod->Kind()) {
87 case ir::MethodDefinitionKind::METHOD: {
88 const ir::FunctionExpression *func = propMethod->Value()->AsFunctionExpression();
89 const util::StringView &internalName = func->Function()->Scope()->InternalName();
90
91 value = compiler::Literal(compiler::LiteralTag::METHOD, internalName);
92 compiled.Set(i);
93 break;
94 }
95 case ir::MethodDefinitionKind::GET:
96 case ir::MethodDefinitionKind::SET: {
97 value = compiler::Literal::NullLiteral();
98 break;
99 }
100 default: {
101 ES2PANDA_UNREACHABLE();
102 }
103 }
104 return value;
105 }
106
CreateClassStaticPropertiesBuf(compiler::LiteralBuffer & buf,compiler::LiteralBuffer & privateBuf,compiler::LiteralBuffer & staticBuf,compiler::PandaGen * pg)107 static std::tuple<int32_t, compiler::LiteralBuffer> CreateClassStaticPropertiesBuf(compiler::LiteralBuffer &buf,
108 compiler::LiteralBuffer &privateBuf,
109 compiler::LiteralBuffer &staticBuf,
110 compiler::PandaGen *pg)
111 {
112 uint32_t litPairs = buf.size() / 2;
113
114 /* Static items are stored at the end of the buffer */
115 buf.insert(buf.end(), staticBuf.begin(), staticBuf.end());
116
117 /* The last literal item represents the offset of the first static property. The regular property literal count
118 * is divided by 2 as key/value pairs count as one. */
119 buf.emplace_back(litPairs);
120
121 return {pg->AddLiteralBuffer(std::move(buf)), privateBuf};
122 }
123
124 // NOLINTNEXTLINE(google-runtime-references)
CreateClassStaticPropertiesHelper(compiler::LiteralBuffer & literalBuf,std::unordered_map<util::StringView,size_t> & nameMap,util::StringView name,bool seenComputed)125 static std::tuple<size_t, compiler::LiteralBuffer, bool> CreateClassStaticPropertiesHelper(
126 compiler::LiteralBuffer &literalBuf, std::unordered_map<util::StringView, size_t> &nameMap, util::StringView name,
127 bool seenComputed)
128 {
129 size_t bufferPos = literalBuf.size();
130 auto res = nameMap.insert({name, bufferPos});
131 if (res.second) {
132 if (seenComputed) {
133 return {bufferPos, literalBuf, true};
134 }
135
136 literalBuf.emplace_back(name);
137 literalBuf.emplace_back();
138 } else {
139 bufferPos = res.first->second;
140 }
141 return {bufferPos, literalBuf, false};
142 }
143
144 // NOLINTNEXTLINE(google-runtime-references)
CreateClassStaticProperties(compiler::PandaGen * pg,util::BitSet & compiled,const ArenaVector<ir::AstNode * > & properties)145 static std::tuple<int32_t, compiler::LiteralBuffer> CreateClassStaticProperties(
146 compiler::PandaGen *pg, util::BitSet &compiled, const ArenaVector<ir::AstNode *> &properties)
147 {
148 compiler::LiteralBuffer buf {};
149 compiler::LiteralBuffer privateBuf {};
150 compiler::LiteralBuffer staticBuf {};
151 bool seenComputed = false;
152 std::unordered_map<util::StringView, size_t> propNameMap;
153 std::unordered_map<util::StringView, size_t> staticPropNameMap;
154
155 for (size_t i = 0; i < properties.size(); i++) {
156 const ir::ClassElement *prop = properties[i]->AsClassElement();
157
158 if (prop->IsClassStaticBlock()) {
159 continue;
160 }
161
162 if (prop->IsClassProperty() && prop->IsPrivateElement()) {
163 bool isStatic = prop->IsStatic();
164 privateBuf.emplace_back(static_cast<uint32_t>(prop->ToPrivateFieldKind(isStatic)));
165 privateBuf.emplace_back(prop->Id()->Name());
166 continue;
167 }
168 if (prop->IsClassProperty() && !prop->IsPrivateElement()) {
169 continue;
170 }
171
172 ES2PANDA_ASSERT(prop->IsMethodDefinition());
173 const ir::MethodDefinition *propMethod = prop->AsMethodDefinition();
174
175 if (!util::Helpers::IsConstantPropertyKey(propMethod->Key(), propMethod->IsComputed()) ||
176 (propMethod->IsComputed() && util::Helpers::IsSpecialPropertyKey(propMethod->Key()))) {
177 seenComputed = true;
178 continue;
179 }
180
181 util::StringView name = util::Helpers::LiteralToPropName(prop->Key());
182 compiler::LiteralBuffer &literalBuf = prop->IsStatic() ? staticBuf : buf;
183 auto &nameMap = prop->IsStatic() ? staticPropNameMap : propNameMap;
184
185 if (prop->IsPrivateElement()) {
186 CreatePrivateElement(prop, propMethod, privateBuf, name);
187 compiled.Set(i);
188 continue;
189 }
190
191 size_t bufferPos = literalBuf.size();
192 bool stop = false;
193 std::tie(bufferPos, literalBuf, stop) =
194 CreateClassStaticPropertiesHelper(literalBuf, nameMap, name, seenComputed);
195 if (stop) {
196 break;
197 }
198
199 compiler::Literal value = PropertyMethodKind(propMethod, compiled, i);
200
201 literalBuf[bufferPos + 1] = std::move(value);
202 }
203
204 return CreateClassStaticPropertiesBuf(buf, privateBuf, staticBuf, pg);
205 }
206
CompileStaticFieldInitializers(compiler::PandaGen * pg,compiler::VReg classReg,const std::vector<compiler::VReg> & staticComputedFieldKeys,const ir::ClassDefinition * node)207 static void CompileStaticFieldInitializers(compiler::PandaGen *pg, compiler::VReg classReg,
208 const std::vector<compiler::VReg> &staticComputedFieldKeys,
209 const ir::ClassDefinition *node)
210 {
211 const auto &properties = node->Body();
212 auto iter = staticComputedFieldKeys.begin();
213
214 if (node->HasPrivateMethod()) {
215 pg->ClassPrivateMethodOrAccessorAdd(node, classReg, classReg);
216 }
217
218 for (const auto *it : properties) {
219 compiler::RegScope rs(pg);
220
221 if (it->IsClassStaticBlock()) {
222 const auto *func = it->AsClassStaticBlock()->Value()->AsFunctionExpression()->Function();
223
224 compiler::VReg funcReg = pg->AllocReg();
225 compiler::VReg thisReg = pg->AllocReg();
226
227 pg->LoadAccumulator(it, classReg);
228 pg->StoreAccumulator(it, thisReg);
229 pg->DefineMethod(it, func->Scope()->InternalName());
230 pg->StoreAccumulator(it, funcReg);
231
232 pg->Call0This(node, funcReg, thisReg);
233 continue;
234 }
235
236 if (it->IsMethodDefinition()) {
237 continue;
238 }
239
240 ES2PANDA_ASSERT(it->IsClassProperty());
241 const ir::ClassProperty *prop = it->AsClassProperty();
242
243 if (!prop->IsStatic()) {
244 continue;
245 }
246
247 compiler::VReg keyReg {};
248
249 if (prop->IsComputed()) {
250 ES2PANDA_ASSERT(iter != staticComputedFieldKeys.end());
251 keyReg = *iter++;
252 } else if (!prop->IsPrivateElement()) {
253 keyReg = pg->LoadPropertyKey(prop->Key(), false);
254 }
255
256 if (prop->Value() == nullptr) {
257 pg->LoadConst(prop, compiler::Constant::JS_UNDEFINED);
258 } else {
259 compiler::RegScope vrs(pg);
260 prop->Value()->Compile(pg);
261 }
262
263 if (prop->IsPrivateElement()) {
264 ES2PANDA_ASSERT(prop->Id() != nullptr);
265 pg->ClassPrivateFieldAdd(prop, classReg, classReg, prop->Id()->Name());
266 continue;
267 }
268
269 pg->ClassFieldAdd(prop, classReg, keyReg);
270 }
271 }
272
CompilePropertyKind(const ir::MethodDefinition * prop,compiler::VReg dest,compiler::PandaGen * pg,const ir::ClassDefinition * node)273 static void CompilePropertyKind(const ir::MethodDefinition *prop, compiler::VReg dest, compiler::PandaGen *pg,
274 const ir::ClassDefinition *node)
275 {
276 switch (prop->Kind()) {
277 case ir::MethodDefinitionKind::METHOD: {
278 compiler::Operand key = pg->ToOwnPropertyKey(prop->Key(), prop->IsComputed());
279
280 pg->LoadAccumulator(node, dest);
281 const ir::FunctionExpression *func = prop->Value()->AsFunctionExpression();
282 func->Compile(pg);
283
284 pg->StoreOwnProperty(prop->Value()->Parent(), dest, key);
285 break;
286 }
287 case ir::MethodDefinitionKind::GET:
288 case ir::MethodDefinitionKind::SET: {
289 compiler::VReg keyReg = pg->LoadPropertyKey(prop->Key(), prop->IsComputed());
290
291 compiler::VReg undef = pg->AllocReg();
292 pg->LoadConst(node, compiler::Constant::JS_UNDEFINED);
293 pg->StoreAccumulator(node, undef);
294
295 compiler::VReg getter = undef;
296 compiler::VReg setter = undef;
297
298 pg->LoadAccumulator(node, dest);
299
300 compiler::VReg accessor = pg->AllocReg();
301 prop->Value()->Compile(pg);
302 pg->StoreAccumulator(prop->Value(), accessor);
303
304 if (prop->Kind() == ir::MethodDefinitionKind::GET) {
305 getter = accessor;
306 } else {
307 setter = accessor;
308 }
309
310 pg->DefineGetterSetterByValue(node, std::make_tuple(dest, keyReg, getter, setter), prop->IsComputed());
311 break;
312 }
313 default: {
314 ES2PANDA_UNREACHABLE();
315 }
316 }
317 }
318
CompileMissingProperties(compiler::PandaGen * pg,const util::BitSet & compiled,compiler::VReg classReg,const ir::ClassDefinition * node)319 static void CompileMissingProperties(compiler::PandaGen *pg, const util::BitSet &compiled, compiler::VReg classReg,
320 const ir::ClassDefinition *node)
321 {
322 const auto &properties = node->Body();
323 std::vector<compiler::VReg> staticComputedFieldKeys;
324 compiler::VReg protoReg = pg->AllocReg();
325 compiler::VReg computedInstanceFieldsArray {};
326 uint32_t computedInstanceFieldsIndex = 0;
327
328 pg->LoadObjByName(node, "prototype");
329 pg->StoreAccumulator(node, protoReg);
330
331 if (node->HasComputedInstanceField()) {
332 pg->CreateEmptyArray(node);
333 computedInstanceFieldsArray = pg->AllocReg();
334 pg->StoreAccumulator(node, computedInstanceFieldsArray);
335 }
336
337 for (size_t i = 0; i < properties.size(); i++) {
338 if (compiled.Test(i)) {
339 continue;
340 }
341
342 if (properties[i]->IsClassStaticBlock()) {
343 continue;
344 }
345
346 if (properties[i]->IsMethodDefinition()) {
347 const ir::MethodDefinition *prop = properties[i]->AsMethodDefinition();
348 compiler::VReg dest = prop->IsStatic() ? classReg : protoReg;
349 compiler::RegScope rs(pg);
350 CompilePropertyKind(prop, dest, pg, node);
351
352 continue;
353 }
354
355 ES2PANDA_ASSERT(properties[i]->IsClassProperty());
356 const ir::ClassProperty *prop = properties[i]->AsClassProperty();
357
358 if (!prop->IsComputed()) {
359 continue;
360 }
361
362 if (prop->IsStatic()) {
363 compiler::VReg keyReg = pg->LoadPropertyKey(prop->Key(), prop->IsComputed());
364 staticComputedFieldKeys.push_back(keyReg);
365 continue;
366 }
367
368 pg->LoadPropertyKeyAcc(prop->Key(), prop->IsComputed());
369 pg->StOwnByIndex(node, computedInstanceFieldsArray, computedInstanceFieldsIndex++);
370 }
371
372 if (computedInstanceFieldsIndex != 0) {
373 pg->SetClassComputedFields(node, classReg, computedInstanceFieldsArray);
374 }
375
376 CompileStaticFieldInitializers(pg, classReg, staticComputedFieldKeys, node);
377 }
378
InitializeClassName(compiler::PandaGen * pg,const ir::ClassDefinition * node)379 static void InitializeClassName(compiler::PandaGen *pg, const ir::ClassDefinition *node)
380 {
381 if (node->Ident() == nullptr) {
382 return;
383 }
384
385 auto lref = compiler::JSLReference::Create(pg, node->Ident(), true);
386 lref.SetValue();
387 }
388
Compile(const ir::ClassDefinition * node) const389 void JSCompiler::Compile(const ir::ClassDefinition *node) const
390 {
391 PandaGen *pg = GetPandaGen();
392 compiler::RegScope rs(pg);
393 compiler::VReg classReg = pg->AllocReg();
394 compiler::VReg lexenv = pg->LexEnv();
395
396 compiler::LocalRegScope lrs(pg, node->Scope());
397
398 compiler::VReg baseReg = CompileHeritageClause(pg, node);
399 ES2PANDA_ASSERT(node->Ctor() != nullptr);
400 util::StringView ctorId = node->Ctor()->Function()->Scope()->InternalName();
401 util::BitSet compiled(node->Body().size());
402
403 auto [bufIdx, privateBuf] = CreateClassStaticProperties(pg, compiled, node->Body());
404
405 pg->DefineClassWithBuffer(node, ctorId, bufIdx, lexenv, baseReg);
406 pg->StoreAccumulator(node, classReg);
407
408 if (!privateBuf.empty()) {
409 pg->DefineClassPrivateFields(node, pg->AddLiteralBuffer(std::move(privateBuf)));
410 }
411
412 auto res = pg->Scope()->Find(node->InternalName());
413 ES2PANDA_ASSERT(res.variable);
414
415 if (res.variable->AsLocalVariable()->LexicalBound()) {
416 pg->StoreLexicalVar(node, res.lexLevel, res.variable->AsLocalVariable()->LexIdx());
417 }
418
419 InitializeClassName(pg, node);
420
421 CompileMissingProperties(pg, compiled, classReg, node);
422
423 pg->LoadAccumulator(node, classReg);
424 }
425
Compile(const ir::MetaProperty * expr) const426 void JSCompiler::Compile(const ir::MetaProperty *expr) const
427 {
428 PandaGen *pg = GetPandaGen();
429 if (expr->Kind() == ir::MetaProperty::MetaPropertyKind::NEW_TARGET) {
430 pg->GetNewTarget(expr);
431 return;
432 }
433
434 if (expr->Kind() == ir::MetaProperty::MetaPropertyKind::IMPORT_META) {
435 // NOTE
436 pg->Unimplemented();
437 }
438 }
439
440 // JSCompiler::compile methods for EXPRESSIONS in alphabetical order
Compile(const ir::ArrayExpression * expr) const441 void JSCompiler::Compile(const ir::ArrayExpression *expr) const
442 {
443 PandaGen *pg = GetPandaGen();
444 compiler::RegScope rs(pg);
445 compiler::VReg arrayObj = pg->AllocReg();
446
447 pg->CreateArray(expr, expr->Elements(), arrayObj);
448 }
449
Compile(const ir::ArrowFunctionExpression * expr) const450 void JSCompiler::Compile(const ir::ArrowFunctionExpression *expr) const
451 {
452 PandaGen *pg = GetPandaGen();
453 pg->DefineFunction(expr->Function(), expr->Function(), expr->Function()->Scope()->InternalName());
454 }
455
Compile(const ir::AssignmentExpression * expr) const456 void JSCompiler::Compile(const ir::AssignmentExpression *expr) const
457 {
458 PandaGen *pg = GetPandaGen();
459 compiler::RegScope rs(pg);
460 auto lref = compiler::JSLReference::Create(pg, expr->Left(), false);
461
462 if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND_EQUAL ||
463 expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_OR_EQUAL) {
464 compiler::PandaGen::Unimplemented();
465 }
466
467 if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
468 expr->Right()->Compile(pg);
469 lref.SetValue();
470 return;
471 }
472
473 compiler::VReg lhsReg = pg->AllocReg();
474
475 lref.GetValue();
476 pg->StoreAccumulator(expr->Left(), lhsReg);
477 expr->Right()->Compile(pg);
478 pg->Binary(expr, expr->OperatorType(), lhsReg);
479
480 lref.SetValue();
481 }
482
Compile(const ir::AwaitExpression * expr) const483 void JSCompiler::Compile(const ir::AwaitExpression *expr) const
484 {
485 PandaGen *pg = GetPandaGen();
486 compiler::RegScope rs(pg);
487
488 if (expr->Argument() != nullptr) {
489 expr->Argument()->Compile(pg);
490 } else {
491 pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED);
492 }
493
494 pg->EmitAwait(expr);
495 }
496
CompileLogical(compiler::PandaGen * pg,const ir::BinaryExpression * expr)497 static void CompileLogical(compiler::PandaGen *pg, const ir::BinaryExpression *expr)
498 {
499 compiler::RegScope rs(pg);
500 compiler::VReg lhs = pg->AllocReg();
501
502 ES2PANDA_ASSERT(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND ||
503 expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_OR ||
504 expr->OperatorType() == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING);
505
506 auto *skipRight = pg->AllocLabel();
507 auto *endLabel = pg->AllocLabel();
508
509 // left -> acc -> lhs -> toboolean -> acc -> bool_lhs
510 expr->Left()->Compile(pg);
511 pg->StoreAccumulator(expr, lhs);
512
513 if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) {
514 pg->ToBoolean(expr);
515 pg->BranchIfFalse(expr, skipRight);
516 } else if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) {
517 pg->ToBoolean(expr);
518 pg->BranchIfTrue(expr, skipRight);
519 } else if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING) {
520 pg->BranchIfCoercible(expr, skipRight);
521 }
522
523 // left is true/false(and/or) then right -> acc
524 expr->Right()->Compile(pg);
525 pg->Branch(expr, endLabel);
526
527 // left is false/true(and/or) then lhs -> acc
528 pg->SetLabel(expr, skipRight);
529 pg->LoadAccumulator(expr, lhs);
530 pg->SetLabel(expr, endLabel);
531 }
532
Compile(const ir::BinaryExpression * expr) const533 void JSCompiler::Compile(const ir::BinaryExpression *expr) const
534 {
535 PandaGen *pg = GetPandaGen();
536 if (expr->IsLogical()) {
537 CompileLogical(pg, expr);
538 return;
539 }
540
541 if (expr->OperatorType() == lexer::TokenType::KEYW_IN && expr->Left()->IsIdentifier() &&
542 expr->Left()->AsIdentifier()->IsPrivateIdent()) {
543 compiler::RegScope rs(pg);
544 compiler::VReg ctor = pg->AllocReg();
545 const auto &name = expr->Left()->AsIdentifier()->Name();
546 compiler::Function::LoadClassContexts(expr, pg, ctor, name);
547 expr->Right()->Compile(pg);
548 pg->ClassPrivateFieldIn(expr, ctor, name);
549 return;
550 }
551
552 compiler::RegScope rs(pg);
553 compiler::VReg lhs = pg->AllocReg();
554
555 expr->Left()->Compile(pg);
556 pg->StoreAccumulator(expr, lhs);
557 expr->Right()->Compile(pg);
558
559 pg->Binary(expr, expr->OperatorType(), lhs);
560 }
561
CreateSpreadArguments(compiler::PandaGen * pg,const ir::CallExpression * expr)562 static compiler::VReg CreateSpreadArguments(compiler::PandaGen *pg, const ir::CallExpression *expr)
563 {
564 compiler::VReg argsObj = pg->AllocReg();
565 pg->CreateArray(expr, expr->Arguments(), argsObj);
566
567 return argsObj;
568 }
569
CompileSuperExprWithoutSpread(PandaGen * pg,const ir::CallExpression * expr)570 void CompileSuperExprWithoutSpread(PandaGen *pg, const ir::CallExpression *expr)
571 {
572 compiler::RegScope paramScope(pg);
573 compiler::VReg argStart {};
574
575 if (expr->Arguments().empty()) {
576 argStart = pg->AllocReg();
577 pg->StoreConst(expr, argStart, compiler::Constant::JS_UNDEFINED);
578 } else {
579 argStart = pg->NextReg();
580 }
581
582 for (const auto *it : expr->Arguments()) {
583 compiler::VReg arg = pg->AllocReg();
584 it->Compile(pg);
585 pg->StoreAccumulator(it, arg);
586 }
587
588 pg->GetFunctionObject(expr);
589 pg->SuperCall(expr, argStart, expr->Arguments().size());
590 }
591
Compile(const ir::CallExpression * expr) const592 void JSCompiler::Compile(const ir::CallExpression *expr) const
593 {
594 PandaGen *pg = GetPandaGen();
595 compiler::RegScope rs(pg);
596 bool containsSpread = util::Helpers::ContainSpreadElement(expr->Arguments());
597
598 if (expr->Callee()->IsSuperExpression()) {
599 if (containsSpread) {
600 compiler::RegScope paramScope(pg);
601 compiler::VReg argsObj = CreateSpreadArguments(pg, expr);
602
603 pg->GetFunctionObject(expr);
604 pg->SuperCallSpread(expr, argsObj);
605 } else {
606 CompileSuperExprWithoutSpread(pg, expr);
607 }
608
609 compiler::VReg newThis = pg->AllocReg();
610 pg->StoreAccumulator(expr, newThis);
611
612 pg->GetThis(expr);
613 pg->ThrowIfSuperNotCorrectCall(expr, 1);
614
615 pg->LoadAccumulator(expr, newThis);
616 pg->SetThis(expr);
617
618 compiler::Function::CompileInstanceFields(pg, pg->RootNode()->AsScriptFunction());
619 return;
620 }
621
622 compiler::VReg callee = pg->AllocReg();
623 compiler::VReg thisReg = compiler::VReg::Invalid();
624
625 if (expr->Callee()->IsMemberExpression()) {
626 thisReg = pg->AllocReg();
627
628 compiler::RegScope mrs(pg);
629 expr->Callee()->AsMemberExpression()->CompileToReg(pg, thisReg);
630 } else if (expr->Callee()->IsChainExpression()) {
631 thisReg = pg->AllocReg();
632
633 compiler::RegScope mrs(pg);
634 expr->Callee()->AsChainExpression()->CompileToReg(pg, thisReg);
635 } else {
636 expr->Callee()->Compile(pg);
637 }
638
639 pg->StoreAccumulator(expr, callee);
640 pg->OptionalChainCheck(expr->IsOptional(), callee);
641
642 if (containsSpread || expr->Arguments().size() >= compiler::PandaGen::MAX_RANGE_CALL_ARG) {
643 if (thisReg.IsInvalid()) {
644 thisReg = pg->AllocReg();
645 pg->StoreConst(expr, thisReg, compiler::Constant::JS_UNDEFINED);
646 }
647
648 compiler::VReg argsObj = CreateSpreadArguments(pg, expr);
649 pg->CallSpread(expr, callee, thisReg, argsObj);
650 } else {
651 pg->Call(expr, callee, thisReg, expr->Arguments());
652 }
653 }
654
Compile(const ir::ChainExpression * expr) const655 void JSCompiler::Compile(const ir::ChainExpression *expr) const
656 {
657 PandaGen *pg = GetPandaGen();
658 compiler::OptionalChain chain(pg, expr);
659 expr->GetExpression()->Compile(pg);
660 }
661
Compile(const ir::ClassExpression * expr) const662 void JSCompiler::Compile(const ir::ClassExpression *expr) const
663 {
664 PandaGen *pg = GetPandaGen();
665 expr->Definition()->Compile(pg);
666 }
667
668 template <typename CodeGen>
CompileImpl(const ir::ConditionalExpression * self,CodeGen * cg)669 static void CompileImpl(const ir::ConditionalExpression *self, CodeGen *cg)
670 {
671 auto *falseLabel = cg->AllocLabel();
672 auto *endLabel = cg->AllocLabel();
673
674 compiler::Condition::Compile(cg, self->Test(), falseLabel);
675 self->Consequent()->Compile(cg);
676 cg->Branch(self, endLabel);
677 cg->SetLabel(self, falseLabel);
678 self->Alternate()->Compile(cg);
679 cg->SetLabel(self, endLabel);
680 }
681
Compile(const ir::ConditionalExpression * expr) const682 void JSCompiler::Compile(const ir::ConditionalExpression *expr) const
683 {
684 PandaGen *pg = GetPandaGen();
685 CompileImpl(expr, pg);
686 }
687
Compile(const ir::DirectEvalExpression * expr) const688 void JSCompiler::Compile(const ir::DirectEvalExpression *expr) const
689 {
690 PandaGen *pg = GetPandaGen();
691 if (expr->Arguments().empty()) {
692 pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED);
693 return;
694 }
695
696 compiler::RegScope rs(pg);
697 bool containsSpread = util::Helpers::ContainSpreadElement(expr->Arguments());
698 if (containsSpread) {
699 [[maybe_unused]] compiler::VReg argsObj = CreateSpreadArguments(pg, expr);
700 pg->LoadObjByIndex(expr, 0);
701 } else {
702 compiler::VReg arg0 = pg->AllocReg();
703 auto iter = expr->Arguments().cbegin();
704 (*iter++)->Compile(pg);
705 pg->StoreAccumulator(expr, arg0);
706
707 while (iter != expr->Arguments().cend()) {
708 (*iter++)->Compile(pg);
709 }
710
711 pg->LoadAccumulator(expr, arg0);
712 }
713
714 pg->DirectEval(expr, expr->parserStatus_);
715 }
716
Compile(const ir::FunctionExpression * expr) const717 void JSCompiler::Compile(const ir::FunctionExpression *expr) const
718 {
719 PandaGen *pg = GetPandaGen();
720 pg->DefineFunction(expr->Function(), expr->Function(), expr->Function()->Scope()->InternalName());
721 }
722
Compile(const ir::Identifier * expr) const723 void JSCompiler::Compile(const ir::Identifier *expr) const
724 {
725 PandaGen *pg = GetPandaGen();
726 auto res = pg->Scope()->Find(expr->Name());
727 if (res.variable != nullptr) {
728 pg->LoadVar(expr, res);
729 return;
730 }
731
732 if (pg->IsDirectEval()) {
733 pg->LoadEvalVariable(expr, expr->Name());
734 return;
735 }
736
737 if (expr->Name().Is("NaN")) {
738 pg->LoadConst(expr, compiler::Constant::JS_NAN);
739 return;
740 }
741
742 if (expr->Name().Is("Infinity")) {
743 pg->LoadConst(expr, compiler::Constant::JS_INFINITY);
744 return;
745 }
746
747 if (expr->Name().Is("globalThis")) {
748 pg->LoadConst(expr, compiler::Constant::JS_GLOBAL);
749 return;
750 }
751
752 if (expr->Name().Is("undefined")) {
753 pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED);
754 return;
755 }
756
757 pg->TryLoadGlobalByName(expr, expr->Name());
758 }
759
Compile(const ir::ImportExpression * expr) const760 void JSCompiler::Compile([[maybe_unused]] const ir::ImportExpression *expr) const
761 {
762 PandaGen *pg = GetPandaGen();
763 pg->Unimplemented();
764 }
765
Compile(const ir::MemberExpression * expr) const766 void JSCompiler::Compile(const ir::MemberExpression *expr) const
767 {
768 PandaGen *pg = GetPandaGen();
769 expr->Object()->Compile(pg);
770 pg->OptionalChainCheck(expr->IsOptional(), compiler::VReg::Invalid());
771 expr->LoadRhs(pg);
772 }
773
Compile(const ir::NewExpression * expr) const774 void JSCompiler::Compile(const ir::NewExpression *expr) const
775 {
776 PandaGen *pg = GetPandaGen();
777 compiler::RegScope rs(pg);
778 compiler::VReg ctor = pg->AllocReg();
779 compiler::VReg newTarget = pg->AllocReg();
780
781 expr->Callee()->Compile(pg);
782 pg->StoreAccumulator(expr, ctor);
783
784 // new.Target will be the same as ctor
785 pg->StoreAccumulator(expr, newTarget);
786
787 if (!util::Helpers::ContainSpreadElement(expr->Arguments()) &&
788 expr->Arguments().size() < compiler::PandaGen::MAX_RANGE_CALL_ARG) {
789 for (const auto *it : expr->Arguments()) {
790 compiler::VReg arg = pg->AllocReg();
791 it->Compile(pg);
792 pg->StoreAccumulator(expr, arg);
793 }
794
795 pg->NewObject(expr, ctor, expr->Arguments().size() + 2U);
796 } else {
797 compiler::VReg argsObj = pg->AllocReg();
798
799 pg->CreateArray(expr, expr->Arguments(), argsObj);
800 pg->NewObjSpread(expr, ctor, newTarget);
801 }
802 }
803
Compile(const ir::ObjectExpression * expr) const804 void JSCompiler::Compile(const ir::ObjectExpression *expr) const
805 {
806 PandaGen *pg = GetPandaGen();
807 if (expr->Properties().empty()) {
808 pg->CreateEmptyObject(expr);
809 return;
810 }
811
812 util::BitSet compiled(expr->Properties().size());
813 CompileStaticProperties(pg, &compiled, expr);
814
815 if (compiled.Any(false)) {
816 CompileRemainingProperties(pg, &compiled, expr);
817 }
818 }
819
CreateLiteral(const ir::Property * prop,util::BitSet * compiled,size_t propIndex)820 static compiler::Literal CreateLiteral(const ir::Property *prop, util::BitSet *compiled, size_t propIndex)
821 {
822 compiler::Literal lit = util::Helpers::ToConstantLiteral(prop->Value());
823 if (!lit.IsInvalid()) {
824 compiled->Set(propIndex);
825 return lit;
826 }
827
828 if (prop->Kind() != ir::PropertyKind::INIT) {
829 ES2PANDA_ASSERT(prop->IsAccessor());
830 return compiler::Literal::AccessorLiteral();
831 }
832
833 if (!prop->Value()->IsFunctionExpression()) {
834 return compiler::Literal::NullLiteral();
835 }
836
837 const ir::ScriptFunction *method = prop->Value()->AsFunctionExpression()->Function();
838
839 compiler::LiteralTag tag = compiler::LiteralTag::METHOD;
840
841 if (method->IsGenerator()) {
842 tag = compiler::LiteralTag::GENERATOR_METHOD;
843
844 if (method->IsAsyncFunc()) {
845 tag = compiler::LiteralTag::ASYNC_GENERATOR_METHOD;
846 }
847 }
848
849 compiled->Set(propIndex);
850 return compiler::Literal(tag, method->Scope()->InternalName());
851 }
852
IsLiteralBufferCompatible(const ir::Expression * expr)853 static bool IsLiteralBufferCompatible(const ir::Expression *expr)
854 {
855 if (expr->IsSpreadElement()) {
856 return false;
857 }
858
859 const ir::Property *prop = expr->AsProperty();
860 if (prop->Value()->IsFunctionExpression() && !prop->Value()->AsFunctionExpression()->Function()->IsMethod()) {
861 return false;
862 }
863
864 return util::Helpers::IsConstantPropertyKey(prop->Key(), prop->IsComputed()) &&
865 prop->Kind() != ir::PropertyKind::PROTO;
866 }
867
CompileStaticProperties(compiler::PandaGen * pg,util::BitSet * compiled,const ir::ObjectExpression * expr) const868 void JSCompiler::CompileStaticProperties(compiler::PandaGen *pg, util::BitSet *compiled,
869 const ir::ObjectExpression *expr) const
870 {
871 bool hasMethod = false;
872 bool seenComputed = false;
873 compiler::LiteralBuffer buf;
874 std::unordered_map<util::StringView, size_t> propNameMap;
875
876 for (size_t i = 0; i < expr->Properties().size(); i++) {
877 if (!IsLiteralBufferCompatible(expr->Properties()[i])) {
878 seenComputed = true;
879 continue;
880 }
881
882 const ir::Property *prop = expr->Properties()[i]->AsProperty();
883
884 util::StringView name = util::Helpers::LiteralToPropName(prop->Key());
885 size_t bufferPos = buf.size();
886 auto res = propNameMap.insert({name, bufferPos});
887 if (res.second) {
888 if (seenComputed) {
889 break;
890 }
891
892 buf.emplace_back(name);
893 buf.emplace_back();
894 } else {
895 bufferPos = res.first->second;
896 }
897
898 compiler::Literal lit = CreateLiteral(prop, compiled, i);
899 if (lit.IsTagMethod()) {
900 hasMethod = true;
901 }
902
903 buf[bufferPos + 1] = std::move(lit);
904 }
905
906 if (buf.empty()) {
907 pg->CreateEmptyObject(expr);
908 return;
909 }
910
911 uint32_t bufIdx = pg->AddLiteralBuffer(std::move(buf));
912
913 if (hasMethod) {
914 pg->CreateObjectHavingMethod(expr, bufIdx);
915 } else {
916 pg->CreateObjectWithBuffer(expr, bufIdx);
917 }
918 }
919
CompileRemainingPropertyKind(const ir::Property * prop,compiler::VReg objReg,compiler::PandaGen * pg,const ir::ObjectExpression * expr)920 void CompileRemainingPropertyKind(const ir::Property *prop, compiler::VReg objReg, compiler::PandaGen *pg,
921 const ir::ObjectExpression *expr)
922 {
923 switch (prop->Kind()) {
924 case ir::PropertyKind::GET:
925 case ir::PropertyKind::SET: {
926 compiler::VReg key = pg->LoadPropertyKey(prop->Key(), prop->IsComputed());
927
928 compiler::VReg undef = pg->AllocReg();
929 pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED);
930 pg->StoreAccumulator(expr, undef);
931
932 compiler::VReg getter = undef;
933 compiler::VReg setter = undef;
934
935 compiler::VReg accessor = pg->AllocReg();
936 pg->LoadAccumulator(prop->Value(), objReg);
937 prop->Value()->Compile(pg);
938 pg->StoreAccumulator(prop->Value(), accessor);
939
940 if (prop->Kind() == ir::PropertyKind::GET) {
941 getter = accessor;
942 } else {
943 setter = accessor;
944 }
945
946 pg->DefineGetterSetterByValue(expr, std::make_tuple(objReg, key, getter, setter), prop->IsComputed());
947 break;
948 }
949 case ir::PropertyKind::INIT: {
950 compiler::Operand key = pg->ToOwnPropertyKey(prop->Key(), prop->IsComputed());
951
952 if (prop->IsMethod()) {
953 pg->LoadAccumulator(prop->Value(), objReg);
954 }
955
956 prop->Value()->Compile(pg);
957 pg->StoreOwnProperty(expr, objReg, key);
958 break;
959 }
960 case ir::PropertyKind::PROTO: {
961 prop->Value()->Compile(pg);
962 compiler::VReg proto = pg->AllocReg();
963 pg->StoreAccumulator(expr, proto);
964
965 pg->SetObjectWithProto(expr, proto, objReg);
966 break;
967 }
968 default: {
969 ES2PANDA_UNREACHABLE();
970 }
971 }
972 }
973
CompileRemainingProperties(compiler::PandaGen * pg,const util::BitSet * compiled,const ir::ObjectExpression * expr) const974 void JSCompiler::CompileRemainingProperties(compiler::PandaGen *pg, const util::BitSet *compiled,
975 const ir::ObjectExpression *expr) const
976 {
977 compiler::RegScope rs(pg);
978 compiler::VReg objReg = pg->AllocReg();
979
980 pg->StoreAccumulator(expr, objReg);
981
982 for (size_t i = 0; i < expr->Properties().size(); i++) {
983 if (compiled->Test(i)) {
984 continue;
985 }
986
987 compiler::RegScope prs(pg);
988
989 if (expr->Properties()[i]->IsSpreadElement()) {
990 compiler::VReg srcObj = pg->AllocReg();
991 auto const *const spread = expr->Properties()[i]->AsSpreadElement();
992
993 spread->Argument()->Compile(pg);
994 pg->StoreAccumulator(spread, srcObj);
995
996 pg->CopyDataProperties(spread, objReg, srcObj);
997 continue;
998 }
999
1000 const ir::Property *prop = expr->Properties()[i]->AsProperty();
1001 CompileRemainingPropertyKind(prop, objReg, pg, expr);
1002 }
1003
1004 pg->LoadAccumulator(expr, objReg);
1005 }
1006
Compile(const ir::SequenceExpression * expr) const1007 void JSCompiler::Compile(const ir::SequenceExpression *expr) const
1008 {
1009 PandaGen *pg = GetPandaGen();
1010 for (const auto *it : expr->Sequence()) {
1011 it->Compile(pg);
1012 }
1013 }
1014
Compile(const ir::SuperExpression * expr) const1015 void JSCompiler::Compile(const ir::SuperExpression *expr) const
1016 {
1017 PandaGen *pg = GetPandaGen();
1018 pg->GetThis(expr);
1019
1020 const ir::ScriptFunction *func = util::Helpers::GetContainingConstructor(expr);
1021
1022 if (func != nullptr) {
1023 pg->ThrowIfSuperNotCorrectCall(expr, 0);
1024 }
1025 }
1026
Compile(const ir::TaggedTemplateExpression * expr) const1027 void JSCompiler::Compile(const ir::TaggedTemplateExpression *expr) const
1028 {
1029 PandaGen *pg = GetPandaGen();
1030 compiler::RegScope rs(pg);
1031 compiler::VReg callee = pg->AllocReg();
1032 compiler::VReg thisReg = compiler::VReg::Invalid();
1033
1034 if (expr->Tag()->IsMemberExpression()) {
1035 thisReg = pg->AllocReg();
1036 compiler::RegScope mrs(pg);
1037 expr->Tag()->AsMemberExpression()->CompileToReg(pg, thisReg);
1038 } else {
1039 expr->Tag()->Compile(pg);
1040 }
1041
1042 pg->CallTagged(expr, callee, thisReg, expr->Quasi()->Expressions());
1043 }
1044
Compile(const ir::TemplateLiteral * expr) const1045 void JSCompiler::Compile(const ir::TemplateLiteral *expr) const
1046 {
1047 PandaGen *pg = GetPandaGen();
1048 auto quasisIt = expr->Quasis().begin();
1049 auto expressionIt = expr->Expressions().begin();
1050
1051 pg->LoadAccumulatorString(expr, (*quasisIt)->Raw());
1052
1053 quasisIt++;
1054
1055 bool isQuais = false;
1056 size_t total = expr->Quasis().size() + expr->Expressions().size();
1057
1058 compiler::RegScope rs(pg);
1059 compiler::VReg lhs = pg->AllocReg();
1060
1061 while (total != 1) {
1062 const ir::AstNode *node = nullptr;
1063
1064 if (isQuais) {
1065 pg->StoreAccumulator(*quasisIt, lhs);
1066 pg->LoadAccumulatorString(expr, (*quasisIt)->Raw());
1067
1068 node = *quasisIt;
1069 quasisIt++;
1070 } else {
1071 const ir::Expression *element = *expressionIt;
1072 pg->StoreAccumulator(element, lhs);
1073
1074 element->Compile(pg);
1075
1076 node = element;
1077 expressionIt++;
1078 }
1079
1080 pg->Binary(node, lexer::TokenType::PUNCTUATOR_PLUS, lhs);
1081
1082 isQuais = !isQuais;
1083 total--;
1084 }
1085 }
1086
Compile(const ir::ThisExpression * expr) const1087 void JSCompiler::Compile(const ir::ThisExpression *expr) const
1088 {
1089 PandaGen *pg = GetPandaGen();
1090 auto res = pg->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS);
1091
1092 ES2PANDA_ASSERT(res.variable && res.variable->IsLocalVariable());
1093 pg->LoadAccFromLexEnv(expr, res);
1094
1095 const ir::ScriptFunction *func = util::Helpers::GetContainingConstructor(expr);
1096
1097 if (func != nullptr) {
1098 pg->ThrowIfSuperNotCorrectCall(expr, 0);
1099 }
1100 }
1101
Compile(const ir::TypeofExpression * expr) const1102 void JSCompiler::Compile([[maybe_unused]] const ir::TypeofExpression *expr) const
1103 {
1104 PandaGen *pg = GetPandaGen();
1105
1106 if (expr->Argument()->IsIdentifier()) {
1107 const auto *ident = expr->Argument()->AsIdentifier();
1108
1109 auto res = pg->Scope()->Find(ident->Name());
1110 if (res.variable == nullptr) {
1111 pg->LoadConst(expr, compiler::Constant::JS_GLOBAL);
1112 pg->LoadObjByName(expr, ident->Name());
1113 } else {
1114 pg->LoadVar(ident, res);
1115 }
1116 } else {
1117 expr->Argument()->Compile(pg);
1118 }
1119
1120 pg->TypeOf(expr);
1121 }
1122
Compile(const ir::UnaryExpression * expr) const1123 void JSCompiler::Compile(const ir::UnaryExpression *expr) const
1124 {
1125 PandaGen *pg = GetPandaGen();
1126 switch (expr->OperatorType()) {
1127 case lexer::TokenType::KEYW_DELETE: {
1128 if (expr->Argument()->IsIdentifier()) {
1129 auto result = pg->Scope()->Find(expr->Argument()->AsIdentifier()->Name());
1130 if (result.variable == nullptr ||
1131 (result.scope->IsGlobalScope() && result.variable->IsGlobalVariable())) {
1132 compiler::RegScope rs(pg);
1133 compiler::VReg variable = pg->AllocReg();
1134 compiler::VReg global = pg->AllocReg();
1135
1136 pg->LoadConst(expr, compiler::Constant::JS_GLOBAL);
1137 pg->StoreAccumulator(expr, global);
1138
1139 pg->LoadAccumulatorString(expr, expr->Argument()->AsIdentifier()->Name());
1140 pg->StoreAccumulator(expr, variable);
1141
1142 pg->DeleteObjProperty(expr, global, variable);
1143 } else {
1144 // Otherwise it is a local variable which can't be deleted and we just
1145 // return false.
1146 pg->LoadConst(expr, compiler::Constant::JS_FALSE);
1147 }
1148 } else if (expr->Argument()->IsMemberExpression()) {
1149 compiler::RegScope rs(pg);
1150 compiler::VReg object = pg->AllocReg();
1151 compiler::VReg property = pg->AllocReg();
1152
1153 expr->Argument()->AsMemberExpression()->CompileToRegs(pg, object, property);
1154 pg->DeleteObjProperty(expr, object, property);
1155 } else {
1156 // compile the delete operand.
1157 expr->Argument()->Compile(pg);
1158 // Deleting any value or a result of an expression returns True.
1159 pg->LoadConst(expr, compiler::Constant::JS_TRUE);
1160 }
1161 break;
1162 }
1163 case lexer::TokenType::KEYW_VOID: {
1164 expr->Argument()->Compile(pg);
1165 pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED);
1166 break;
1167 }
1168 default: {
1169 expr->Argument()->Compile(pg);
1170
1171 compiler::RegScope rs(pg);
1172 compiler::VReg operandReg = pg->AllocReg();
1173 pg->StoreAccumulator(expr, operandReg);
1174 pg->Unary(expr, expr->OperatorType(), operandReg);
1175 break;
1176 }
1177 }
1178 }
1179
Compile(const ir::UpdateExpression * expr) const1180 void JSCompiler::Compile(const ir::UpdateExpression *expr) const
1181 {
1182 PandaGen *pg = GetPandaGen();
1183 compiler::RegScope rs(pg);
1184 compiler::VReg operandReg = pg->AllocReg();
1185
1186 auto lref = compiler::JSLReference::Create(pg, expr->Argument(), false);
1187 lref.GetValue();
1188
1189 pg->StoreAccumulator(expr, operandReg);
1190 pg->Unary(expr, expr->OperatorType(), operandReg);
1191
1192 lref.SetValue();
1193
1194 if (!expr->IsPrefix()) {
1195 pg->ToNumber(expr, operandReg);
1196 }
1197 }
1198
Compile(const ir::YieldExpression * expr) const1199 void JSCompiler::Compile(const ir::YieldExpression *expr) const
1200 {
1201 PandaGen *pg = GetPandaGen();
1202 compiler::RegScope rs(pg);
1203
1204 if (expr->Argument() != nullptr) {
1205 expr->Argument()->Compile(pg);
1206 } else {
1207 pg->LoadConst(expr, compiler::Constant::JS_UNDEFINED);
1208 }
1209
1210 if (expr->HasDelegate()) {
1211 ES2PANDA_ASSERT(expr->Argument());
1212 pg->FuncBuilder()->YieldStar(expr);
1213 } else {
1214 pg->FuncBuilder()->Yield(expr);
1215 }
1216 }
1217
1218 // Compile methods for LITERAL EXPRESSIONS in alphabetical order
Compile(const ir::BigIntLiteral * expr) const1219 void JSCompiler::Compile(const ir::BigIntLiteral *expr) const
1220 {
1221 PandaGen *pg = GetPandaGen();
1222 pg->LoadAccumulatorBigInt(expr, expr->Str());
1223 }
1224
Compile(const ir::BooleanLiteral * expr) const1225 void JSCompiler::Compile(const ir::BooleanLiteral *expr) const
1226 {
1227 PandaGen *pg = GetPandaGen();
1228 pg->LoadConst(expr, expr->Value() ? compiler::Constant::JS_TRUE : compiler::Constant::JS_FALSE);
1229 }
1230
Compile(const ir::NullLiteral * expr) const1231 void JSCompiler::Compile(const ir::NullLiteral *expr) const
1232 {
1233 PandaGen *pg = GetPandaGen();
1234 pg->LoadConst(expr, compiler::Constant::JS_NULL);
1235 }
1236
Compile(const ir::NumberLiteral * expr) const1237 void JSCompiler::Compile(const ir::NumberLiteral *expr) const
1238 {
1239 PandaGen *pg = GetPandaGen();
1240 if (std::isnan(expr->Number().GetDouble())) {
1241 pg->LoadConst(expr, compiler::Constant::JS_NAN);
1242 } else if (!std::isfinite(expr->Number().GetDouble())) {
1243 pg->LoadConst(expr, compiler::Constant::JS_INFINITY);
1244 } else if (util::Helpers::IsInteger<int32_t>(expr->Number().GetDouble())) {
1245 pg->LoadAccumulatorInt(expr, static_cast<int32_t>(expr->Number().GetDouble()));
1246 } else {
1247 pg->LoadAccumulatorDouble(expr, expr->Number().GetDouble());
1248 }
1249 }
1250
Compile(const ir::RegExpLiteral * expr) const1251 void JSCompiler::Compile(const ir::RegExpLiteral *expr) const
1252 {
1253 PandaGen *pg = GetPandaGen();
1254 pg->CreateRegExpWithLiteral(expr, expr->Pattern(), static_cast<uint8_t>(expr->Flags()));
1255 }
1256
Compile(const ir::StringLiteral * expr) const1257 void JSCompiler::Compile(const ir::StringLiteral *expr) const
1258 {
1259 PandaGen *pg = GetPandaGen();
1260 pg->LoadAccumulatorString(expr, expr->Str());
1261 }
1262
1263 // Compile methods for MODULE-related nodes in alphabetical order
Compile(const ir::ExportAllDeclaration * st) const1264 void JSCompiler::Compile([[maybe_unused]] const ir::ExportAllDeclaration *st) const {}
1265
Compile(const ir::ExportDefaultDeclaration * st) const1266 void JSCompiler::Compile(const ir::ExportDefaultDeclaration *st) const
1267 {
1268 PandaGen *pg = GetPandaGen();
1269 st->Decl()->Compile(pg);
1270 pg->StoreModuleVar(st, "default");
1271 }
1272
Compile(const ir::ExportNamedDeclaration * st) const1273 void JSCompiler::Compile(const ir::ExportNamedDeclaration *st) const
1274 {
1275 PandaGen *pg = GetPandaGen();
1276 if (st->Decl() == nullptr) {
1277 return;
1278 }
1279
1280 st->Decl()->Compile(pg);
1281 }
1282
Compile(const ir::ImportDeclaration * st) const1283 void JSCompiler::Compile([[maybe_unused]] const ir::ImportDeclaration *st) const {}
1284
Compile(const ir::BlockStatement * st) const1285 void JSCompiler::Compile(const ir::BlockStatement *st) const
1286 {
1287 PandaGen *pg = GetPandaGen();
1288 compiler::LocalRegScope lrs(pg, st->Scope());
1289
1290 for (const auto *it : st->Statements()) {
1291 it->Compile(pg);
1292 }
1293 }
1294
1295 template <typename CodeGen>
CompileImpl(const ir::BreakStatement * self,CodeGen * cg)1296 static void CompileImpl(const ir::BreakStatement *self, [[maybe_unused]] CodeGen *cg)
1297 {
1298 compiler::Label *target = cg->ControlFlowChangeBreak(self->Ident());
1299 cg->Branch(self, target);
1300 }
Compile(const ir::BreakStatement * st) const1301 void JSCompiler::Compile(const ir::BreakStatement *st) const
1302 {
1303 PandaGen *pg = GetPandaGen();
1304 CompileImpl(st, pg);
1305 }
1306
Compile(const ir::ClassDeclaration * st) const1307 void JSCompiler::Compile(const ir::ClassDeclaration *st) const
1308 {
1309 PandaGen *pg = GetPandaGen();
1310 auto lref = compiler::JSLReference::Create(pg, st->Definition()->Ident(), true);
1311 st->Definition()->Compile(pg);
1312 lref.SetValue();
1313 }
1314
CompileImpl(const ir::ContinueStatement * self,PandaGen * cg)1315 static void CompileImpl(const ir::ContinueStatement *self, PandaGen *cg)
1316 {
1317 compiler::Label *target = cg->ControlFlowChangeContinue(self->Ident());
1318 cg->Branch(self, target);
1319 }
1320
Compile(const ir::ContinueStatement * st) const1321 void JSCompiler::Compile(const ir::ContinueStatement *st) const
1322 {
1323 PandaGen *pg = GetPandaGen();
1324 CompileImpl(st, pg);
1325 }
1326
Compile(const ir::DebuggerStatement * st) const1327 void JSCompiler::Compile([[maybe_unused]] const ir::DebuggerStatement *st) const {}
1328
CompileImpl(const ir::DoWhileStatement * self,PandaGen * cg)1329 static void CompileImpl(const ir::DoWhileStatement *self, PandaGen *cg)
1330 {
1331 auto *startLabel = cg->AllocLabel();
1332 compiler::LabelTarget labelTarget(cg);
1333
1334 cg->SetLabel(self, startLabel);
1335
1336 {
1337 compiler::LocalRegScope regScope(cg, self->Scope());
1338 compiler::LabelContext labelCtx(cg, labelTarget);
1339 self->Body()->Compile(cg);
1340 }
1341
1342 cg->SetLabel(self, labelTarget.ContinueTarget());
1343 compiler::Condition::Compile(cg, self->Test(), labelTarget.BreakTarget());
1344
1345 cg->Branch(self, startLabel);
1346 cg->SetLabel(self, labelTarget.BreakTarget());
1347 }
1348
Compile(const ir::DoWhileStatement * st) const1349 void JSCompiler::Compile(const ir::DoWhileStatement *st) const
1350 {
1351 PandaGen *pg = GetPandaGen();
1352 CompileImpl(st, pg);
1353 }
1354
Compile(const ir::EmptyStatement * st) const1355 void JSCompiler::Compile([[maybe_unused]] const ir::EmptyStatement *st) const {}
1356
Compile(const ir::ExpressionStatement * st) const1357 void JSCompiler::Compile(const ir::ExpressionStatement *st) const
1358 {
1359 PandaGen *pg = GetPandaGen();
1360 st->GetExpression()->Compile(pg);
1361 }
1362
Compile(const ir::ForInStatement * st) const1363 void JSCompiler::Compile(const ir::ForInStatement *st) const
1364 {
1365 PandaGen *pg = GetPandaGen();
1366 compiler::LabelTarget labelTarget(pg);
1367
1368 compiler::RegScope rs(pg);
1369 compiler::VReg iter = pg->AllocReg();
1370 compiler::VReg propName = pg->AllocReg();
1371
1372 // create enumerator
1373 st->Right()->Compile(pg);
1374 pg->GetPropIterator(st);
1375 pg->StoreAccumulator(st, iter);
1376
1377 pg->SetLabel(st, labelTarget.ContinueTarget());
1378
1379 // get next prop of enumerator
1380 pg->GetNextPropName(st, iter);
1381 pg->StoreAccumulator(st, propName);
1382 pg->BranchIfUndefined(st, labelTarget.BreakTarget());
1383
1384 compiler::LocalRegScope declRegScope(pg, st->Scope()->DeclScope()->InitScope());
1385 auto lref = compiler::JSLReference::Create(pg, st->Left(), false);
1386 pg->LoadAccumulator(st, propName);
1387 lref.SetValue();
1388
1389 compiler::LoopEnvScope declEnvScope(pg, st->Scope()->DeclScope());
1390
1391 {
1392 compiler::LoopEnvScope envScope(pg, st->Scope(), labelTarget);
1393 st->Body()->Compile(pg);
1394 }
1395
1396 pg->Branch(st, labelTarget.ContinueTarget());
1397 pg->SetLabel(st, labelTarget.BreakTarget());
1398 }
1399
Compile(const ir::ForOfStatement * st) const1400 void JSCompiler::Compile(const ir::ForOfStatement *st) const
1401 {
1402 PandaGen *pg = GetPandaGen();
1403 compiler::LocalRegScope declRegScope(pg, st->Scope()->DeclScope()->InitScope());
1404
1405 st->Right()->Compile(pg);
1406
1407 compiler::LabelTarget labelTarget(pg);
1408 auto iteratorType = st->IsAwait() ? compiler::IteratorType::ASYNC : compiler::IteratorType::SYNC;
1409 compiler::Iterator iterator(pg, st, iteratorType);
1410
1411 pg->SetLabel(st, labelTarget.ContinueTarget());
1412
1413 iterator.Next();
1414 iterator.Complete();
1415 pg->BranchIfTrue(st, labelTarget.BreakTarget());
1416
1417 iterator.Value();
1418 pg->StoreAccumulator(st, iterator.NextResult());
1419
1420 auto lref = compiler::JSLReference::Create(pg, st->Left(), false);
1421
1422 {
1423 compiler::IteratorContext forOfCtx(pg, iterator, labelTarget);
1424 pg->LoadAccumulator(st, iterator.NextResult());
1425 lref.SetValue();
1426
1427 compiler::LoopEnvScope declEnvScope(pg, st->Scope()->DeclScope());
1428 compiler::LoopEnvScope envScope(pg, st->Scope(), {});
1429 st->Body()->Compile(pg);
1430 }
1431
1432 pg->Branch(st, labelTarget.ContinueTarget());
1433 pg->SetLabel(st, labelTarget.BreakTarget());
1434 }
1435
Compile(const ir::ForUpdateStatement * st) const1436 void JSCompiler::Compile(const ir::ForUpdateStatement *st) const
1437 {
1438 PandaGen *pg = GetPandaGen();
1439 compiler::LocalRegScope declRegScope(pg, st->Scope()->DeclScope()->InitScope());
1440
1441 if (st->Init() != nullptr) {
1442 ES2PANDA_ASSERT(st->Init()->IsVariableDeclaration() || st->Init()->IsExpression());
1443 st->Init()->Compile(pg);
1444 }
1445
1446 auto *startLabel = pg->AllocLabel();
1447 compiler::LabelTarget labelTarget(pg);
1448
1449 compiler::LoopEnvScope declEnvScope(pg, st->Scope()->DeclScope());
1450 compiler::LoopEnvScope envScope(pg, labelTarget, st->Scope());
1451 pg->SetLabel(st, startLabel);
1452
1453 {
1454 compiler::LocalRegScope regScope(pg, st->Scope());
1455
1456 if (st->Test() != nullptr) {
1457 compiler::Condition::Compile(pg, st->Test(), labelTarget.BreakTarget());
1458 }
1459
1460 st->Body()->Compile(pg);
1461 pg->SetLabel(st, labelTarget.ContinueTarget());
1462 envScope.CopyPetIterationCtx();
1463 }
1464
1465 if (st->Update() != nullptr) {
1466 st->Update()->Compile(pg);
1467 }
1468
1469 pg->Branch(st, startLabel);
1470 pg->SetLabel(st, labelTarget.BreakTarget());
1471 }
1472
Compile(const ir::FunctionDeclaration * st) const1473 void JSCompiler::Compile([[maybe_unused]] const ir::FunctionDeclaration *st) const {}
Compile(const ir::AnnotationDeclaration * st) const1474 void JSCompiler::Compile([[maybe_unused]] const ir::AnnotationDeclaration *st) const {}
Compile(const ir::AnnotationUsage * st) const1475 void JSCompiler::Compile([[maybe_unused]] const ir::AnnotationUsage *st) const {}
1476
Compile(const ir::IfStatement * st) const1477 void JSCompiler::Compile(const ir::IfStatement *st) const
1478 {
1479 PandaGen *pg = GetPandaGen();
1480 auto *consequentEnd = pg->AllocLabel();
1481 compiler::Label *statementEnd = consequentEnd;
1482
1483 compiler::Condition::Compile(pg, st->Test(), consequentEnd);
1484 st->Consequent()->Compile(pg);
1485
1486 if (st->Alternate() != nullptr) {
1487 statementEnd = pg->AllocLabel();
1488 pg->Branch(pg->Insns().back()->Node(), statementEnd);
1489
1490 pg->SetLabel(st, consequentEnd);
1491 st->Alternate()->Compile(pg);
1492 }
1493
1494 pg->SetLabel(st, statementEnd);
1495 }
1496
CompileImpl(const ir::LabelledStatement * self,PandaGen * cg)1497 void CompileImpl(const ir::LabelledStatement *self, PandaGen *cg)
1498 {
1499 compiler::LabelContext labelCtx(cg, self);
1500 self->Body()->Compile(cg);
1501 }
1502
Compile(const ir::LabelledStatement * st) const1503 void JSCompiler::Compile(const ir::LabelledStatement *st) const
1504 {
1505 PandaGen *pg = GetPandaGen();
1506 CompileImpl(st, pg);
1507 }
1508
Compile(const ir::ReturnStatement * st) const1509 void JSCompiler::Compile(const ir::ReturnStatement *st) const
1510 {
1511 PandaGen *pg = GetPandaGen();
1512 if (st->Argument() != nullptr) {
1513 st->Argument()->Compile(pg);
1514 } else {
1515 pg->LoadConst(st, compiler::Constant::JS_UNDEFINED);
1516 }
1517
1518 if (pg->CheckControlFlowChange()) {
1519 compiler::RegScope rs(pg);
1520 compiler::VReg res = pg->AllocReg();
1521
1522 pg->StoreAccumulator(st, res);
1523 pg->ControlFlowChangeBreak();
1524 pg->LoadAccumulator(st, res);
1525 }
1526
1527 if (st->Argument() != nullptr) {
1528 pg->ValidateClassDirectReturn(st);
1529 pg->DirectReturn(st);
1530 } else {
1531 pg->ImplicitReturn(st);
1532 }
1533 }
1534
CompileImpl(const ir::SwitchStatement * self,PandaGen * cg)1535 static void CompileImpl(const ir::SwitchStatement *self, PandaGen *cg)
1536 {
1537 compiler::LocalRegScope lrs(cg, self->Scope());
1538 compiler::SwitchBuilder builder(cg, self);
1539 compiler::VReg tag = cg->AllocReg();
1540
1541 builder.CompileTagOfSwitch(tag);
1542 uint32_t defaultIndex = 0;
1543
1544 for (size_t i = 0; i < self->Cases().size(); i++) {
1545 const auto *clause = self->Cases()[i];
1546
1547 if (clause->Test() == nullptr) {
1548 defaultIndex = i;
1549 continue;
1550 }
1551
1552 builder.JumpIfCase(tag, i);
1553 }
1554
1555 if (defaultIndex > 0) {
1556 builder.JumpToDefault(defaultIndex);
1557 } else {
1558 builder.Break();
1559 }
1560
1561 for (size_t i = 0; i < self->Cases().size(); i++) {
1562 builder.SetCaseTarget(i);
1563 builder.CompileCaseStatements(i);
1564 }
1565 }
1566
Compile(const ir::SwitchStatement * st) const1567 void JSCompiler::Compile(const ir::SwitchStatement *st) const
1568 {
1569 PandaGen *pg = GetPandaGen();
1570 CompileImpl(st, pg);
1571 }
1572
Compile(const ir::ThrowStatement * st) const1573 void JSCompiler::Compile(const ir::ThrowStatement *st) const
1574 {
1575 PandaGen *pg = GetPandaGen();
1576 st->Argument()->Compile(pg);
1577 pg->EmitThrow(st);
1578 }
1579
CompileTryCatch(compiler::PandaGen * pg,const ir::TryStatement * st)1580 static void CompileTryCatch(compiler::PandaGen *pg, const ir::TryStatement *st)
1581 {
1582 ES2PANDA_ASSERT(st->CatchClauses().size() == 1);
1583 ES2PANDA_ASSERT(st->CatchClauses().front() && !st->FinallyBlock());
1584
1585 compiler::TryContext tryCtx(pg, st);
1586 const auto &labelSet = tryCtx.LabelSet();
1587
1588 pg->SetLabel(st, labelSet.TryBegin());
1589 st->Block()->Compile(pg);
1590 pg->SetLabel(st, labelSet.TryEnd());
1591
1592 pg->Branch(st, labelSet.CatchEnd());
1593
1594 pg->SetLabel(st, labelSet.CatchBegin());
1595 st->CatchClauses().front()->Compile(pg);
1596 pg->SetLabel(st, labelSet.CatchEnd());
1597 }
1598
CompileFinally(compiler::PandaGen * pg,compiler::TryContext * tryCtx,const compiler::TryLabelSet & labelSet,const ir::TryStatement * st)1599 static void CompileFinally(compiler::PandaGen *pg, compiler::TryContext *tryCtx, const compiler::TryLabelSet &labelSet,
1600 const ir::TryStatement *st)
1601 {
1602 compiler::RegScope rs(pg);
1603 compiler::VReg exception = pg->AllocReg();
1604 pg->StoreConst(st, exception, compiler::Constant::JS_HOLE);
1605 pg->Branch(st, labelSet.CatchEnd());
1606
1607 pg->SetLabel(st, labelSet.CatchBegin());
1608 pg->StoreAccumulator(st, exception);
1609
1610 pg->SetLabel(st, labelSet.CatchEnd());
1611
1612 compiler::Label *label = pg->AllocLabel();
1613 pg->LoadAccumulator(st, tryCtx->FinalizerRun());
1614
1615 pg->BranchIfNotUndefined(st, label);
1616 pg->StoreAccumulator(st, tryCtx->FinalizerRun());
1617 tryCtx->EmitFinalizer();
1618 pg->SetLabel(st, label);
1619
1620 pg->LoadAccumulator(st, exception);
1621 pg->EmitRethrow(st);
1622 }
1623
CompileTryCatchFinally(compiler::PandaGen * pg,const ir::TryStatement * st)1624 static void CompileTryCatchFinally(compiler::PandaGen *pg, const ir::TryStatement *st)
1625 {
1626 ES2PANDA_ASSERT(st->CatchClauses().size() == 1);
1627 ES2PANDA_ASSERT(st->CatchClauses().front() && st->FinallyBlock());
1628
1629 compiler::TryContext tryCtx(pg, st);
1630 const auto &labelSet = tryCtx.LabelSet();
1631
1632 pg->SetLabel(st, labelSet.TryBegin());
1633 {
1634 compiler::TryContext innerTryCtx(pg, st, false);
1635 const auto &innerLabelSet = innerTryCtx.LabelSet();
1636
1637 pg->SetLabel(st, innerLabelSet.TryBegin());
1638 st->Block()->Compile(pg);
1639 pg->SetLabel(st, innerLabelSet.TryEnd());
1640
1641 pg->Branch(st, innerLabelSet.CatchEnd());
1642
1643 pg->SetLabel(st, innerLabelSet.CatchBegin());
1644 st->CatchClauses().front()->Compile(pg);
1645 pg->SetLabel(st, innerLabelSet.CatchEnd());
1646 }
1647 pg->SetLabel(st, labelSet.TryEnd());
1648
1649 CompileFinally(pg, &tryCtx, labelSet, st);
1650 }
1651
CompileTryFinally(compiler::PandaGen * pg,const ir::TryStatement * st)1652 static void CompileTryFinally(compiler::PandaGen *pg, const ir::TryStatement *st)
1653 {
1654 ES2PANDA_ASSERT(st->CatchClauses().empty() && st->FinallyBlock());
1655
1656 compiler::TryContext tryCtx(pg, st);
1657 const auto &labelSet = tryCtx.LabelSet();
1658
1659 pg->SetLabel(st, labelSet.TryBegin());
1660 {
1661 compiler::TryContext innerTryCtx(pg, st, false);
1662 const auto &innerLabelSet = innerTryCtx.LabelSet();
1663
1664 pg->SetLabel(st, innerLabelSet.TryBegin());
1665 st->Block()->Compile(pg);
1666 pg->SetLabel(st, innerLabelSet.TryEnd());
1667
1668 pg->Branch(st, innerLabelSet.CatchEnd());
1669
1670 pg->SetLabel(st, innerLabelSet.CatchBegin());
1671 pg->EmitThrow(st);
1672 pg->SetLabel(st, innerLabelSet.CatchEnd());
1673 }
1674 pg->SetLabel(st, labelSet.TryEnd());
1675
1676 CompileFinally(pg, &tryCtx, labelSet, st);
1677 }
1678
Compile(const ir::TryStatement * st) const1679 void JSCompiler::Compile(const ir::TryStatement *st) const
1680 {
1681 PandaGen *pg = GetPandaGen();
1682 if (st->finalizer_ != nullptr) {
1683 if (!st->CatchClauses().empty()) {
1684 CompileTryCatchFinally(pg, st);
1685 } else {
1686 CompileTryFinally(pg, st);
1687 }
1688 } else {
1689 CompileTryCatch(pg, st);
1690 }
1691 }
1692
Compile(const ir::VariableDeclarator * st) const1693 void JSCompiler::Compile(const ir::VariableDeclarator *st) const
1694 {
1695 PandaGen *pg = GetPandaGen();
1696 auto lref = compiler::JSLReference::Create(pg, st->Id(), true);
1697 const ir::VariableDeclaration *decl = st->Parent()->AsVariableDeclaration();
1698
1699 if (st->Init() != nullptr) {
1700 st->Init()->Compile(pg);
1701 } else {
1702 if (decl->Kind() == ir::VariableDeclaration::VariableDeclarationKind::VAR) {
1703 return;
1704 }
1705 if (decl->Kind() == ir::VariableDeclaration::VariableDeclarationKind::LET && !decl->Parent()->IsCatchClause()) {
1706 pg->LoadConst(st, compiler::Constant::JS_UNDEFINED);
1707 }
1708 }
1709
1710 lref.SetValue();
1711 }
1712
Compile(const ir::VariableDeclaration * st) const1713 void JSCompiler::Compile(const ir::VariableDeclaration *st) const
1714 {
1715 PandaGen *pg = GetPandaGen();
1716 for (const auto *it : st->Declarators()) {
1717 it->Compile(pg);
1718 }
1719 }
1720
1721 template <typename CodeGen>
CompileImpl(const ir::WhileStatement * whileStmt,CodeGen * cg)1722 void CompileImpl(const ir::WhileStatement *whileStmt, [[maybe_unused]] CodeGen *cg)
1723 {
1724 compiler::LabelTarget labelTarget(cg);
1725
1726 cg->SetLabel(whileStmt, labelTarget.ContinueTarget());
1727 compiler::Condition::Compile(cg, whileStmt->Test(), labelTarget.BreakTarget());
1728
1729 {
1730 compiler::LocalRegScope regScope(cg, whileStmt->Scope());
1731 compiler::LabelContext labelCtx(cg, labelTarget);
1732 whileStmt->Body()->Compile(cg);
1733 }
1734
1735 cg->Branch(whileStmt, labelTarget.ContinueTarget());
1736 cg->SetLabel(whileStmt, labelTarget.BreakTarget());
1737 }
1738
Compile(const ir::WhileStatement * st) const1739 void JSCompiler::Compile(const ir::WhileStatement *st) const
1740 {
1741 PandaGen *pg = GetPandaGen();
1742 CompileImpl(st, pg);
1743 }
1744 } // namespace ark::es2panda::compiler
1745