• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
27 namespace ark::es2panda::compiler {
28 
29 class ETSGen final : public CodeGen {
30 public:
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>
ResolveConditionalResultFloat(const ir::AstNode * node,Label * realEndLabel)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>
ResolveConditionalResultNumeric(const ir::AstNode * node,Label * ifFalse,Label ** end)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>
ResolveConditionalResultReference(const ir::AstNode * node)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>
ResolveConditionalResult(const ir::AstNode * node,Label * ifFalse)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 
BranchIfFalse(const ir::AstNode * node,Label * ifFalse)249     void BranchIfFalse(const ir::AstNode *node, Label *ifFalse)
250     {
251         Sa().Emit<Jeqz>(node, ifFalse);
252     }
253 
BranchIfTrue(const ir::AstNode * node,Label * ifTrue)254     void BranchIfTrue(const ir::AstNode *node, Label *ifTrue)
255     {
256         Sa().Emit<Jnez>(node, ifTrue);
257     }
258 
BranchIfNull(const ir::AstNode * node,Label * ifNull)259     void BranchIfNull(const ir::AstNode *node, Label *ifNull)
260     {
261         Sa().Emit<JeqzObj>(node, ifNull);
262     }
263 
BranchIfUndefined(const ir::AstNode * node,Label * ifUndefined)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 
BranchIfNotUndefined(const ir::AstNode * node,Label * ifUndefined)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 
BranchIfNotNull(const ir::AstNode * node,Label * ifNotNull)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 
JumpTo(const ir::AstNode * node,Label * labelTo)293     void JumpTo(const ir::AstNode *node, Label *labelTo)
294     {
295         Sa().Emit<Jmp>(node, labelTo);
296     }
297 
EmitThrow(const ir::AstNode * node,VReg err)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 
LoadAccumulatorByte(const ir::AstNode * node,int8_t number)310     void LoadAccumulatorByte(const ir::AstNode *node, int8_t number)
311     {
312         LoadAccumulatorNumber<int8_t>(node, number, checker::TypeFlag::BYTE);
313     }
314 
LoadAccumulatorShort(const ir::AstNode * node,int16_t number)315     void LoadAccumulatorShort(const ir::AstNode *node, int16_t number)
316     {
317         LoadAccumulatorNumber<int16_t>(node, number, checker::TypeFlag::SHORT);
318     }
319 
LoadAccumulatorInt(const ir::AstNode * node,int32_t number)320     void LoadAccumulatorInt(const ir::AstNode *node, int32_t number)
321     {
322         LoadAccumulatorNumber<int32_t>(node, number, checker::TypeFlag::INT);
323     }
324 
LoadAccumulatorWideInt(const ir::AstNode * node,int64_t number)325     void LoadAccumulatorWideInt(const ir::AstNode *node, int64_t number)
326     {
327         LoadAccumulatorNumber<int64_t>(node, number, checker::TypeFlag::LONG);
328     }
329 
LoadAccumulatorFloat(const ir::AstNode * node,float number)330     void LoadAccumulatorFloat(const ir::AstNode *node, float number)
331     {
332         LoadAccumulatorNumber<float>(node, number, checker::TypeFlag::FLOAT);
333     }
334 
LoadAccumulatorDouble(const ir::AstNode * node,double number)335     void LoadAccumulatorDouble(const ir::AstNode *node, double number)
336     {
337         LoadAccumulatorNumber<double>(node, number, checker::TypeFlag::DOUBLE);
338     }
339 
LoadAccumulatorBoolean(const ir::AstNode * node,bool value)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 
LoadAccumulatorString(const ir::AstNode * node,util::StringView str)347     void LoadAccumulatorString(const ir::AstNode *node, util::StringView str)
348     {
349         Sa().Emit<LdaStr>(node, str);
350         SetAccumulatorType(Checker()->GlobalETSStringLiteralType());
351     }
352 
LoadAccumulatorBigInt(const ir::AstNode * node,util::StringView str)353     void LoadAccumulatorBigInt(const ir::AstNode *node, util::StringView str)
354     {
355         Sa().Emit<LdaStr>(node, str);
356         SetAccumulatorType(Checker()->GlobalETSBigIntType());
357     }
358 
LoadAccumulatorNull(const ir::AstNode * node,const checker::Type * type)359     void LoadAccumulatorNull(const ir::AstNode *node, const checker::Type *type)
360     {
361         Sa().Emit<LdaNull>(node);
362         SetAccumulatorType(type);
363     }
364 
LoadAccumulatorUndefined(const ir::AstNode * node)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 
LoadAccumulatorChar(const ir::AstNode * node,char16_t value)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);
ApplyConversion(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>
MoveImmediateToRegister(const ir::AstNode * node,VReg reg,const checker::TypeFlag valueType,T const value)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>
IncrementImmediateRegister(const ir::AstNode * node,VReg reg,const checker::TypeFlag valueType,T const value)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>
JumpCompareRegister(const ir::AstNode * node,VReg lhs,Label * ifFalse)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);
InitObject(const ir::AstNode * node,checker::Signature const * signature,const ArenaVector<ir::Expression * > & arguments)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 
IsDevirtualizedSignature(const checker::Signature * signature)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 
CallExact(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)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 
CallExact(const ir::AstNode * const node,const checker::Signature * signature,const VReg arg0,const ArenaVector<ir::Expression * > & arguments)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 
CallExact(const ir::AstNode * const node,const util::StringView name)552     void CallExact(const ir::AstNode *const node, const util::StringView name)
553     {
554         Ra().Emit<CallShort, 0>(node, name, dummyReg_, dummyReg_);
555     }
556 
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0)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 
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0,const VReg arg1)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 
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0,const VReg arg1,const VReg arg2)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 
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis,const ArenaVector<ir::Expression * > & arguments)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 
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis)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 
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis,const VReg arg0)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 
CallVirtual(const ir::AstNode * const node,const util::StringView name,const VReg athis)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 
CallVirtual(const ir::AstNode * const node,const util::StringView name,const VReg athis,const VReg arg0)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 
CallDynamic(CallDynamicData data,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)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 
CallDynamic(CallDynamicData data,VReg param3,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)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
LaunchExact(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)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 
LaunchVirtual(const ir::AstNode * const node,checker::Signature * const signature,const VReg athis,const ArenaVector<ir::Expression * > & arguments)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 
GetType(const ir::AstNode * node,bool isEtsPrimitive)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 
671 private:
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 TestIsInstanceConstituent(const ir::AstNode *node, Label *ifTrue, Label *ifFalse, checker::Type const *target,
688                                    bool acceptUndefined);
689     void CheckedReferenceNarrowingObject(const ir::AstNode *node, const checker::Type *target);
690 
691     void HandleLooseNullishEquality(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse, Label *ifTrue);
692 
EmitIsUndefined(const ir::AstNode * node)693     void EmitIsUndefined([[maybe_unused]] const ir::AstNode *node)
694     {
695 #ifdef PANDA_WITH_ETS
696         Sa().Emit<EtsIsundefined>(node);
697 #else
698         UNREACHABLE();
699 #endif  // PANDA_WITH_ETS
700     }
701 
EmitEtsEquals(const ir::AstNode * node,const VReg lhs,const VReg rhs)702     void EmitEtsEquals([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const VReg lhs,
703                        [[maybe_unused]] const VReg rhs)
704     {
705 #ifdef PANDA_WITH_ETS
706         Ra().Emit<EtsEquals>(node, lhs, rhs);
707 #else
708         UNREACHABLE();
709 #endif  // PANDA_WITH_ETS
710     }
711 
712     template <typename T>
StoreValueIntoArray(const ir::AstNode * const node,const VReg arr,const VReg index)713     void StoreValueIntoArray(const ir::AstNode *const node, const VReg arr, const VReg index)
714     {
715         Ra().Emit<T>(node, arr, index);
716     }
717 
718     template <typename LongOp, typename IntOp, typename DoubleOp, typename FloatOp>
UpdateOperator(const ir::AstNode * node)719     void UpdateOperator(const ir::AstNode *node)
720     {
721         switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
722             case checker::TypeFlag::LONG: {
723                 RegScope scope(this);
724                 VReg reg = AllocReg();
725                 Ra().Emit<MoviWide>(node, reg, 1LL);
726                 Ra().Emit<LongOp>(node, reg);
727                 break;
728             }
729             case checker::TypeFlag::INT: {
730                 Sa().Emit<IntOp>(node, 1);
731                 break;
732             }
733             case checker::TypeFlag::CHAR: {
734                 Sa().Emit<IntOp>(node, 1);
735                 Sa().Emit<I32tou16>(node);
736                 break;
737             }
738             case checker::TypeFlag::SHORT: {
739                 Sa().Emit<IntOp>(node, 1);
740                 Sa().Emit<I32toi16>(node);
741                 break;
742             }
743             case checker::TypeFlag::BYTE: {
744                 Sa().Emit<IntOp>(node, 1);
745                 Sa().Emit<I32toi8>(node);
746                 break;
747             }
748             case checker::TypeFlag::DOUBLE: {
749                 RegScope scope(this);
750                 VReg reg = AllocReg();
751                 Ra().Emit<FmoviWide>(node, reg, 1.0);
752                 Ra().Emit<DoubleOp>(node, reg);
753                 break;
754             }
755             case checker::TypeFlag::FLOAT: {
756                 RegScope scope(this);
757                 VReg reg = AllocReg();
758                 Ra().Emit<Fmovi>(node, reg, 1.0F);
759                 Ra().Emit<FloatOp>(node, reg);
760                 break;
761             }
762             default: {
763                 UNREACHABLE();
764             }
765         }
766     }
767 
768     template <typename Br>
769     void InverseCondition(const ir::AstNode *node, Br const &br, Label *target, bool inverse = true)
770     {
771         if (!inverse) {
772             br(target);
773             return;
774         }
775         Label *loc = AllocLabel();
776         br(loc);
777         JumpTo(node, target);
778         SetLabel(node, loc);
779     }
780 
781     void RefEqualityLoose(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
782     void RefEqualityLooseDynamic(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
783 
784     template <typename Compare, typename Cond>
BinaryNumberComparison(const ir::AstNode * node,VReg lhs,Label * ifFalse)785     void BinaryNumberComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
786     {
787         Ra().Emit<Compare>(node, lhs);
788         Sa().Emit<Cond>(node, ifFalse);
789     }
790 
791     template <typename DynCompare>
RefEqualityStrictDynamic(const ir::AstNode * node,VReg lhs,Label * ifFalse)792     void RefEqualityStrictDynamic(const ir::AstNode *node, VReg lhs, Label *ifFalse)
793     {
794         ASSERT(GetAccumulatorType()->IsETSDynamicType() && GetVRegType(lhs)->IsETSDynamicType());
795         RegScope scope(this);
796         Ra().Emit<CallShort, 2U>(node, Signatures::BUILTIN_JSRUNTIME_STRICT_EQUAL, lhs, MoveAccToReg(node));
797         Ra().Emit<DynCompare>(node, ifFalse);
798     }
799 
800     template <typename ObjCompare, typename IntCompare, typename CondCompare, typename DynCompare>
BinaryEquality(const ir::AstNode * node,VReg lhs,Label * ifFalse)801     void BinaryEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse)
802     {
803         BinaryEqualityCondition<ObjCompare, IntCompare, CondCompare>(node, lhs, ifFalse);
804         ToBinaryResult(node, ifFalse);
805         SetAccumulatorType(Checker()->GlobalETSBooleanType());
806     }
807 
808     template <typename ObjCompare, typename IntCompare, typename CondCompare>
BinaryEqualityCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)809     void BinaryEqualityCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
810     {
811         if (targetType_->IsETSReferenceType()) {
812             RegScope rs(this);
813             VReg arg0 = AllocReg();
814             StoreAccumulator(node, arg0);
815             InverseCondition(
816                 node, [this, node, lhs, arg0](Label *tgt) { RefEqualityLoose(node, lhs, arg0, tgt); }, ifFalse,
817                 std::is_same_v<CondCompare, Jeqz>);
818             SetAccumulatorType(Checker()->GlobalETSBooleanType());
819             return;
820         }
821 
822         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
823 
824         switch (typeKind) {
825             case checker::TypeFlag::DOUBLE: {
826                 BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
827                 break;
828             }
829             case checker::TypeFlag::FLOAT: {
830                 BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
831                 break;
832             }
833             case checker::TypeFlag::LONG: {
834                 BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
835                 break;
836             }
837             case checker::TypeFlag::ETS_INT_ENUM:
838             case checker::TypeFlag::ETS_STRING_ENUM:
839             case checker::TypeFlag::ETS_BOOLEAN:
840             case checker::TypeFlag::BYTE:
841             case checker::TypeFlag::CHAR:
842             case checker::TypeFlag::SHORT:
843             case checker::TypeFlag::INT: {
844                 Ra().Emit<IntCompare>(node, lhs, ifFalse);
845                 break;
846             }
847             default: {
848                 UNREACHABLE();
849             }
850         }
851 
852         SetAccumulatorType(Checker()->GlobalETSBooleanType());
853     }
854 
855     template <typename ObjCompare, typename DynCompare>
RefEqualityStrict(const ir::AstNode * node,VReg lhs,Label * ifFalse)856     void RefEqualityStrict(const ir::AstNode *node, VReg lhs, Label *ifFalse)
857     {
858         if (GetAccumulatorType()->IsETSDynamicType() || GetVRegType(lhs)->IsETSDynamicType()) {
859             RefEqualityStrictDynamic<DynCompare>(node, lhs, ifFalse);
860         } else {
861             Ra().Emit<ObjCompare>(node, lhs, ifFalse);
862         }
863 
864         ToBinaryResult(node, ifFalse);
865         SetAccumulatorType(Checker()->GlobalETSBooleanType());
866     }
867 
868     template <typename IntCompare, typename CondCompare>
BinaryRelation(const ir::AstNode * node,VReg lhs,Label * ifFalse)869     void BinaryRelation(const ir::AstNode *node, VReg lhs, Label *ifFalse)
870     {
871         BinaryRelationCondition<IntCompare, CondCompare>(node, lhs, ifFalse);
872         ToBinaryResult(node, ifFalse);
873         SetAccumulatorType(Checker()->GlobalETSBooleanType());
874     }
875 
876     template <typename IntCompare, typename CondCompare>
BinaryRelationCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)877     void BinaryRelationCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
878     {
879         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
880 
881         switch (typeKind) {
882             case checker::TypeFlag::DOUBLE: {
883                 BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
884                 break;
885             }
886             case checker::TypeFlag::FLOAT: {
887                 BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
888                 break;
889             }
890             case checker::TypeFlag::LONG: {
891                 BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
892                 break;
893             }
894             case checker::TypeFlag::ETS_BOOLEAN:
895             case checker::TypeFlag::BYTE:
896             case checker::TypeFlag::SHORT:
897             case checker::TypeFlag::CHAR:
898             case checker::TypeFlag::INT: {
899                 Ra().Emit<IntCompare>(node, lhs, ifFalse);
900                 break;
901             }
902             default: {
903                 UNREACHABLE();
904             }
905         }
906 
907         SetAccumulatorType(Checker()->GlobalETSBooleanType());
908     }
909 
910     template <typename CompareGreater, typename CompareLess, typename CondCompare>
BinaryFloatingPointComparison(const ir::AstNode * node,VReg lhs,Label * ifFalse)911     void BinaryFloatingPointComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
912     {
913         if constexpr (std::is_same_v<CondCompare, Jgez> || std::is_same_v<CondCompare, Jgtz>) {
914             BinaryNumberComparison<CompareGreater, CondCompare>(node, lhs, ifFalse);
915         } else {
916             BinaryNumberComparison<CompareLess, CondCompare>(node, lhs, ifFalse);
917         }
918     }
919 
920     template <typename IntOp, typename LongOp, typename FloatOp, typename DoubleOp>
BinaryArithmetic(const ir::AstNode * node,VReg lhs)921     void BinaryArithmetic(const ir::AstNode *node, VReg lhs)
922     {
923         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
924 
925         switch (typeKind) {
926             case checker::TypeFlag::DOUBLE: {
927                 Ra().Emit<DoubleOp>(node, lhs);
928                 SetAccumulatorType(Checker()->GlobalDoubleType());
929                 break;
930             }
931             case checker::TypeFlag::FLOAT: {
932                 Ra().Emit<FloatOp>(node, lhs);
933                 SetAccumulatorType(Checker()->GlobalFloatType());
934                 break;
935             }
936             default: {
937                 BinaryBitwiseArithmetic<IntOp, LongOp>(node, lhs);
938             }
939         }
940     }
941 
942     template <typename IntOp, typename LongOp>
BinaryBitwiseArithmetic(const ir::AstNode * node,VReg lhs)943     void BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs)
944     {
945         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
946 
947         switch (typeKind) {
948             case checker::TypeFlag::LONG: {
949                 Ra().Emit<LongOp>(node, lhs);
950                 SetAccumulatorType(Checker()->GlobalLongType());
951                 break;
952             }
953             case checker::TypeFlag::BYTE:
954             case checker::TypeFlag::SHORT:
955             case checker::TypeFlag::INT: {
956                 Ra().Emit<IntOp>(node, lhs);
957                 SetAccumulatorType(Checker()->GlobalIntType());
958                 break;
959             }
960             case checker::TypeFlag::ETS_BOOLEAN: {
961                 Ra().Emit<IntOp>(node, lhs);
962                 SetAccumulatorType(Checker()->GlobalETSBooleanType());
963                 break;
964             }
965             case checker::TypeFlag::CHAR: {
966                 Ra().Emit<IntOp>(node, lhs);
967                 SetAccumulatorType(Checker()->GlobalCharType());
968                 break;
969             }
970             default: {
971                 UNREACHABLE();
972             }
973         }
974     }
975 // NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty)
976 #define COMPILE_ARG(idx)                                                                                       \
977     ASSERT((idx) < arguments.size());                                                                          \
978     ASSERT((idx) < signature->Params().size() || signature->RestVar() != nullptr);                             \
979     auto *param##idx = (idx) < signature->Params().size() ? signature->Params()[(idx)] : signature->RestVar(); \
980     auto *paramType##idx = param##idx->TsType();                                                               \
981     auto ttctx##idx = TargetTypeContext(this, paramType##idx);                                                 \
982     arguments[idx]->Compile(this);                                                                             \
983     VReg arg##idx = AllocReg();                                                                                \
984     ApplyConversion(arguments[idx], nullptr);                                                                  \
985     ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
986 
987     template <typename Short, typename General, typename Range>
CallArgStart(const ir::AstNode * const node,const checker::Signature * signature,const VReg argStart,const ArenaVector<ir::Expression * > & arguments)988     void CallArgStart(const ir::AstNode *const node, const checker::Signature *signature, const VReg argStart,
989                       const ArenaVector<ir::Expression *> &arguments)
990     {
991         RegScope rs(this);
992         const auto name = signature->InternalName();
993 
994         switch (arguments.size()) {
995             case 0U: {
996                 Ra().Emit<Short, 1>(node, name, argStart, dummyReg_);
997                 break;
998             }
999             case 1U: {
1000                 COMPILE_ARG(0);
1001                 Ra().Emit<Short>(node, name, argStart, arg0);
1002                 break;
1003             }
1004             case 2U: {
1005                 COMPILE_ARG(0);
1006                 COMPILE_ARG(1);
1007                 Ra().Emit<General, 3U>(node, name, argStart, arg0, arg1, dummyReg_);
1008                 break;
1009             }
1010             case 3U: {
1011                 COMPILE_ARG(0);
1012                 COMPILE_ARG(1);
1013                 COMPILE_ARG(2);
1014                 Ra().Emit<General>(node, name, argStart, arg0, arg1, arg2);
1015                 break;
1016             }
1017             default: {
1018                 for (size_t idx = 0; idx < arguments.size(); idx++) {
1019                     COMPILE_ARG(idx);
1020                 }
1021 
1022                 Rra().Emit<Range>(node, argStart, arguments.size() + 1, name, argStart);
1023                 break;
1024             }
1025         }
1026     }
1027 
1028     template <typename Short, typename General, typename Range>
CallImpl(const ir::AstNode * node,checker::Signature const * signature,const ArenaVector<ir::Expression * > & arguments)1029     void CallImpl(const ir::AstNode *node, checker::Signature const *signature,
1030                   const ArenaVector<ir::Expression *> &arguments)
1031     {
1032         ASSERT(signature != nullptr);
1033         RegScope rs(this);
1034 
1035         switch (arguments.size()) {
1036             case 0U: {
1037                 Ra().Emit<Short, 0U>(node, signature->InternalName(), dummyReg_, dummyReg_);
1038                 break;
1039             }
1040             case 1U: {
1041                 COMPILE_ARG(0);
1042                 Ra().Emit<Short, 1U>(node, signature->InternalName(), arg0, dummyReg_);
1043                 break;
1044             }
1045             case 2U: {
1046                 COMPILE_ARG(0);
1047                 COMPILE_ARG(1);
1048                 Ra().Emit<Short, 2U>(node, signature->InternalName(), arg0, arg1);
1049                 break;
1050             }
1051             case 3U: {
1052                 COMPILE_ARG(0);
1053                 COMPILE_ARG(1);
1054                 COMPILE_ARG(2);
1055                 Ra().Emit<General, 3U>(node, signature->InternalName(), arg0, arg1, arg2, dummyReg_);
1056                 break;
1057             }
1058             case 4U: {
1059                 COMPILE_ARG(0);
1060                 COMPILE_ARG(1);
1061                 COMPILE_ARG(2);
1062                 COMPILE_ARG(3);
1063                 Ra().Emit<General, 4U>(node, signature->InternalName(), arg0, arg1, arg2, arg3);
1064                 break;
1065             }
1066             default: {
1067                 VReg argStart = NextReg();
1068 
1069                 for (size_t idx = 0; idx < arguments.size(); idx++) {
1070                     COMPILE_ARG(idx);
1071                 }
1072 
1073                 Rra().Emit<Range>(node, argStart, arguments.size(), signature->InternalName(), argStart);
1074                 break;
1075             }
1076         }
1077     }
1078 #undef COMPILE_ARG
1079 
1080 #define COMPILE_ARG(idx, shift)                                                              \
1081     ASSERT((idx) < arguments.size());                                                        \
1082     ASSERT((idx) + (shift) < signature->Params().size() || signature->RestVar() != nullptr); \
1083     auto *paramType##idx = (idx) + (shift) < signature->Params().size()                      \
1084                                ? signature->Params()[(idx) + (shift)]->TsType()              \
1085                                : signature->RestVar()->TsType();                             \
1086     auto ttctx##idx = TargetTypeContext(this, paramType##idx);                               \
1087     VReg arg##idx = AllocReg();                                                              \
1088     arguments[idx]->Compile(this);                                                           \
1089     ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
1090 
1091     template <typename Short, typename General, typename Range>
CallDynamicImpl(CallDynamicData data,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)1092     void CallDynamicImpl(CallDynamicData data, checker::Signature *signature,
1093                          const ArenaVector<ir::Expression *> &arguments)
1094     {
1095         RegScope rs(this);
1096         const auto name = signature->InternalName();
1097 
1098         switch (arguments.size()) {
1099             case 0U: {
1100                 Ra().Emit<Short>(data.node, name, data.obj, data.param2);
1101                 break;
1102             }
1103             case 1U: {
1104                 COMPILE_ARG(0, 2U);
1105                 Ra().Emit<General, 3U>(data.node, name, data.obj, data.param2, arg0, dummyReg_);
1106                 break;
1107             }
1108             case 2U: {
1109                 COMPILE_ARG(0, 2U);
1110                 COMPILE_ARG(1, 2U);
1111                 Ra().Emit<General>(data.node, name, data.obj, data.param2, arg0, arg1);
1112                 break;
1113             }
1114             default: {
1115                 for (size_t idx = 0; idx < arguments.size(); idx++) {
1116                     COMPILE_ARG(idx, 2U);
1117                 }
1118 
1119                 Rra().Emit<Range>(data.node, data.obj, arguments.size() + 2U, name, data.obj);
1120                 break;
1121             }
1122         }
1123     }
1124 
1125     template <typename Short, typename General, typename Range>
CallDynamicImpl(CallDynamicData data,VReg param3,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)1126     void CallDynamicImpl(CallDynamicData data, VReg param3, checker::Signature *signature,
1127                          const ArenaVector<ir::Expression *> &arguments)
1128     {
1129         RegScope rs(this);
1130         const auto name = signature->InternalName();
1131 
1132         switch (arguments.size()) {
1133             case 0U: {
1134                 Ra().Emit<General, 3U>(data.node, name, data.obj, data.param2, param3, dummyReg_);
1135                 break;
1136             }
1137             case 1U: {
1138                 COMPILE_ARG(0, 3U);
1139                 Ra().Emit<General>(data.node, name, data.obj, data.param2, param3, arg0);
1140                 break;
1141             }
1142             default: {
1143                 for (size_t idx = 0; idx < arguments.size(); idx++) {
1144                     COMPILE_ARG(idx, 3U);
1145                 }
1146 
1147                 Rra().Emit<Range>(data.node, data.obj, arguments.size() + 3U, name, data.obj);
1148                 break;
1149             }
1150         }
1151     }
1152 
1153 #undef COMPILE_ARG
1154     // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty)
1155 
1156     void ToBinaryResult(const ir::AstNode *node, Label *ifFalse);
1157 
1158     template <typename T>
1159     void LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType);
1160     template <typename T>
1161     void SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number);
1162     void InitializeContainingClass();
1163 
1164     util::StringView FormDynamicModulePropReference(const varbinder::Variable *var);
1165     util::StringView FormDynamicModulePropReference(const ir::ETSImportDeclaration *import);
1166 
1167     friend class TargetTypeContext;
1168 
1169     VReg acc_ {};
1170     const checker::Type *targetType_ {};
1171     const checker::ETSObjectType *containingObjectType_ {};
1172 };
1173 
1174 template <typename T>
SetAccumulatorTargetType(const ir::AstNode * node,checker::TypeFlag typeKind,T number)1175 void ETSGen::SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number)
1176 {
1177     switch (typeKind) {
1178         case checker::TypeFlag::ETS_BOOLEAN:
1179         case checker::TypeFlag::BYTE: {
1180             Sa().Emit<Ldai>(node, static_cast<checker::ByteType::UType>(number));
1181             SetAccumulatorType(Checker()->GlobalByteType());
1182             break;
1183         }
1184         case checker::TypeFlag::CHAR: {
1185             Sa().Emit<Ldai>(node, static_cast<checker::CharType::UType>(number));
1186             SetAccumulatorType(Checker()->GlobalCharType());
1187             break;
1188         }
1189         case checker::TypeFlag::SHORT: {
1190             Sa().Emit<Ldai>(node, static_cast<checker::ShortType::UType>(number));
1191             SetAccumulatorType(Checker()->GlobalShortType());
1192             break;
1193         }
1194         case checker::TypeFlag::INT: {
1195             Sa().Emit<Ldai>(node, static_cast<checker::IntType::UType>(number));
1196             SetAccumulatorType(Checker()->GlobalIntType());
1197             break;
1198         }
1199         case checker::TypeFlag::LONG: {
1200             Sa().Emit<LdaiWide>(node, static_cast<checker::LongType::UType>(number));
1201             SetAccumulatorType(Checker()->GlobalLongType());
1202             break;
1203         }
1204         case checker::TypeFlag::FLOAT: {
1205             Sa().Emit<Fldai>(node, static_cast<checker::FloatType::UType>(number));
1206             SetAccumulatorType(Checker()->GlobalFloatType());
1207             break;
1208         }
1209         case checker::TypeFlag::DOUBLE: {
1210             Sa().Emit<FldaiWide>(node, static_cast<checker::DoubleType::UType>(number));
1211             SetAccumulatorType(Checker()->GlobalDoubleType());
1212             break;
1213         }
1214         case checker::TypeFlag::ETS_STRING_ENUM:
1215             [[fallthrough]];
1216         case checker::TypeFlag::ETS_INT_ENUM: {
1217             Sa().Emit<Ldai>(node, static_cast<checker::ETSEnumType::UType>(number));
1218             SetAccumulatorType(Checker()->GlobalIntType());
1219             break;
1220         }
1221         default: {
1222             UNREACHABLE();
1223         }
1224     }
1225 }
1226 
1227 template <typename T>
LoadAccumulatorNumber(const ir::AstNode * node,T number,checker::TypeFlag targetType)1228 void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType)
1229 {
1230     auto typeKind = targetType_ && (!targetType_->IsETSObjectType() && !targetType_->IsETSUnionType() &&
1231                                     !targetType_->IsETSArrayType())
1232                         ? checker::ETSChecker::TypeKind(targetType_)
1233                         : targetType;
1234 
1235     SetAccumulatorTargetType(node, typeKind, number);
1236 
1237     if (targetType_ && (targetType_->IsETSObjectType() || targetType_->IsETSUnionType())) {
1238         ApplyConversion(node, targetType_);
1239     }
1240 }
1241 
1242 }  // namespace ark::es2panda::compiler
1243 
1244 #endif
1245