• 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 EmitBoxingConversion(ir::BoxingUnboxingFlags boxingFlag, const ir::AstNode *node);
397     void EmitBoxingConversion(const ir::AstNode *node);
398     void SwapBinaryOpArgs(const ir::AstNode *node, VReg lhs);
399     VReg MoveAccToReg(const ir::AstNode *node);
400 
401     void LoadArrayLength(const ir::AstNode *node, VReg arrayReg);
402     void LoadArrayElement(const ir::AstNode *node, VReg objectReg);
403     void StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index, const checker::Type *elementType);
404 
405     template <typename T>
MoveImmediateToRegister(const ir::AstNode * node,VReg reg,const checker::TypeFlag valueType,T const value)406     void MoveImmediateToRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value)
407     {
408         switch (valueType) {
409             case checker::TypeFlag::ETS_BOOLEAN:
410                 [[fallthrough]];
411             case checker::TypeFlag::BYTE: {
412                 Ra().Emit<Movi>(node, reg, static_cast<checker::ByteType::UType>(value));
413                 SetVRegType(reg, Checker()->GlobalByteType());
414                 break;
415             }
416             case checker::TypeFlag::CHAR: {
417                 Ra().Emit<Movi>(node, reg, static_cast<checker::CharType::UType>(value));
418                 SetVRegType(reg, Checker()->GlobalCharType());
419                 break;
420             }
421             case checker::TypeFlag::SHORT: {
422                 Ra().Emit<Movi>(node, reg, static_cast<checker::ShortType::UType>(value));
423                 SetVRegType(reg, Checker()->GlobalShortType());
424                 break;
425             }
426             case checker::TypeFlag::INT: {
427                 Ra().Emit<Movi>(node, reg, static_cast<checker::IntType::UType>(value));
428                 SetVRegType(reg, Checker()->GlobalIntType());
429                 break;
430             }
431             case checker::TypeFlag::LONG: {
432                 Ra().Emit<MoviWide>(node, reg, static_cast<checker::LongType::UType>(value));
433                 SetVRegType(reg, Checker()->GlobalLongType());
434                 break;
435             }
436             case checker::TypeFlag::FLOAT: {
437                 Ra().Emit<Fmovi>(node, reg, static_cast<checker::FloatType::UType>(value));
438                 SetVRegType(reg, Checker()->GlobalFloatType());
439                 break;
440             }
441             case checker::TypeFlag::DOUBLE: {
442                 Ra().Emit<FmoviWide>(node, reg, static_cast<checker::DoubleType::UType>(value));
443                 SetVRegType(reg, Checker()->GlobalDoubleType());
444                 break;
445             }
446             default: {
447                 UNREACHABLE();
448             }
449         }
450     }
451 
452     template <typename T>
IncrementImmediateRegister(const ir::AstNode * node,VReg reg,const checker::TypeFlag valueType,T const value)453     void IncrementImmediateRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value)
454     {
455         switch (valueType) {
456             // NOTE: operand of increment instruction (INCI) is defined in spec as 32-bit integer,
457             // but its current implementation actually can work with 64-bit integers as well.
458             case checker::TypeFlag::INT: {
459                 Ra().Emit<Inci>(node, reg, static_cast<checker::IntType::UType>(value));
460                 break;
461             }
462             case checker::TypeFlag::CHAR: {
463                 Ra().Emit<Inci>(node, reg, static_cast<checker::CharType::UType>(value));
464                 break;
465             }
466             case checker::TypeFlag::SHORT: {
467                 Ra().Emit<Inci>(node, reg, static_cast<checker::ShortType::UType>(value));
468                 break;
469             }
470             case checker::TypeFlag::ETS_BOOLEAN:
471                 [[fallthrough]];
472             case checker::TypeFlag::BYTE: {
473                 Ra().Emit<Inci>(node, reg, static_cast<checker::ByteType::UType>(value));
474                 break;
475             }
476             default: {
477                 UNREACHABLE();
478             }
479         }
480     }
481 
482     template <typename IntCompare>
JumpCompareRegister(const ir::AstNode * node,VReg lhs,Label * ifFalse)483     void JumpCompareRegister(const ir::AstNode *node, VReg lhs, Label *ifFalse)
484     {
485         Ra().Emit<IntCompare>(node, lhs, ifFalse);
486     }
487 
488     void LoadStringLength(const ir::AstNode *node);
489     void LoadStringChar(const ir::AstNode *node, VReg stringObj, VReg charIndex);
490 
491     void FloatIsNaN(const ir::AstNode *node);
492     void DoubleIsNaN(const ir::AstNode *node);
493 
494     void CompileStatements(const ArenaVector<ir::Statement *> &statements);
495 
496     // Cast
497     void CastToBoolean(const ir::AstNode *node);
498     void CastToByte(const ir::AstNode *node);
499     void CastToChar(const ir::AstNode *node);
500     void CastToShort(const ir::AstNode *node);
501     void CastToDouble(const ir::AstNode *node);
502     void CastToFloat(const ir::AstNode *node);
503     void CastToLong(const ir::AstNode *node);
504     void CastToInt(const ir::AstNode *node);
505     void CastToString(const ir::AstNode *node);
506     void CastToDynamic(const ir::AstNode *node, const checker::ETSDynamicType *type);
507     void CastDynamicTo(const ir::AstNode *node, enum checker::TypeFlag typeFlag);
508     void CastToReftype(const ir::AstNode *node, const checker::Type *targetType, bool unchecked);
509     void CastDynamicToObject(const ir::AstNode *node, const checker::Type *targetType);
510     void CastUnionToFunctionType(const ir::AstNode *node, const checker::ETSUnionType *unionType,
511                                  checker::Signature *signatureTarget);
512 
513     void InternalIsInstance(const ir::AstNode *node, const checker::Type *target);
514     void InternalCheckCast(const ir::AstNode *node, const checker::Type *target);
515     void CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target);
516     void GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target);
517 
518     // Call, Construct
519     void NewArray(const ir::AstNode *node, VReg arr, VReg dim, const checker::Type *arrType);
520     void NewObject(const ir::AstNode *node, util::StringView name, VReg athis);
521     void BuildString(const ir::Expression *node);
522     void CallBigIntUnaryOperator(const ir::Expression *node, VReg arg, util::StringView signature);
523     void CallBigIntBinaryOperator(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature);
524     void CallBigIntBinaryComparison(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature);
525     void BuildTemplateString(const ir::TemplateLiteral *node);
InitObject(const ir::AstNode * node,checker::Signature const * signature,const ArenaVector<ir::Expression * > & arguments)526     void InitObject(const ir::AstNode *node, checker::Signature const *signature,
527                     const ArenaVector<ir::Expression *> &arguments)
528     {
529         CallImpl<InitobjShort, Initobj, InitobjRange>(node, signature, arguments);
530     }
531 
IsDevirtualizedSignature(const checker::Signature * signature)532     bool IsDevirtualizedSignature(const checker::Signature *signature)
533     {
534         ASSERT(!signature->HasSignatureFlag(checker::SignatureFlags::STATIC));
535         return signature->HasSignatureFlag(checker::SignatureFlags::FINAL | checker::SignatureFlags::PRIVATE |
536                                            checker::SignatureFlags::CONSTRUCTOR);
537     }
538 
CallExact(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)539     void CallExact(const ir::AstNode *node, checker::Signature *signature,
540                    const ArenaVector<ir::Expression *> &arguments)
541     {
542         CallImpl<CallShort, Call, CallRange>(node, signature, arguments);
543     }
544 
CallExact(const ir::AstNode * const node,const checker::Signature * signature,const VReg arg0,const ArenaVector<ir::Expression * > & arguments)545     void CallExact(const ir::AstNode *const node, const checker::Signature *signature, const VReg arg0,
546                    const ArenaVector<ir::Expression *> &arguments)
547     {
548         CallArgStart<CallShort, Call, CallRange>(node, signature, arg0, arguments);
549     }
550 
CallExact(const ir::AstNode * const node,const util::StringView name)551     void CallExact(const ir::AstNode *const node, const util::StringView name)
552     {
553         Ra().Emit<CallShort, 0>(node, name, dummyReg_, dummyReg_);
554     }
555 
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0)556     void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0)
557     {
558         Ra().Emit<CallShort, 1>(node, name, arg0, dummyReg_);
559     }
560 
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0,const VReg arg1)561     void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1)
562     {
563         Ra().Emit<CallShort>(node, name, arg0, arg1);
564     }
565 
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0,const VReg arg1,const VReg arg2)566     void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1,
567                    const VReg arg2)
568     {
569         Ra().Emit<Call, 3U>(node, name, arg0, arg1, arg2, dummyReg_);
570     }
571 
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis,const ArenaVector<ir::Expression * > & arguments)572     void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis,
573                      const ArenaVector<ir::Expression *> &arguments)
574     {
575         ASSERT(!signature->HasSignatureFlag(checker::SignatureFlags::STATIC));
576         ASSERT(!signature->Owner()->GetDeclNode()->IsFinal() || signature->IsFinal());
577         if (IsDevirtualizedSignature(signature)) {
578             CallArgStart<CallShort, Call, CallRange>(node, signature, athis, arguments);
579         } else {
580             CallArgStart<CallVirtShort, CallVirt, CallVirtRange>(node, signature, athis, arguments);
581         }
582     }
583 
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis)584     void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis)
585     {
586         if (IsDevirtualizedSignature(signature)) {
587             CallExact(node, signature->InternalName(), athis);
588         } else {
589             CallVirtual(node, signature->InternalName(), athis);
590         }
591     }
592 
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis,const VReg arg0)593     void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis,
594                      const VReg arg0)
595     {
596         if (IsDevirtualizedSignature(signature)) {
597             CallExact(node, signature->InternalName(), athis, arg0);
598         } else {
599             CallVirtual(node, signature->InternalName(), athis, arg0);
600         }
601     }
602 
CallVirtual(const ir::AstNode * const node,const util::StringView name,const VReg athis)603     void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis)
604     {
605         Ra().Emit<CallVirtShort, 1>(node, name, athis, dummyReg_);
606     }
607 
CallVirtual(const ir::AstNode * const node,const util::StringView name,const VReg athis,const VReg arg0)608     void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis, const VReg arg0)
609     {
610         Ra().Emit<CallVirtShort>(node, name, athis, arg0);
611     }
612 
613     struct CallDynamicData {
614         const ir::AstNode *node = nullptr;
615         VReg obj;
616         VReg param2;
617     };
618 
CallDynamic(CallDynamicData data,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)619     void CallDynamic(CallDynamicData data, checker::Signature *signature,
620                      const ArenaVector<ir::Expression *> &arguments)
621     {
622         CallDynamicImpl<CallShort, Call, CallRange>(data, signature, arguments);
623     }
624 
CallDynamic(CallDynamicData data,VReg param3,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)625     void CallDynamic(CallDynamicData data, VReg param3, checker::Signature *signature,
626                      const ArenaVector<ir::Expression *> &arguments)
627     {
628         CallDynamicImpl<CallShort, Call, CallRange>(data, param3, signature, arguments);
629     }
630 
631 #ifdef PANDA_WITH_ETS
632     // The functions below use ETS specific instructions.
633     // Compilation of es2panda fails if ETS plugin is disabled
LaunchExact(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)634     void LaunchExact(const ir::AstNode *node, checker::Signature *signature,
635                      const ArenaVector<ir::Expression *> &arguments)
636     {
637         CallImpl<EtsLaunchShort, EtsLaunch, EtsLaunchRange>(node, signature, arguments);
638     }
639 
LaunchVirtual(const ir::AstNode * const node,checker::Signature * const signature,const VReg athis,const ArenaVector<ir::Expression * > & arguments)640     void LaunchVirtual(const ir::AstNode *const node, checker::Signature *const signature, const VReg athis,
641                        const ArenaVector<ir::Expression *> &arguments)
642     {
643         if (IsDevirtualizedSignature(signature)) {
644             CallArgStart<EtsLaunchShort, EtsLaunch, EtsLaunchRange>(node, signature, athis, arguments);
645         } else {
646             CallArgStart<EtsLaunchVirtShort, EtsLaunchVirt, EtsLaunchVirtRange>(node, signature, athis, arguments);
647         }
648     }
649 #endif  // PANDA_WITH_ETS
650 
651     void CreateBigIntObject(const ir::AstNode *node, VReg arg0,
652                             std::string_view signature = Signatures::BUILTIN_BIGINT_CTOR);
653 
GetType(const ir::AstNode * node,bool isEtsPrimitive)654     void GetType(const ir::AstNode *node, bool isEtsPrimitive)
655     {
656         if (isEtsPrimitive) {
657             // NOTE: SzD. LoadStaticProperty if ETS stdlib has static TYPE constants otherwise fallback to LdaType
658         } else {
659             auto classRef = GetAccumulatorType()->AsETSObjectType()->AssemblerName();
660             Sa().Emit<LdaType>(node, classRef);
661         }
662     }
663 
664     ~ETSGen() override = default;
665     NO_COPY_SEMANTIC(ETSGen);
666     NO_MOVE_SEMANTIC(ETSGen);
667 
668 private:
669     const VReg dummyReg_ = VReg::RegStart();
670 
671     void EmitUnboxedCall(const ir::AstNode *node, std::string_view signatureFlag, const checker::Type *targetType,
672                          const checker::Type *boxedType);
673 
674     void LoadConstantObject(const ir::Expression *node, const checker::Type *type);
675     void StringBuilderAppend(const ir::AstNode *node, VReg builder);
676     void AppendString(const ir::Expression *binExpr, VReg builder);
677     void StringBuilder(const ir::Expression *left, const ir::Expression *right, VReg builder);
678     util::StringView FormClassPropReference(varbinder::Variable const *var);
679     void UnaryMinus(const ir::AstNode *node);
680     void UnaryTilde(const ir::AstNode *node);
681 
682     util::StringView ToAssemblerType(const es2panda::checker::Type *type) const;
683     void TestIsInstanceConstant(const ir::AstNode *node, Label *ifTrue, VReg srcReg, checker::Type const *target);
684     void TestIsInstanceConstituent(const ir::AstNode *node, std::tuple<Label *, Label *> label, VReg srcReg,
685                                    checker::Type const *target, bool acceptUndefined);
686     void CheckedReferenceNarrowingObject(const ir::AstNode *node, const checker::Type *target);
687 
688     void HandleLooseNullishEquality(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse, Label *ifTrue);
689 
EmitIsUndefined(const ir::AstNode * node)690     void EmitIsUndefined([[maybe_unused]] const ir::AstNode *node)
691     {
692 #ifdef PANDA_WITH_ETS
693         Sa().Emit<EtsIsundefined>(node);
694 #else
695         UNREACHABLE();
696 #endif  // PANDA_WITH_ETS
697     }
698 
EmitEtsEquals(const ir::AstNode * node,const VReg lhs,const VReg rhs)699     void EmitEtsEquals([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const VReg lhs,
700                        [[maybe_unused]] const VReg rhs)
701     {
702 #ifdef PANDA_WITH_ETS
703         Ra().Emit<EtsEquals>(node, lhs, rhs);
704 #else
705         UNREACHABLE();
706 #endif  // PANDA_WITH_ETS
707     }
708 
709     template <typename T>
StoreValueIntoArray(const ir::AstNode * const node,const VReg arr,const VReg index)710     void StoreValueIntoArray(const ir::AstNode *const node, const VReg arr, const VReg index)
711     {
712         Ra().Emit<T>(node, arr, index);
713     }
714 
715     template <typename LongOp, typename IntOp, typename DoubleOp, typename FloatOp>
UpdateOperator(const ir::AstNode * node)716     void UpdateOperator(const ir::AstNode *node)
717     {
718         switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
719             case checker::TypeFlag::LONG: {
720                 RegScope scope(this);
721                 VReg reg = AllocReg();
722                 Ra().Emit<MoviWide>(node, reg, 1LL);
723                 Ra().Emit<LongOp>(node, reg);
724                 break;
725             }
726             case checker::TypeFlag::INT: {
727                 Sa().Emit<IntOp>(node, 1);
728                 break;
729             }
730             case checker::TypeFlag::CHAR: {
731                 Sa().Emit<IntOp>(node, 1);
732                 Sa().Emit<I32tou16>(node);
733                 break;
734             }
735             case checker::TypeFlag::SHORT: {
736                 Sa().Emit<IntOp>(node, 1);
737                 Sa().Emit<I32toi16>(node);
738                 break;
739             }
740             case checker::TypeFlag::BYTE: {
741                 Sa().Emit<IntOp>(node, 1);
742                 Sa().Emit<I32toi8>(node);
743                 break;
744             }
745             case checker::TypeFlag::DOUBLE: {
746                 RegScope scope(this);
747                 VReg reg = AllocReg();
748                 Ra().Emit<FmoviWide>(node, reg, 1.0);
749                 Ra().Emit<DoubleOp>(node, reg);
750                 break;
751             }
752             case checker::TypeFlag::FLOAT: {
753                 RegScope scope(this);
754                 VReg reg = AllocReg();
755                 Ra().Emit<Fmovi>(node, reg, 1.0F);
756                 Ra().Emit<FloatOp>(node, reg);
757                 break;
758             }
759             default: {
760                 UNREACHABLE();
761             }
762         }
763     }
764 
765     template <typename Br>
766     void InverseCondition(const ir::AstNode *node, Br const &br, Label *target, bool inverse = true)
767     {
768         if (!inverse) {
769             br(target);
770             return;
771         }
772         Label *loc = AllocLabel();
773         br(loc);
774         JumpTo(node, target);
775         SetLabel(node, loc);
776     }
777 
778     void RefEqualityLoose(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
779     void RefEqualityLooseDynamic(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
780 
781     template <typename Compare, typename Cond>
BinaryNumberComparison(const ir::AstNode * node,VReg lhs,Label * ifFalse)782     void BinaryNumberComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
783     {
784         Ra().Emit<Compare>(node, lhs);
785         Sa().Emit<Cond>(node, ifFalse);
786     }
787 
788     template <typename DynCompare>
RefEqualityStrictDynamic(const ir::AstNode * node,VReg lhs,Label * ifFalse)789     void RefEqualityStrictDynamic(const ir::AstNode *node, VReg lhs, Label *ifFalse)
790     {
791         ASSERT(GetAccumulatorType()->IsETSDynamicType() && GetVRegType(lhs)->IsETSDynamicType());
792         RegScope scope(this);
793         Ra().Emit<CallShort, 2U>(node, Signatures::BUILTIN_JSRUNTIME_STRICT_EQUAL, lhs, MoveAccToReg(node));
794         Ra().Emit<DynCompare>(node, ifFalse);
795     }
796 
797     template <typename ObjCompare, typename IntCompare, typename CondCompare, typename DynCompare>
BinaryEquality(const ir::AstNode * node,VReg lhs,Label * ifFalse)798     void BinaryEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse)
799     {
800         BinaryEqualityCondition<ObjCompare, IntCompare, CondCompare>(node, lhs, ifFalse);
801         ToBinaryResult(node, ifFalse);
802         SetAccumulatorType(Checker()->GlobalETSBooleanType());
803     }
804 
805     template <typename ObjCompare, typename IntCompare, typename CondCompare>
BinaryEqualityCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)806     void BinaryEqualityCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
807     {
808         if (targetType_->IsETSReferenceType()) {
809             RegScope rs(this);
810             VReg arg0 = AllocReg();
811             StoreAccumulator(node, arg0);
812             InverseCondition(
813                 node, [this, node, lhs, arg0](Label *tgt) { RefEqualityLoose(node, lhs, arg0, tgt); }, ifFalse,
814                 std::is_same_v<CondCompare, Jeqz>);
815             SetAccumulatorType(Checker()->GlobalETSBooleanType());
816             return;
817         }
818 
819         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
820 
821         switch (typeKind) {
822             case checker::TypeFlag::DOUBLE: {
823                 BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
824                 break;
825             }
826             case checker::TypeFlag::FLOAT: {
827                 BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
828                 break;
829             }
830             case checker::TypeFlag::LONG: {
831                 BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
832                 break;
833             }
834             case checker::TypeFlag::ETS_INT_ENUM:
835             case checker::TypeFlag::ETS_STRING_ENUM:
836             case checker::TypeFlag::ETS_BOOLEAN:
837             case checker::TypeFlag::BYTE:
838             case checker::TypeFlag::CHAR:
839             case checker::TypeFlag::SHORT:
840             case checker::TypeFlag::INT: {
841                 Ra().Emit<IntCompare>(node, lhs, ifFalse);
842                 break;
843             }
844             default: {
845                 UNREACHABLE();
846             }
847         }
848 
849         SetAccumulatorType(Checker()->GlobalETSBooleanType());
850     }
851 
852     template <typename ObjCompare, typename DynCompare>
RefEqualityStrict(const ir::AstNode * node,VReg lhs,Label * ifFalse)853     void RefEqualityStrict(const ir::AstNode *node, VReg lhs, Label *ifFalse)
854     {
855         if (GetAccumulatorType()->IsETSDynamicType() || GetVRegType(lhs)->IsETSDynamicType()) {
856             RefEqualityStrictDynamic<DynCompare>(node, lhs, ifFalse);
857         } else {
858             Ra().Emit<ObjCompare>(node, lhs, ifFalse);
859         }
860 
861         ToBinaryResult(node, ifFalse);
862         SetAccumulatorType(Checker()->GlobalETSBooleanType());
863     }
864 
865     template <typename IntCompare, typename CondCompare>
BinaryRelation(const ir::AstNode * node,VReg lhs,Label * ifFalse)866     void BinaryRelation(const ir::AstNode *node, VReg lhs, Label *ifFalse)
867     {
868         BinaryRelationCondition<IntCompare, CondCompare>(node, lhs, ifFalse);
869         ToBinaryResult(node, ifFalse);
870         SetAccumulatorType(Checker()->GlobalETSBooleanType());
871     }
872 
873     template <typename IntCompare, typename CondCompare>
BinaryRelationCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)874     void BinaryRelationCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
875     {
876         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
877 
878         switch (typeKind) {
879             case checker::TypeFlag::DOUBLE: {
880                 BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
881                 break;
882             }
883             case checker::TypeFlag::FLOAT: {
884                 BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
885                 break;
886             }
887             case checker::TypeFlag::LONG: {
888                 BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
889                 break;
890             }
891             case checker::TypeFlag::ETS_BOOLEAN:
892             case checker::TypeFlag::BYTE:
893             case checker::TypeFlag::SHORT:
894             case checker::TypeFlag::CHAR:
895             case checker::TypeFlag::INT: {
896                 Ra().Emit<IntCompare>(node, lhs, ifFalse);
897                 break;
898             }
899             default: {
900                 UNREACHABLE();
901             }
902         }
903 
904         SetAccumulatorType(Checker()->GlobalETSBooleanType());
905     }
906 
907     template <typename CompareGreater, typename CompareLess, typename CondCompare>
BinaryFloatingPointComparison(const ir::AstNode * node,VReg lhs,Label * ifFalse)908     void BinaryFloatingPointComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
909     {
910         if constexpr (std::is_same_v<CondCompare, Jgez> || std::is_same_v<CondCompare, Jgtz>) {
911             BinaryNumberComparison<CompareGreater, CondCompare>(node, lhs, ifFalse);
912         } else {
913             BinaryNumberComparison<CompareLess, CondCompare>(node, lhs, ifFalse);
914         }
915     }
916 
917     template <typename IntOp, typename LongOp, typename FloatOp, typename DoubleOp>
BinaryArithmetic(const ir::AstNode * node,VReg lhs)918     void BinaryArithmetic(const ir::AstNode *node, VReg lhs)
919     {
920         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
921 
922         switch (typeKind) {
923             case checker::TypeFlag::DOUBLE: {
924                 Ra().Emit<DoubleOp>(node, lhs);
925                 SetAccumulatorType(Checker()->GlobalDoubleType());
926                 break;
927             }
928             case checker::TypeFlag::FLOAT: {
929                 Ra().Emit<FloatOp>(node, lhs);
930                 SetAccumulatorType(Checker()->GlobalFloatType());
931                 break;
932             }
933             default: {
934                 BinaryBitwiseArithmetic<IntOp, LongOp>(node, lhs);
935             }
936         }
937     }
938 
939     template <typename IntOp, typename LongOp>
BinaryBitwiseArithmetic(const ir::AstNode * node,VReg lhs)940     void BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs)
941     {
942         auto typeKind = checker::ETSChecker::TypeKind(targetType_);
943 
944         switch (typeKind) {
945             case checker::TypeFlag::LONG: {
946                 Ra().Emit<LongOp>(node, lhs);
947                 SetAccumulatorType(Checker()->GlobalLongType());
948                 break;
949             }
950             case checker::TypeFlag::BYTE:
951             case checker::TypeFlag::SHORT:
952             case checker::TypeFlag::INT: {
953                 Ra().Emit<IntOp>(node, lhs);
954                 SetAccumulatorType(Checker()->GlobalIntType());
955                 break;
956             }
957             case checker::TypeFlag::ETS_BOOLEAN: {
958                 Ra().Emit<IntOp>(node, lhs);
959                 SetAccumulatorType(Checker()->GlobalETSBooleanType());
960                 break;
961             }
962             case checker::TypeFlag::CHAR: {
963                 Ra().Emit<IntOp>(node, lhs);
964                 SetAccumulatorType(Checker()->GlobalCharType());
965                 break;
966             }
967             default: {
968                 UNREACHABLE();
969             }
970         }
971     }
972 // NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty)
973 #define COMPILE_ARG(idx)                                                                                       \
974     ASSERT((idx) < arguments.size());                                                                          \
975     ASSERT((idx) < signature->Params().size() || signature->RestVar() != nullptr);                             \
976     auto *param##idx = (idx) < signature->Params().size() ? signature->Params()[(idx)] : signature->RestVar(); \
977     auto *paramType##idx = param##idx->TsType();                                                               \
978     auto ttctx##idx = TargetTypeContext(this, paramType##idx);                                                 \
979     arguments[idx]->Compile(this);                                                                             \
980     VReg arg##idx = AllocReg();                                                                                \
981     ApplyConversion(arguments[idx], nullptr);                                                                  \
982     ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
983 
984     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)985     void CallArgStart(const ir::AstNode *const node, const checker::Signature *signature, const VReg argStart,
986                       const ArenaVector<ir::Expression *> &arguments)
987     {
988         RegScope rs(this);
989         const auto name = signature->InternalName();
990 
991         switch (arguments.size()) {
992             case 0U: {
993                 Ra().Emit<Short, 1>(node, name, argStart, dummyReg_);
994                 break;
995             }
996             case 1U: {
997                 COMPILE_ARG(0);
998                 Ra().Emit<Short>(node, name, argStart, arg0);
999                 break;
1000             }
1001             case 2U: {
1002                 COMPILE_ARG(0);
1003                 COMPILE_ARG(1);
1004                 Ra().Emit<General, 3U>(node, name, argStart, arg0, arg1, dummyReg_);
1005                 break;
1006             }
1007             case 3U: {
1008                 COMPILE_ARG(0);
1009                 COMPILE_ARG(1);
1010                 COMPILE_ARG(2);
1011                 Ra().Emit<General>(node, name, argStart, arg0, arg1, arg2);
1012                 break;
1013             }
1014             default: {
1015                 for (size_t idx = 0; idx < arguments.size(); idx++) {
1016                     COMPILE_ARG(idx);
1017                 }
1018 
1019                 Rra().Emit<Range>(node, argStart, arguments.size() + 1, name, argStart);
1020                 break;
1021             }
1022         }
1023     }
1024 
1025     template <typename Short, typename General, typename Range>
CallImpl(const ir::AstNode * node,checker::Signature const * signature,const ArenaVector<ir::Expression * > & arguments)1026     void CallImpl(const ir::AstNode *node, checker::Signature const *signature,
1027                   const ArenaVector<ir::Expression *> &arguments)
1028     {
1029         ASSERT(signature != nullptr);
1030         RegScope rs(this);
1031 
1032         switch (arguments.size()) {
1033             case 0U: {
1034                 Ra().Emit<Short, 0U>(node, signature->InternalName(), dummyReg_, dummyReg_);
1035                 break;
1036             }
1037             case 1U: {
1038                 COMPILE_ARG(0);
1039                 Ra().Emit<Short, 1U>(node, signature->InternalName(), arg0, dummyReg_);
1040                 break;
1041             }
1042             case 2U: {
1043                 COMPILE_ARG(0);
1044                 COMPILE_ARG(1);
1045                 Ra().Emit<Short, 2U>(node, signature->InternalName(), arg0, arg1);
1046                 break;
1047             }
1048             case 3U: {
1049                 COMPILE_ARG(0);
1050                 COMPILE_ARG(1);
1051                 COMPILE_ARG(2);
1052                 Ra().Emit<General, 3U>(node, signature->InternalName(), arg0, arg1, arg2, dummyReg_);
1053                 break;
1054             }
1055             case 4U: {
1056                 COMPILE_ARG(0);
1057                 COMPILE_ARG(1);
1058                 COMPILE_ARG(2);
1059                 COMPILE_ARG(3);
1060                 Ra().Emit<General, 4U>(node, signature->InternalName(), arg0, arg1, arg2, arg3);
1061                 break;
1062             }
1063             default: {
1064                 VReg argStart = NextReg();
1065 
1066                 for (size_t idx = 0; idx < arguments.size(); idx++) {
1067                     COMPILE_ARG(idx);
1068                 }
1069 
1070                 Rra().Emit<Range>(node, argStart, arguments.size(), signature->InternalName(), argStart);
1071                 break;
1072             }
1073         }
1074     }
1075 #undef COMPILE_ARG
1076 
1077 #define COMPILE_ARG(idx, shift)                                                              \
1078     ASSERT((idx) < arguments.size());                                                        \
1079     ASSERT((idx) + (shift) < signature->Params().size() || signature->RestVar() != nullptr); \
1080     auto *paramType##idx = (idx) + (shift) < signature->Params().size()                      \
1081                                ? signature->Params()[(idx) + (shift)]->TsType()              \
1082                                : signature->RestVar()->TsType();                             \
1083     auto ttctx##idx = TargetTypeContext(this, paramType##idx);                               \
1084     VReg arg##idx = AllocReg();                                                              \
1085     arguments[idx]->Compile(this);                                                           \
1086     ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
1087 
1088     template <typename Short, typename General, typename Range>
CallDynamicImpl(CallDynamicData data,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)1089     void CallDynamicImpl(CallDynamicData data, checker::Signature *signature,
1090                          const ArenaVector<ir::Expression *> &arguments)
1091     {
1092         RegScope rs(this);
1093         const auto name = signature->InternalName();
1094 
1095         switch (arguments.size()) {
1096             case 0U: {
1097                 Ra().Emit<Short>(data.node, name, data.obj, data.param2);
1098                 break;
1099             }
1100             case 1U: {
1101                 COMPILE_ARG(0, 2U);
1102                 Ra().Emit<General, 3U>(data.node, name, data.obj, data.param2, arg0, dummyReg_);
1103                 break;
1104             }
1105             case 2U: {
1106                 COMPILE_ARG(0, 2U);
1107                 COMPILE_ARG(1, 2U);
1108                 Ra().Emit<General>(data.node, name, data.obj, data.param2, arg0, arg1);
1109                 break;
1110             }
1111             default: {
1112                 for (size_t idx = 0; idx < arguments.size(); idx++) {
1113                     COMPILE_ARG(idx, 2U);
1114                 }
1115 
1116                 Rra().Emit<Range>(data.node, data.obj, arguments.size() + 2U, name, data.obj);
1117                 break;
1118             }
1119         }
1120     }
1121 
1122     template <typename Short, typename General, typename Range>
CallDynamicImpl(CallDynamicData data,VReg param3,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)1123     void CallDynamicImpl(CallDynamicData data, VReg param3, checker::Signature *signature,
1124                          const ArenaVector<ir::Expression *> &arguments)
1125     {
1126         RegScope rs(this);
1127         const auto name = signature->InternalName();
1128 
1129         switch (arguments.size()) {
1130             case 0U: {
1131                 Ra().Emit<General, 3U>(data.node, name, data.obj, data.param2, param3, dummyReg_);
1132                 break;
1133             }
1134             case 1U: {
1135                 COMPILE_ARG(0, 3U);
1136                 Ra().Emit<General>(data.node, name, data.obj, data.param2, param3, arg0);
1137                 break;
1138             }
1139             default: {
1140                 for (size_t idx = 0; idx < arguments.size(); idx++) {
1141                     COMPILE_ARG(idx, 3U);
1142                 }
1143 
1144                 Rra().Emit<Range>(data.node, data.obj, arguments.size() + 3U, name, data.obj);
1145                 break;
1146             }
1147         }
1148     }
1149 
1150 #undef COMPILE_ARG
1151     // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty)
1152 
1153     void ToBinaryResult(const ir::AstNode *node, Label *ifFalse);
1154 
1155     template <typename T>
1156     void LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType);
1157     template <typename T>
1158     void SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number);
1159     void InitializeContainingClass();
1160 
1161     util::StringView FormDynamicModulePropReference(const varbinder::Variable *var);
1162     util::StringView FormDynamicModulePropReference(const ir::ETSImportDeclaration *import);
1163 
1164     friend class TargetTypeContext;
1165 
1166     VReg acc_ {};
1167     const checker::Type *targetType_ {};
1168     const checker::ETSObjectType *containingObjectType_ {};
1169 };
1170 
1171 template <typename T>
SetAccumulatorTargetType(const ir::AstNode * node,checker::TypeFlag typeKind,T number)1172 void ETSGen::SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number)
1173 {
1174     switch (typeKind) {
1175         case checker::TypeFlag::ETS_BOOLEAN:
1176         case checker::TypeFlag::BYTE: {
1177             Sa().Emit<Ldai>(node, static_cast<checker::ByteType::UType>(number));
1178             SetAccumulatorType(Checker()->GlobalByteType());
1179             break;
1180         }
1181         case checker::TypeFlag::CHAR: {
1182             Sa().Emit<Ldai>(node, static_cast<checker::CharType::UType>(number));
1183             SetAccumulatorType(Checker()->GlobalCharType());
1184             break;
1185         }
1186         case checker::TypeFlag::SHORT: {
1187             Sa().Emit<Ldai>(node, static_cast<checker::ShortType::UType>(number));
1188             SetAccumulatorType(Checker()->GlobalShortType());
1189             break;
1190         }
1191         case checker::TypeFlag::INT: {
1192             Sa().Emit<Ldai>(node, static_cast<checker::IntType::UType>(number));
1193             SetAccumulatorType(Checker()->GlobalIntType());
1194             break;
1195         }
1196         case checker::TypeFlag::LONG: {
1197             Sa().Emit<LdaiWide>(node, static_cast<checker::LongType::UType>(number));
1198             SetAccumulatorType(Checker()->GlobalLongType());
1199             break;
1200         }
1201         case checker::TypeFlag::FLOAT: {
1202             Sa().Emit<Fldai>(node, static_cast<checker::FloatType::UType>(number));
1203             SetAccumulatorType(Checker()->GlobalFloatType());
1204             break;
1205         }
1206         case checker::TypeFlag::DOUBLE: {
1207             Sa().Emit<FldaiWide>(node, static_cast<checker::DoubleType::UType>(number));
1208             SetAccumulatorType(Checker()->GlobalDoubleType());
1209             break;
1210         }
1211         case checker::TypeFlag::ETS_STRING_ENUM:
1212             [[fallthrough]];
1213         case checker::TypeFlag::ETS_INT_ENUM: {
1214             Sa().Emit<Ldai>(node, static_cast<checker::ETSEnumType::UType>(number));
1215             SetAccumulatorType(Checker()->GlobalIntType());
1216             break;
1217         }
1218         default: {
1219             UNREACHABLE();
1220         }
1221     }
1222 }
1223 
1224 template <typename T>
LoadAccumulatorNumber(const ir::AstNode * node,T number,checker::TypeFlag targetType)1225 void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType)
1226 {
1227     auto typeKind = targetType_ && (!targetType_->IsETSObjectType() && !targetType_->IsETSUnionType() &&
1228                                     !targetType_->IsETSArrayType())
1229                         ? checker::ETSChecker::TypeKind(targetType_)
1230                         : targetType;
1231 
1232     SetAccumulatorTargetType(node, typeKind, number);
1233 
1234     if (targetType_ && (targetType_->IsETSObjectType() || targetType_->IsETSUnionType())) {
1235         ApplyConversion(node, targetType_);
1236     }
1237 }
1238 
1239 }  // namespace ark::es2panda::compiler
1240 
1241 #endif
1242