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#ifndef ES2PANDA_COMPILER_CORE_ETSGEN_H
17#define ES2PANDA_COMPILER_CORE_ETSGEN_H
18
19#include "ir/astNode.h"
20#include "varbinder/ETSBinder.h"
21#include "compiler/core/codeGen.h"
22#include "compiler/core/ETSfunction.h"
23#include "compiler/core/targetTypeContext.h"
24#include "checker/ETSchecker.h"
25#include "util/helpers.h"
26
27namespace ark::es2panda::compiler {
28
29class ETSGen final : public CodeGen {
30public:
31    explicit ETSGen(ArenaAllocator *allocator, RegSpiller *spiller, public_lib::Context *context,
32                    std::tuple<varbinder::FunctionScope *, ProgramElement *, AstCompiler *> toCompile) noexcept;
33
34    [[nodiscard]] const checker::ETSChecker *Checker() const noexcept;
35    [[nodiscard]] const varbinder::ETSBinder *VarBinder() const noexcept;
36    [[nodiscard]] const checker::Type *ReturnType() const noexcept;
37    [[nodiscard]] const checker::ETSObjectType *ContainingObjectType() const noexcept;
38
39    [[nodiscard]] VReg &Acc() noexcept;
40    [[nodiscard]] VReg Acc() const noexcept;
41
42    void SetAccumulatorType(const checker::Type *type);
43    [[nodiscard]] const checker::Type *GetAccumulatorType() const;
44    void CompileAndCheck(const ir::Expression *expr);
45
46    [[nodiscard]] VReg StoreException(const ir::AstNode *node);
47    void ApplyConversionAndStoreAccumulator(const ir::AstNode *node, VReg vreg, const checker::Type *targetType);
48    void StoreAccumulator(const ir::AstNode *node, VReg vreg);
49    void LoadAccumulator(const ir::AstNode *node, VReg vreg);
50    [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, VReg vd, VReg vs) override;
51    [[nodiscard]] IRNode *AllocMov(const ir::AstNode *node, OutVReg vd, VReg vs) override;
52    void MoveVreg(const ir::AstNode *node, VReg vd, VReg vs);
53
54    [[nodiscard]] checker::Type const *TypeForVar(varbinder::Variable const *var) const noexcept override;
55
56    void LoadVar(const ir::Identifier *node, varbinder::Variable const *var);
57    void LoadDynamicModuleVariable(const ir::AstNode *node, varbinder::Variable const *var);
58    void LoadDynamicNamespaceVariable(const ir::AstNode *node, varbinder::Variable const *var);
59    void StoreVar(const ir::Identifier *node, const varbinder::ConstScopeFindResult &result);
60
61    void LoadStaticProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &fullName);
62    void StoreStaticProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &fullName);
63
64    void StoreStaticOwnProperty(const ir::AstNode *node, const checker::Type *propType, const util::StringView &name);
65    [[nodiscard]] util::StringView FormClassPropReference(const checker::ETSObjectType *classType,
66                                                          const util::StringView &name);
67
68    void StoreProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
69                       const util::StringView &name);
70    void LoadProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
71                      const util::StringView &fullName);
72    void StorePropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
73                              const util::StringView &propName);
74    void LoadPropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
75                             const util::StringView &propName);
76
77    void StoreElementDynamic(const ir::AstNode *node, VReg objectReg, VReg index);
78    void LoadElementDynamic(const ir::AstNode *node, VReg objectReg);
79
80    void StoreUnionProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
81                            const util::StringView &propName);
82    void LoadUnionProperty(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
83                           const util::StringView &propName);
84
85    void LoadUndefinedDynamic(const ir::AstNode *node, Language lang);
86
87    void LoadThis(const ir::AstNode *node);
88    [[nodiscard]] VReg GetThisReg() const;
89
90    const checker::Type *LoadDefaultValue(const ir::AstNode *node, const checker::Type *type);
91    void EmitReturnVoid(const ir::AstNode *node);
92    void ReturnAcc(const ir::AstNode *node);
93
94    void BranchIfIsInstance(const ir::AstNode *node, VReg srcReg, const checker::Type *target, Label *ifTrue);
95    void IsInstance(const ir::AstNode *node, VReg srcReg, checker::Type const *target);
96    void IsInstanceDynamic(const ir::BinaryExpression *node, VReg srcReg, VReg tgtReg);
97    void EmitFailedTypeCastException(const ir::AstNode *node, VReg src, checker::Type const *target);
98
99    void BinaryLogic(const ir::AstNode *node, lexer::TokenType op, VReg lhs);
100    void BinaryArithmLogic(const ir::AstNode *node, lexer::TokenType op, VReg lhs);
101    void Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs);
102    void Unary(const ir::AstNode *node, lexer::TokenType op);
103    void Update(const ir::AstNode *node, lexer::TokenType op);
104    void UpdateBigInt(const ir::Expression *node, VReg arg, lexer::TokenType op);
105
106    bool TryLoadConstantExpression(const ir::Expression *node);
107    void Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse);
108
109    template <typename CondCompare, bool BEFORE_LOGICAL_NOT>
110    void ResolveConditionalResultFloat(const ir::AstNode *node, Label *realEndLabel)
111    {
112        auto type = GetAccumulatorType();
113        VReg tmpReg = AllocReg();
114        StoreAccumulator(node, tmpReg);
115        if (type->IsFloatType()) {
116            FloatIsNaN(node);
117        } else {
118            DoubleIsNaN(node);
119        }
120        Sa().Emit<Xori>(node, 1);
121
122        BranchIfFalse(node, realEndLabel);
123        LoadAccumulator(node, tmpReg);
124        VReg zeroReg = AllocReg();
125
126        if (type->IsFloatType()) {
127            MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::FLOAT, 0);
128            BinaryNumberComparison<Fcmpl, Jeqz>(node, zeroReg, realEndLabel);
129        } else {
130            MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::DOUBLE, 0);
131            BinaryNumberComparison<FcmplWide, Jeqz>(node, zeroReg, realEndLabel);
132        }
133    }
134
135    template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
136    void ResolveConditionalResultNumeric(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse, Label **end)
137    {
138        auto type = GetAccumulatorType();
139        ASSERT(type != nullptr);
140        auto realEndLabel = [end, ifFalse, this](bool useFalseLabel) {
141            if (useFalseLabel) {
142                return ifFalse;
143            }
144            if ((*end) == nullptr) {
145                (*end) = AllocLabel();
146            }
147            return (*end);
148        }(USE_FALSE_LABEL);
149        if (type->IsDoubleType() || type->IsFloatType()) {
150            ResolveConditionalResultFloat<CondCompare, BEFORE_LOGICAL_NOT>(node, realEndLabel);
151        }
152        if (type->IsLongType()) {
153            VReg zeroReg = AllocReg();
154            MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::LONG, 0);
155            BinaryNumberComparison<CmpWide, CondCompare>(node, zeroReg, realEndLabel);
156        }
157        if constexpr (BEFORE_LOGICAL_NOT) {
158            Label *zeroPrimitive = AllocLabel();
159            BranchIfFalse(node, zeroPrimitive);
160            ToBinaryResult(node, zeroPrimitive);
161        }
162    }
163
164    template <typename CondCompare, bool BEFORE_LOGICAL_NOT>
165    void ResolveConditionalResultReference(const ir::AstNode *node)
166    {
167        auto const testString = [this, node]() {
168            LoadStringLength(node);
169            if constexpr (BEFORE_LOGICAL_NOT) {
170                Label *zeroLenth = AllocLabel();
171                BranchIfFalse(node, zeroLenth);
172                ToBinaryResult(node, zeroLenth);
173            }
174        };
175
176        auto type = GetAccumulatorType();
177        if (!type->PossiblyETSString()) {
178            Sa().Emit<Ldai>(node, 1);
179            return;
180        }
181        if (type->IsETSStringType()) {  // should also be valid for string|null|undefined
182            testString();
183            return;
184        }
185
186        Label *isString = AllocLabel();
187        Label *end = AllocLabel();
188        compiler::VReg objReg = AllocReg();
189        StoreAccumulator(node, objReg);
190
191        Sa().Emit<Isinstance>(node, Checker()->GlobalBuiltinETSStringType()->AssemblerName());
192        BranchIfTrue(node, isString);
193        Sa().Emit<Ldai>(node, 1);
194        Branch(node, end);
195        SetLabel(node, isString);
196        LoadAccumulator(node, objReg);
197        InternalCheckCast(node, Checker()->GlobalBuiltinETSStringType());  // help verifier
198        testString();
199        SetLabel(node, end);
200    }
201
202    template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
203    void ResolveConditionalResult(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse)
204    {
205        auto type = GetAccumulatorType();
206        if (type->IsETSBooleanType()) {
207            return;
208        }
209        Label *ifNullish {nullptr};
210        Label *end {nullptr};
211        if (type->PossiblyETSNullish()) {
212            if constexpr (USE_FALSE_LABEL) {
213                BranchIfNullish(node, ifFalse);
214            } else {
215                ifNullish = AllocLabel();
216                end = AllocLabel();
217                BranchIfNullish(node, ifNullish);
218            }
219        }
220        if (type->DefinitelyETSNullish()) {
221            // skip
222        } else if (type->IsETSReferenceType()) {
223            ResolveConditionalResultReference<CondCompare, BEFORE_LOGICAL_NOT>(node);
224        } else {
225            ResolveConditionalResultNumeric<CondCompare, BEFORE_LOGICAL_NOT, USE_FALSE_LABEL>(node, ifFalse, &end);
226        }
227        if (ifNullish != nullptr) {
228            Branch(node, end);
229            SetLabel(node, ifNullish);
230            Sa().Emit<Ldai>(node, 0);
231        }
232        if (end != nullptr) {
233            SetLabel(node, end);
234        }
235    }
236
237    template <bool BEFORE_LOGICAL_NOT = false, bool FALSE_LABEL_EXISTED = true>
238    void ResolveConditionalResultIfFalse(const ir::AstNode *node, Label *ifFalse = nullptr)
239    {
240        ResolveConditionalResult<Jeqz, BEFORE_LOGICAL_NOT, FALSE_LABEL_EXISTED>(node, ifFalse);
241    }
242
243    template <bool BEFORE_LOGICAL_NOT = false, bool FALSE_LABEL_EXISTED = true>
244    void ResolveConditionalResultIfTrue(const ir::AstNode *node, Label *ifFalse = nullptr)
245    {
246        ResolveConditionalResult<Jnez, BEFORE_LOGICAL_NOT, FALSE_LABEL_EXISTED>(node, ifFalse);
247    }
248
249    void BranchIfFalse(const ir::AstNode *node, Label *ifFalse)
250    {
251        Sa().Emit<Jeqz>(node, ifFalse);
252    }
253
254    void BranchIfTrue(const ir::AstNode *node, Label *ifTrue)
255    {
256        Sa().Emit<Jnez>(node, ifTrue);
257    }
258
259    void BranchIfNull(const ir::AstNode *node, Label *ifNull)
260    {
261        Sa().Emit<JeqzObj>(node, ifNull);
262    }
263
264    void BranchIfUndefined([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] Label *ifUndefined)
265    {
266#ifdef PANDA_WITH_ETS
267        Sa().Emit<EtsIsundefined>(node);
268        Sa().Emit<Jnez>(node, ifUndefined);
269#else
270        UNREACHABLE();
271#endif  // PANDA_WITH_ETS
272    }
273
274    void BranchIfNotUndefined([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] Label *ifUndefined)
275    {
276#ifdef PANDA_WITH_ETS
277        Sa().Emit<EtsIsundefined>(node);
278        Sa().Emit<Jeqz>(node, ifUndefined);
279#else
280        UNREACHABLE();
281#endif  // PANDA_WITH_ETS
282    }
283
284    void BranchIfNotNull(const ir::AstNode *node, Label *ifNotNull)
285    {
286        Sa().Emit<JnezObj>(node, ifNotNull);
287    }
288
289    void BranchIfNullish(const ir::AstNode *node, Label *ifNullish);
290    void BranchIfNotNullish(const ir::AstNode *node, Label *ifNotNullish);
291    void AssumeNonNullish(const ir::AstNode *node, checker::Type const *targetType);
292
293    void JumpTo(const ir::AstNode *node, Label *labelTo)
294    {
295        Sa().Emit<Jmp>(node, labelTo);
296    }
297
298    void EmitThrow(const ir::AstNode *node, VReg err)
299    {
300        Ra().Emit<Throw>(node, err);
301    }
302
303    void EmitNullishException(const ir::AstNode *node);
304    void ThrowException(const ir::Expression *expr);
305    bool ExtendWithFinalizer(ir::AstNode const *node, const ir::AstNode *originalNode, Label *prevFinnaly = nullptr);
306
307    void Negate(const ir::AstNode *node);
308    void LogicalNot(const ir::AstNode *node);
309
310    void LoadAccumulatorByte(const ir::AstNode *node, int8_t number)
311    {
312        LoadAccumulatorNumber<int8_t>(node, number, checker::TypeFlag::BYTE);
313    }
314
315    void LoadAccumulatorShort(const ir::AstNode *node, int16_t number)
316    {
317        LoadAccumulatorNumber<int16_t>(node, number, checker::TypeFlag::SHORT);
318    }
319
320    void LoadAccumulatorInt(const ir::AstNode *node, int32_t number)
321    {
322        LoadAccumulatorNumber<int32_t>(node, number, checker::TypeFlag::INT);
323    }
324
325    void LoadAccumulatorWideInt(const ir::AstNode *node, int64_t number)
326    {
327        LoadAccumulatorNumber<int64_t>(node, number, checker::TypeFlag::LONG);
328    }
329
330    void LoadAccumulatorFloat(const ir::AstNode *node, float number)
331    {
332        LoadAccumulatorNumber<float>(node, number, checker::TypeFlag::FLOAT);
333    }
334
335    void LoadAccumulatorDouble(const ir::AstNode *node, double number)
336    {
337        LoadAccumulatorNumber<double>(node, number, checker::TypeFlag::DOUBLE);
338    }
339
340    void LoadAccumulatorBoolean(const ir::AstNode *node, bool value)
341    {
342        Sa().Emit<Ldai>(node, value ? 1 : 0);
343        SetAccumulatorType(Checker()->GlobalETSBooleanType());
344        ApplyConversion(node, Checker()->GlobalETSBooleanType());
345    }
346
347    void LoadAccumulatorString(const ir::AstNode *node, util::StringView str)
348    {
349        Sa().Emit<LdaStr>(node, str);
350        SetAccumulatorType(Checker()->GlobalETSStringLiteralType());
351    }
352
353    void LoadAccumulatorBigInt(const ir::AstNode *node, util::StringView str)
354    {
355        Sa().Emit<LdaStr>(node, str);
356        SetAccumulatorType(Checker()->GlobalETSBigIntType());
357    }
358
359    void LoadAccumulatorNull(const ir::AstNode *node, const checker::Type *type)
360    {
361        Sa().Emit<LdaNull>(node);
362        SetAccumulatorType(type);
363    }
364
365    void LoadAccumulatorUndefined([[maybe_unused]] const ir::AstNode *node)
366    {
367#ifdef PANDA_WITH_ETS
368        Sa().Emit<EtsLdundefined>(node);
369        SetAccumulatorType(Checker()->GlobalETSUndefinedType());
370#else
371        UNREACHABLE();
372#endif  // PANDA_WITH_ETS
373    }
374
375    void LoadAccumulatorChar(const ir::AstNode *node, char16_t value)
376    {
377        Sa().Emit<Ldai>(node, value);
378        SetAccumulatorType(Checker()->GlobalCharType());
379        ApplyConversion(node, Checker()->GlobalCharType());
380    }
381
382    void LoadAccumulatorDynamicModule(const ir::AstNode *node, const ir::ETSImportDeclaration *import);
383
384    void ApplyBoxingConversion(const ir::AstNode *node);
385    void ApplyUnboxingConversion(const ir::AstNode *node);
386    void ApplyConversion(const ir::AstNode *node)
387    {
388        if (targetType_ != nullptr) {
389            ApplyConversion(node, targetType_);
390        }
391    }
392    void ApplyConversionCast(const ir::AstNode *node, const checker::Type *targetType);
393    void ApplyConversion(const ir::AstNode *node, const checker::Type *targetType);
394    void ApplyCast(const ir::AstNode *node, const checker::Type *targetType);
395    void ApplyCastToBoxingFlags(const ir::AstNode *node, const ir::BoxingUnboxingFlags targetType);
396    void EmitUnboxingConversion(const ir::AstNode *node);
397    checker::Type *EmitBoxedType(ir::BoxingUnboxingFlags boxingFlag, const ir::AstNode *node);
398    void EmitBoxingConversion(const ir::AstNode *node);
399    void SwapBinaryOpArgs(const ir::AstNode *node, VReg lhs);
400    VReg MoveAccToReg(const ir::AstNode *node);
401
402    void LoadArrayLength(const ir::AstNode *node, VReg arrayReg);
403    void LoadArrayElement(const ir::AstNode *node, VReg objectReg);
404    void StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index, const checker::Type *elementType);
405
406    template <typename T>
407    void MoveImmediateToRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value)
408    {
409        switch (valueType) {
410            case checker::TypeFlag::ETS_BOOLEAN:
411                [[fallthrough]];
412            case checker::TypeFlag::BYTE: {
413                Ra().Emit<Movi>(node, reg, static_cast<checker::ByteType::UType>(value));
414                SetVRegType(reg, Checker()->GlobalByteType());
415                break;
416            }
417            case checker::TypeFlag::CHAR: {
418                Ra().Emit<Movi>(node, reg, static_cast<checker::CharType::UType>(value));
419                SetVRegType(reg, Checker()->GlobalCharType());
420                break;
421            }
422            case checker::TypeFlag::SHORT: {
423                Ra().Emit<Movi>(node, reg, static_cast<checker::ShortType::UType>(value));
424                SetVRegType(reg, Checker()->GlobalShortType());
425                break;
426            }
427            case checker::TypeFlag::INT: {
428                Ra().Emit<Movi>(node, reg, static_cast<checker::IntType::UType>(value));
429                SetVRegType(reg, Checker()->GlobalIntType());
430                break;
431            }
432            case checker::TypeFlag::LONG: {
433                Ra().Emit<MoviWide>(node, reg, static_cast<checker::LongType::UType>(value));
434                SetVRegType(reg, Checker()->GlobalLongType());
435                break;
436            }
437            case checker::TypeFlag::FLOAT: {
438                Ra().Emit<Fmovi>(node, reg, static_cast<checker::FloatType::UType>(value));
439                SetVRegType(reg, Checker()->GlobalFloatType());
440                break;
441            }
442            case checker::TypeFlag::DOUBLE: {
443                Ra().Emit<FmoviWide>(node, reg, static_cast<checker::DoubleType::UType>(value));
444                SetVRegType(reg, Checker()->GlobalDoubleType());
445                break;
446            }
447            default: {
448                UNREACHABLE();
449            }
450        }
451    }
452
453    template <typename T>
454    void IncrementImmediateRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value)
455    {
456        switch (valueType) {
457            // NOTE: operand of increment instruction (INCI) is defined in spec as 32-bit integer,
458            // but its current implementation actually can work with 64-bit integers as well.
459            case checker::TypeFlag::INT: {
460                Ra().Emit<Inci>(node, reg, static_cast<checker::IntType::UType>(value));
461                break;
462            }
463            case checker::TypeFlag::CHAR: {
464                Ra().Emit<Inci>(node, reg, static_cast<checker::CharType::UType>(value));
465                break;
466            }
467            case checker::TypeFlag::SHORT: {
468                Ra().Emit<Inci>(node, reg, static_cast<checker::ShortType::UType>(value));
469                break;
470            }
471            case checker::TypeFlag::ETS_BOOLEAN:
472                [[fallthrough]];
473            case checker::TypeFlag::BYTE: {
474                Ra().Emit<Inci>(node, reg, static_cast<checker::ByteType::UType>(value));
475                break;
476            }
477            default: {
478                UNREACHABLE();
479            }
480        }
481    }
482
483    template <typename IntCompare>
484    void JumpCompareRegister(const ir::AstNode *node, VReg lhs, Label *ifFalse)
485    {
486        Ra().Emit<IntCompare>(node, lhs, ifFalse);
487    }
488
489    void LoadStringLength(const ir::AstNode *node);
490    void LoadStringChar(const ir::AstNode *node, VReg stringObj, VReg charIndex);
491
492    void FloatIsNaN(const ir::AstNode *node);
493    void DoubleIsNaN(const ir::AstNode *node);
494
495    void CompileStatements(const ArenaVector<ir::Statement *> &statements);
496
497    // Cast
498    void CastToBoolean(const ir::AstNode *node);
499    void CastToByte(const ir::AstNode *node);
500    void CastToChar(const ir::AstNode *node);
501    void CastToShort(const ir::AstNode *node);
502    void CastToDouble(const ir::AstNode *node);
503    void CastToFloat(const ir::AstNode *node);
504    void CastToLong(const ir::AstNode *node);
505    void CastToInt(const ir::AstNode *node);
506    void CastToString(const ir::AstNode *node);
507    void CastToDynamic(const ir::AstNode *node, const checker::ETSDynamicType *type);
508    void CastDynamicTo(const ir::AstNode *node, enum checker::TypeFlag typeFlag);
509    void CastToReftype(const ir::AstNode *node, const checker::Type *targetType, bool unchecked);
510    void CastDynamicToObject(const ir::AstNode *node, const checker::Type *targetType);
511    void CastUnionToFunctionType(const ir::AstNode *node, const checker::ETSUnionType *unionType,
512                                 checker::Signature *signatureTarget);
513
514    void InternalIsInstance(const ir::AstNode *node, const checker::Type *target);
515    void InternalCheckCast(const ir::AstNode *node, const checker::Type *target);
516    void CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target);
517    void GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target);
518
519    // Call, Construct
520    void NewArray(const ir::AstNode *node, VReg arr, VReg dim, const checker::Type *arrType);
521    void NewObject(const ir::AstNode *node, util::StringView name, VReg athis);
522    void BuildString(const ir::Expression *node);
523    void CallBigIntUnaryOperator(const ir::Expression *node, VReg arg, util::StringView signature);
524    void CallBigIntBinaryOperator(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature);
525    void CallBigIntBinaryComparison(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature);
526    void BuildTemplateString(const ir::TemplateLiteral *node);
527    void InitObject(const ir::AstNode *node, checker::Signature const *signature,
528                    const ArenaVector<ir::Expression *> &arguments)
529    {
530        CallImpl<InitobjShort, Initobj, InitobjRange>(node, signature, arguments);
531    }
532
533    bool IsDevirtualizedSignature(const checker::Signature *signature)
534    {
535        ASSERT(!signature->HasSignatureFlag(checker::SignatureFlags::STATIC));
536        return signature->HasSignatureFlag(checker::SignatureFlags::FINAL | checker::SignatureFlags::PRIVATE |
537                                           checker::SignatureFlags::CONSTRUCTOR);
538    }
539
540    void CallExact(const ir::AstNode *node, checker::Signature *signature,
541                   const ArenaVector<ir::Expression *> &arguments)
542    {
543        CallImpl<CallShort, Call, CallRange>(node, signature, arguments);
544    }
545
546    void CallExact(const ir::AstNode *const node, const checker::Signature *signature, const VReg arg0,
547                   const ArenaVector<ir::Expression *> &arguments)
548    {
549        CallArgStart<CallShort, Call, CallRange>(node, signature, arg0, arguments);
550    }
551
552    void CallExact(const ir::AstNode *const node, const util::StringView name)
553    {
554        Ra().Emit<CallShort, 0>(node, name, dummyReg_, dummyReg_);
555    }
556
557    void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0)
558    {
559        Ra().Emit<CallShort, 1>(node, name, arg0, dummyReg_);
560    }
561
562    void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1)
563    {
564        Ra().Emit<CallShort>(node, name, arg0, arg1);
565    }
566
567    void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1,
568                   const VReg arg2)
569    {
570        Ra().Emit<Call, 3U>(node, name, arg0, arg1, arg2, dummyReg_);
571    }
572
573    void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis,
574                     const ArenaVector<ir::Expression *> &arguments)
575    {
576        ASSERT(!signature->HasSignatureFlag(checker::SignatureFlags::STATIC));
577        ASSERT(!signature->Owner()->GetDeclNode()->IsFinal() || signature->IsFinal());
578        if (IsDevirtualizedSignature(signature)) {
579            CallArgStart<CallShort, Call, CallRange>(node, signature, athis, arguments);
580        } else {
581            CallArgStart<CallVirtShort, CallVirt, CallVirtRange>(node, signature, athis, arguments);
582        }
583    }
584
585    void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis)
586    {
587        if (IsDevirtualizedSignature(signature)) {
588            CallExact(node, signature->InternalName(), athis);
589        } else {
590            CallVirtual(node, signature->InternalName(), athis);
591        }
592    }
593
594    void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis,
595                     const VReg arg0)
596    {
597        if (IsDevirtualizedSignature(signature)) {
598            CallExact(node, signature->InternalName(), athis, arg0);
599        } else {
600            CallVirtual(node, signature->InternalName(), athis, arg0);
601        }
602    }
603
604    void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis)
605    {
606        Ra().Emit<CallVirtShort, 1>(node, name, athis, dummyReg_);
607    }
608
609    void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis, const VReg arg0)
610    {
611        Ra().Emit<CallVirtShort>(node, name, athis, arg0);
612    }
613
614    struct CallDynamicData {
615        const ir::AstNode *node = nullptr;
616        VReg obj;
617        VReg param2;
618    };
619
620    void CallDynamic(CallDynamicData data, checker::Signature *signature,
621                     const ArenaVector<ir::Expression *> &arguments)
622    {
623        CallDynamicImpl<CallShort, Call, CallRange>(data, signature, arguments);
624    }
625
626    void CallDynamic(CallDynamicData data, VReg param3, checker::Signature *signature,
627                     const ArenaVector<ir::Expression *> &arguments)
628    {
629        CallDynamicImpl<CallShort, Call, CallRange>(data, param3, signature, arguments);
630    }
631
632#ifdef PANDA_WITH_ETS
633    // The functions below use ETS specific instructions.
634    // Compilation of es2panda fails if ETS plugin is disabled
635    void LaunchExact(const ir::AstNode *node, checker::Signature *signature,
636                     const ArenaVector<ir::Expression *> &arguments)
637    {
638        CallImpl<EtsLaunchShort, EtsLaunch, EtsLaunchRange>(node, signature, arguments);
639    }
640
641    void LaunchVirtual(const ir::AstNode *const node, checker::Signature *const signature, const VReg athis,
642                       const ArenaVector<ir::Expression *> &arguments)
643    {
644        if (IsDevirtualizedSignature(signature)) {
645            CallArgStart<EtsLaunchShort, EtsLaunch, EtsLaunchRange>(node, signature, athis, arguments);
646        } else {
647            CallArgStart<EtsLaunchVirtShort, EtsLaunchVirt, EtsLaunchVirtRange>(node, signature, athis, arguments);
648        }
649    }
650#endif  // PANDA_WITH_ETS
651
652    void CreateBigIntObject(const ir::AstNode *node, VReg arg0,
653                            std::string_view signature = Signatures::BUILTIN_BIGINT_CTOR);
654
655    void GetType(const ir::AstNode *node, bool isEtsPrimitive)
656    {
657        if (isEtsPrimitive) {
658            // NOTE: SzD. LoadStaticProperty if ETS stdlib has static TYPE constants otherwise fallback to LdaType
659        } else {
660            auto classRef = GetAccumulatorType()->AsETSObjectType()->AssemblerName();
661            Sa().Emit<LdaType>(node, classRef);
662        }
663    }
664
665    ~ETSGen() override = default;
666    NO_COPY_SEMANTIC(ETSGen);
667    NO_MOVE_SEMANTIC(ETSGen);
668
669    void EmitUnboxEnum(const ir::AstNode *node, const checker::Type *enumType);
670
671private:
672    const VReg dummyReg_ = VReg::RegStart();
673
674    void EmitUnboxedCall(const ir::AstNode *node, std::string_view signatureFlag, const checker::Type *targetType,
675                         const checker::Type *boxedType);
676
677    void LoadConstantObject(const ir::Expression *node, const checker::Type *type);
678    void StringBuilderAppend(const ir::AstNode *node, VReg builder);
679    void AppendString(const ir::Expression *binExpr, VReg builder);
680    void StringBuilder(const ir::Expression *left, const ir::Expression *right, VReg builder);
681    util::StringView FormClassPropReference(varbinder::Variable const *var);
682    void UnaryMinus(const ir::AstNode *node);
683    void UnaryTilde(const ir::AstNode *node);
684    void UnaryDollarDollar(const ir::AstNode *node);
685
686    util::StringView ToAssemblerType(const es2panda::checker::Type *type) const;
687    void TestIsInstanceConstant(const ir::AstNode *node, Label *ifTrue, VReg srcReg, checker::Type const *target);
688    void TestIsInstanceConstituent(const ir::AstNode *node, std::tuple<Label *, Label *> label, VReg srcReg,
689                                   checker::Type const *target, bool acceptUndefined);
690    void CheckedReferenceNarrowingObject(const ir::AstNode *node, const checker::Type *target);
691
692    void HandleLooseNullishEquality(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse, Label *ifTrue);
693
694    void EmitIsUndefined([[maybe_unused]] const ir::AstNode *node)
695    {
696#ifdef PANDA_WITH_ETS
697        Sa().Emit<EtsIsundefined>(node);
698#else
699        UNREACHABLE();
700#endif  // PANDA_WITH_ETS
701    }
702
703    void EmitEtsEquals([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const VReg lhs,
704                       [[maybe_unused]] const VReg rhs)
705    {
706#ifdef PANDA_WITH_ETS
707        Ra().Emit<EtsEquals>(node, lhs, rhs);
708#else
709        UNREACHABLE();
710#endif  // PANDA_WITH_ETS
711    }
712
713    template <typename T>
714    void StoreValueIntoArray(const ir::AstNode *const node, const VReg arr, const VReg index)
715    {
716        Ra().Emit<T>(node, arr, index);
717    }
718
719    template <typename LongOp, typename IntOp, typename DoubleOp, typename FloatOp>
720    void UpdateOperator(const ir::AstNode *node)
721    {
722        switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
723            case checker::TypeFlag::LONG: {
724                RegScope scope(this);
725                VReg reg = AllocReg();
726                Ra().Emit<MoviWide>(node, reg, 1LL);
727                Ra().Emit<LongOp>(node, reg);
728                break;
729            }
730            case checker::TypeFlag::INT: {
731                Sa().Emit<IntOp>(node, 1);
732                break;
733            }
734            case checker::TypeFlag::CHAR: {
735                Sa().Emit<IntOp>(node, 1);
736                Sa().Emit<I32tou16>(node);
737                break;
738            }
739            case checker::TypeFlag::SHORT: {
740                Sa().Emit<IntOp>(node, 1);
741                Sa().Emit<I32toi16>(node);
742                break;
743            }
744            case checker::TypeFlag::BYTE: {
745                Sa().Emit<IntOp>(node, 1);
746                Sa().Emit<I32toi8>(node);
747                break;
748            }
749            case checker::TypeFlag::DOUBLE: {
750                RegScope scope(this);
751                VReg reg = AllocReg();
752                Ra().Emit<FmoviWide>(node, reg, 1.0);
753                Ra().Emit<DoubleOp>(node, reg);
754                break;
755            }
756            case checker::TypeFlag::FLOAT: {
757                RegScope scope(this);
758                VReg reg = AllocReg();
759                Ra().Emit<Fmovi>(node, reg, 1.0F);
760                Ra().Emit<FloatOp>(node, reg);
761                break;
762            }
763            default: {
764                UNREACHABLE();
765            }
766        }
767    }
768
769    template <typename Br>
770    void InverseCondition(const ir::AstNode *node, Br const &br, Label *target, bool inverse = true)
771    {
772        if (!inverse) {
773            br(target);
774            return;
775        }
776        Label *loc = AllocLabel();
777        br(loc);
778        JumpTo(node, target);
779        SetLabel(node, loc);
780    }
781
782    void RefEqualityLoose(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
783    void RefEqualityLooseDynamic(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
784
785    template <typename Compare, typename Cond>
786    void BinaryNumberComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
787    {
788        Ra().Emit<Compare>(node, lhs);
789        Sa().Emit<Cond>(node, ifFalse);
790    }
791
792    template <typename DynCompare>
793    void RefEqualityStrictDynamic(const ir::AstNode *node, VReg lhs, Label *ifFalse)
794    {
795        ASSERT(GetAccumulatorType()->IsETSDynamicType() && GetVRegType(lhs)->IsETSDynamicType());
796        RegScope scope(this);
797        Ra().Emit<CallShort, 2U>(node, Signatures::BUILTIN_JSRUNTIME_STRICT_EQUAL, lhs, MoveAccToReg(node));
798        Ra().Emit<DynCompare>(node, ifFalse);
799    }
800
801    template <typename ObjCompare, typename IntCompare, typename CondCompare, typename DynCompare>
802    void BinaryEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse)
803    {
804        BinaryEqualityCondition<ObjCompare, IntCompare, CondCompare>(node, lhs, ifFalse);
805        ToBinaryResult(node, ifFalse);
806        SetAccumulatorType(Checker()->GlobalETSBooleanType());
807    }
808
809    template <typename ObjCompare, typename IntCompare, typename CondCompare>
810    void BinaryEqualityCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
811    {
812        if (targetType_->IsETSReferenceType()) {
813            RegScope rs(this);
814            VReg arg0 = AllocReg();
815            StoreAccumulator(node, arg0);
816            InverseCondition(
817                node, [this, node, lhs, arg0](Label *tgt) { RefEqualityLoose(node, lhs, arg0, tgt); }, ifFalse,
818                std::is_same_v<CondCompare, Jeqz>);
819            SetAccumulatorType(Checker()->GlobalETSBooleanType());
820            return;
821        }
822
823        auto typeKind = checker::ETSChecker::TypeKind(targetType_);
824
825        switch (typeKind) {
826            case checker::TypeFlag::DOUBLE: {
827                BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
828                break;
829            }
830            case checker::TypeFlag::FLOAT: {
831                BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
832                break;
833            }
834            case checker::TypeFlag::LONG: {
835                BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
836                break;
837            }
838            case checker::TypeFlag::ETS_INT_ENUM:
839            case checker::TypeFlag::ETS_STRING_ENUM:
840            case checker::TypeFlag::ETS_BOOLEAN:
841            case checker::TypeFlag::BYTE:
842            case checker::TypeFlag::CHAR:
843            case checker::TypeFlag::SHORT:
844            case checker::TypeFlag::INT: {
845                Ra().Emit<IntCompare>(node, lhs, ifFalse);
846                break;
847            }
848            default: {
849                UNREACHABLE();
850            }
851        }
852
853        SetAccumulatorType(Checker()->GlobalETSBooleanType());
854    }
855
856    template <typename ObjCompare, typename DynCompare>
857    void RefEqualityStrict(const ir::AstNode *node, VReg lhs, Label *ifFalse)
858    {
859        if (GetAccumulatorType()->IsETSDynamicType() || GetVRegType(lhs)->IsETSDynamicType()) {
860            RefEqualityStrictDynamic<DynCompare>(node, lhs, ifFalse);
861        } else {
862            Ra().Emit<ObjCompare>(node, lhs, ifFalse);
863        }
864
865        ToBinaryResult(node, ifFalse);
866        SetAccumulatorType(Checker()->GlobalETSBooleanType());
867    }
868
869    template <typename IntCompare, typename CondCompare>
870    void BinaryRelation(const ir::AstNode *node, VReg lhs, Label *ifFalse)
871    {
872        BinaryRelationCondition<IntCompare, CondCompare>(node, lhs, ifFalse);
873        ToBinaryResult(node, ifFalse);
874        SetAccumulatorType(Checker()->GlobalETSBooleanType());
875    }
876
877    template <typename IntCompare, typename CondCompare>
878    void BinaryRelationCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
879    {
880        auto typeKind = checker::ETSChecker::TypeKind(targetType_);
881
882        switch (typeKind) {
883            case checker::TypeFlag::DOUBLE: {
884                BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
885                break;
886            }
887            case checker::TypeFlag::FLOAT: {
888                BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
889                break;
890            }
891            case checker::TypeFlag::LONG: {
892                BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
893                break;
894            }
895            case checker::TypeFlag::ETS_BOOLEAN:
896            case checker::TypeFlag::BYTE:
897            case checker::TypeFlag::SHORT:
898            case checker::TypeFlag::CHAR:
899            case checker::TypeFlag::INT: {
900                Ra().Emit<IntCompare>(node, lhs, ifFalse);
901                break;
902            }
903            default: {
904                UNREACHABLE();
905            }
906        }
907
908        SetAccumulatorType(Checker()->GlobalETSBooleanType());
909    }
910
911    template <typename CompareGreater, typename CompareLess, typename CondCompare>
912    void BinaryFloatingPointComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
913    {
914        if constexpr (std::is_same_v<CondCompare, Jgez> || std::is_same_v<CondCompare, Jgtz>) {
915            BinaryNumberComparison<CompareGreater, CondCompare>(node, lhs, ifFalse);
916        } else {
917            BinaryNumberComparison<CompareLess, CondCompare>(node, lhs, ifFalse);
918        }
919    }
920
921    template <typename IntOp, typename LongOp, typename FloatOp, typename DoubleOp>
922    void BinaryArithmetic(const ir::AstNode *node, VReg lhs)
923    {
924        auto typeKind = checker::ETSChecker::TypeKind(targetType_);
925
926        switch (typeKind) {
927            case checker::TypeFlag::DOUBLE: {
928                Ra().Emit<DoubleOp>(node, lhs);
929                SetAccumulatorType(Checker()->GlobalDoubleType());
930                break;
931            }
932            case checker::TypeFlag::FLOAT: {
933                Ra().Emit<FloatOp>(node, lhs);
934                SetAccumulatorType(Checker()->GlobalFloatType());
935                break;
936            }
937            default: {
938                BinaryBitwiseArithmetic<IntOp, LongOp>(node, lhs);
939            }
940        }
941    }
942
943    template <typename IntOp, typename LongOp>
944    void BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs)
945    {
946        auto typeKind = checker::ETSChecker::TypeKind(targetType_);
947
948        switch (typeKind) {
949            case checker::TypeFlag::LONG: {
950                Ra().Emit<LongOp>(node, lhs);
951                SetAccumulatorType(Checker()->GlobalLongType());
952                break;
953            }
954            case checker::TypeFlag::BYTE:
955            case checker::TypeFlag::SHORT:
956            case checker::TypeFlag::INT: {
957                Ra().Emit<IntOp>(node, lhs);
958                SetAccumulatorType(Checker()->GlobalIntType());
959                break;
960            }
961            case checker::TypeFlag::ETS_BOOLEAN: {
962                Ra().Emit<IntOp>(node, lhs);
963                SetAccumulatorType(Checker()->GlobalETSBooleanType());
964                break;
965            }
966            case checker::TypeFlag::CHAR: {
967                Ra().Emit<IntOp>(node, lhs);
968                SetAccumulatorType(Checker()->GlobalCharType());
969                break;
970            }
971            default: {
972                UNREACHABLE();
973            }
974        }
975    }
976// NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty)
977#define COMPILE_ARG(idx)                                                                                       \
978    ASSERT((idx) < arguments.size());                                                                          \
979    ASSERT((idx) < signature->Params().size() || signature->RestVar() != nullptr);                             \
980    auto *param##idx = (idx) < signature->Params().size() ? signature->Params()[(idx)] : signature->RestVar(); \
981    auto *paramType##idx = param##idx->TsType();                                                               \
982    auto ttctx##idx = TargetTypeContext(this, paramType##idx);                                                 \
983    arguments[idx]->Compile(this);                                                                             \
984    VReg arg##idx = AllocReg();                                                                                \
985    ApplyConversion(arguments[idx], nullptr);                                                                  \
986    ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
987
988    template <typename Short, typename General, typename Range>
989    void CallArgStart(const ir::AstNode *const node, const checker::Signature *signature, const VReg argStart,
990                      const ArenaVector<ir::Expression *> &arguments)
991    {
992        RegScope rs(this);
993        const auto name = signature->InternalName();
994
995        switch (arguments.size()) {
996            case 0U: {
997                Ra().Emit<Short, 1>(node, name, argStart, dummyReg_);
998                break;
999            }
1000            case 1U: {
1001                COMPILE_ARG(0);
1002                Ra().Emit<Short>(node, name, argStart, arg0);
1003                break;
1004            }
1005            case 2U: {
1006                COMPILE_ARG(0);
1007                COMPILE_ARG(1);
1008                Ra().Emit<General, 3U>(node, name, argStart, arg0, arg1, dummyReg_);
1009                break;
1010            }
1011            case 3U: {
1012                COMPILE_ARG(0);
1013                COMPILE_ARG(1);
1014                COMPILE_ARG(2);
1015                Ra().Emit<General>(node, name, argStart, arg0, arg1, arg2);
1016                break;
1017            }
1018            default: {
1019                for (size_t idx = 0; idx < arguments.size(); idx++) {
1020                    COMPILE_ARG(idx);
1021                }
1022
1023                Rra().Emit<Range>(node, argStart, arguments.size() + 1, name, argStart);
1024                break;
1025            }
1026        }
1027    }
1028
1029    template <typename Short, typename General, typename Range>
1030    void CallImpl(const ir::AstNode *node, checker::Signature const *signature,
1031                  const ArenaVector<ir::Expression *> &arguments)
1032    {
1033        ASSERT(signature != nullptr);
1034        RegScope rs(this);
1035
1036        switch (arguments.size()) {
1037            case 0U: {
1038                Ra().Emit<Short, 0U>(node, signature->InternalName(), dummyReg_, dummyReg_);
1039                break;
1040            }
1041            case 1U: {
1042                COMPILE_ARG(0);
1043                Ra().Emit<Short, 1U>(node, signature->InternalName(), arg0, dummyReg_);
1044                break;
1045            }
1046            case 2U: {
1047                COMPILE_ARG(0);
1048                COMPILE_ARG(1);
1049                Ra().Emit<Short, 2U>(node, signature->InternalName(), arg0, arg1);
1050                break;
1051            }
1052            case 3U: {
1053                COMPILE_ARG(0);
1054                COMPILE_ARG(1);
1055                COMPILE_ARG(2);
1056                Ra().Emit<General, 3U>(node, signature->InternalName(), arg0, arg1, arg2, dummyReg_);
1057                break;
1058            }
1059            case 4U: {
1060                COMPILE_ARG(0);
1061                COMPILE_ARG(1);
1062                COMPILE_ARG(2);
1063                COMPILE_ARG(3);
1064                Ra().Emit<General, 4U>(node, signature->InternalName(), arg0, arg1, arg2, arg3);
1065                break;
1066            }
1067            default: {
1068                VReg argStart = NextReg();
1069
1070                for (size_t idx = 0; idx < arguments.size(); idx++) {
1071                    COMPILE_ARG(idx);
1072                }
1073
1074                Rra().Emit<Range>(node, argStart, arguments.size(), signature->InternalName(), argStart);
1075                break;
1076            }
1077        }
1078    }
1079#undef COMPILE_ARG
1080
1081#define COMPILE_ARG(idx, shift)                                                              \
1082    ASSERT((idx) < arguments.size());                                                        \
1083    ASSERT((idx) + (shift) < signature->Params().size() || signature->RestVar() != nullptr); \
1084    auto *paramType##idx = (idx) + (shift) < signature->Params().size()                      \
1085                               ? signature->Params()[(idx) + (shift)]->TsType()              \
1086                               : signature->RestVar()->TsType();                             \
1087    auto ttctx##idx = TargetTypeContext(this, paramType##idx);                               \
1088    VReg arg##idx = AllocReg();                                                              \
1089    arguments[idx]->Compile(this);                                                           \
1090    ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
1091
1092    template <typename Short, typename General, typename Range>
1093    void CallDynamicImpl(CallDynamicData data, checker::Signature *signature,
1094                         const ArenaVector<ir::Expression *> &arguments)
1095    {
1096        RegScope rs(this);
1097        const auto name = signature->InternalName();
1098
1099        switch (arguments.size()) {
1100            case 0U: {
1101                Ra().Emit<Short>(data.node, name, data.obj, data.param2);
1102                break;
1103            }
1104            case 1U: {
1105                COMPILE_ARG(0, 2U);
1106                Ra().Emit<General, 3U>(data.node, name, data.obj, data.param2, arg0, dummyReg_);
1107                break;
1108            }
1109            case 2U: {
1110                COMPILE_ARG(0, 2U);
1111                COMPILE_ARG(1, 2U);
1112                Ra().Emit<General>(data.node, name, data.obj, data.param2, arg0, arg1);
1113                break;
1114            }
1115            default: {
1116                for (size_t idx = 0; idx < arguments.size(); idx++) {
1117                    COMPILE_ARG(idx, 2U);
1118                }
1119
1120                Rra().Emit<Range>(data.node, data.obj, arguments.size() + 2U, name, data.obj);
1121                break;
1122            }
1123        }
1124    }
1125
1126    template <typename Short, typename General, typename Range>
1127    void CallDynamicImpl(CallDynamicData data, VReg param3, checker::Signature *signature,
1128                         const ArenaVector<ir::Expression *> &arguments)
1129    {
1130        RegScope rs(this);
1131        const auto name = signature->InternalName();
1132
1133        switch (arguments.size()) {
1134            case 0U: {
1135                Ra().Emit<General, 3U>(data.node, name, data.obj, data.param2, param3, dummyReg_);
1136                break;
1137            }
1138            case 1U: {
1139                COMPILE_ARG(0, 3U);
1140                Ra().Emit<General>(data.node, name, data.obj, data.param2, param3, arg0);
1141                break;
1142            }
1143            default: {
1144                for (size_t idx = 0; idx < arguments.size(); idx++) {
1145                    COMPILE_ARG(idx, 3U);
1146                }
1147
1148                Rra().Emit<Range>(data.node, data.obj, arguments.size() + 3U, name, data.obj);
1149                break;
1150            }
1151        }
1152    }
1153
1154#undef COMPILE_ARG
1155    // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty)
1156
1157    void ToBinaryResult(const ir::AstNode *node, Label *ifFalse);
1158
1159    template <typename T>
1160    void LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType);
1161    template <typename T>
1162    void SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number);
1163    void InitializeContainingClass();
1164
1165    util::StringView FormDynamicModulePropReference(const varbinder::Variable *var);
1166    util::StringView FormDynamicModulePropReference(const ir::ETSImportDeclaration *import);
1167
1168    friend class TargetTypeContext;
1169
1170    VReg acc_ {};
1171    const checker::Type *targetType_ {};
1172    const checker::ETSObjectType *containingObjectType_ {};
1173};
1174
1175template <typename T>
1176void ETSGen::SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number)
1177{
1178    switch (typeKind) {
1179        case checker::TypeFlag::ETS_BOOLEAN:
1180        case checker::TypeFlag::BYTE: {
1181            Sa().Emit<Ldai>(node, static_cast<checker::ByteType::UType>(number));
1182            SetAccumulatorType(Checker()->GlobalByteType());
1183            break;
1184        }
1185        case checker::TypeFlag::CHAR: {
1186            Sa().Emit<Ldai>(node, static_cast<checker::CharType::UType>(number));
1187            SetAccumulatorType(Checker()->GlobalCharType());
1188            break;
1189        }
1190        case checker::TypeFlag::SHORT: {
1191            Sa().Emit<Ldai>(node, static_cast<checker::ShortType::UType>(number));
1192            SetAccumulatorType(Checker()->GlobalShortType());
1193            break;
1194        }
1195        case checker::TypeFlag::INT: {
1196            Sa().Emit<Ldai>(node, static_cast<checker::IntType::UType>(number));
1197            SetAccumulatorType(Checker()->GlobalIntType());
1198            break;
1199        }
1200        case checker::TypeFlag::LONG: {
1201            Sa().Emit<LdaiWide>(node, static_cast<checker::LongType::UType>(number));
1202            SetAccumulatorType(Checker()->GlobalLongType());
1203            break;
1204        }
1205        case checker::TypeFlag::FLOAT: {
1206            Sa().Emit<Fldai>(node, static_cast<checker::FloatType::UType>(number));
1207            SetAccumulatorType(Checker()->GlobalFloatType());
1208            break;
1209        }
1210        case checker::TypeFlag::DOUBLE: {
1211            Sa().Emit<FldaiWide>(node, static_cast<checker::DoubleType::UType>(number));
1212            SetAccumulatorType(Checker()->GlobalDoubleType());
1213            break;
1214        }
1215        case checker::TypeFlag::ETS_STRING_ENUM:
1216            [[fallthrough]];
1217        case checker::TypeFlag::ETS_INT_ENUM: {
1218            Sa().Emit<Ldai>(node, static_cast<checker::ETSEnumType::UType>(number));
1219            SetAccumulatorType(Checker()->GlobalIntType());
1220            break;
1221        }
1222        default: {
1223            UNREACHABLE();
1224        }
1225    }
1226}
1227
1228template <typename T>
1229void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType)
1230{
1231    auto typeKind = targetType_ && (!targetType_->IsETSObjectType() && !targetType_->IsETSUnionType() &&
1232                                    !targetType_->IsETSArrayType())
1233                        ? checker::ETSChecker::TypeKind(targetType_)
1234                        : targetType;
1235
1236    SetAccumulatorTargetType(node, typeKind, number);
1237
1238    if (targetType_ && (targetType_->IsETSObjectType() || targetType_->IsETSUnionType())) {
1239        ApplyConversion(node, targetType_);
1240    }
1241}
1242
1243}  // namespace ark::es2panda::compiler
1244
1245#endif
1246