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