• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef ES2PANDA_COMPILER_CORE_ETSGEN_H
17 #define ES2PANDA_COMPILER_CORE_ETSGEN_H
18 
19 #include "ir/astNode.h"
20 #include "compiler/core/codeGen.h"
21 #include "compiler/core/ETSfunction.h"
22 #include "compiler/core/targetTypeContext.h"
23 #include "checker/ETSchecker.h"
24 #include "util/helpers.h"
25 #include <variant>
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                              std::variant<util::StringView, const ark::es2panda::ir::Expression *> property);
76 
77     void StoreElementDynamic(const ir::AstNode *node, VReg objectReg, VReg index);
78     void LoadElementDynamic(const ir::AstNode *node, VReg objectReg);
79 
80     void StorePropertyByName(const ir::AstNode *node, VReg objReg,
81                              checker::ETSChecker::NamedAccessMeta const &fieldMeta);
82     void LoadPropertyByName(const ir::AstNode *node, VReg objReg,
83                             checker::ETSChecker::NamedAccessMeta const &fieldMeta);
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     void Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse);
107 
108     template <typename CondCompare, bool BEFORE_LOGICAL_NOT>
109     void ResolveConditionalResultFloat(const ir::AstNode *node, Label *realEndLabel);
110 
111     template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
112     void ResolveConditionalResultNumeric(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse, Label **end);
113 
114     template <typename CondCompare, bool BEFORE_LOGICAL_NOT>
115     void ResolveConditionalResultReference(const ir::AstNode *node);
116 
117     template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
118     void ResolveConditionalResult(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse);
119 
120     template <bool BEFORE_LOGICAL_NOT = false, bool FALSE_LABEL_EXISTED = true>
121     void ResolveConditionalResultIfFalse(const ir::AstNode *node, Label *ifFalse = nullptr);
122 
123     template <bool BEFORE_LOGICAL_NOT = false, bool FALSE_LABEL_EXISTED = true>
124     void ResolveConditionalResultIfTrue(const ir::AstNode *node, Label *ifFalse = nullptr);
125 
126     template <typename CondCompare, typename NegCondCompare>
127     void BranchConditional(const ir::AstNode *node, Label *endLabel);
128 
129     void ConditionalFloat(const ir::AstNode *node);
130 
131     void BranchConditionalIfFalse(const ir::AstNode *node, Label *endLabel);
132 
133     void BranchConditionalIfTrue(const ir::AstNode *node, Label *endLabel);
134 
BranchIfFalse(const ir::AstNode * node,Label * ifFalse)135     void BranchIfFalse(const ir::AstNode *node, Label *ifFalse)
136     {
137         Sa().Emit<Jeqz>(node, ifFalse);
138     }
139 
BranchIfTrue(const ir::AstNode * node,Label * ifTrue)140     void BranchIfTrue(const ir::AstNode *node, Label *ifTrue)
141     {
142         Sa().Emit<Jnez>(node, ifTrue);
143     }
144 
BranchIfUndefined(const ir::AstNode * node,Label * ifNull)145     void BranchIfUndefined(const ir::AstNode *node, Label *ifNull)
146     {
147         Sa().Emit<JeqzObj>(node, ifNull);
148     }
149 
BranchIfNotUndefined(const ir::AstNode * node,Label * ifNotNull)150     void BranchIfNotUndefined(const ir::AstNode *node, Label *ifNotNull)
151     {
152         Sa().Emit<JnezObj>(node, ifNotNull);
153     }
154 
BranchIfNull(const ir::AstNode * node,Label * ifTaken)155     void BranchIfNull(const ir::AstNode *node, Label *ifTaken)
156     {
157         EmitIsNull(node);
158         BranchIfTrue(node, ifTaken);
159     }
160 
BranchIfNotNull(const ir::AstNode * node,Label * ifTaken)161     void BranchIfNotNull(const ir::AstNode *node, Label *ifTaken)
162     {
163         EmitIsNull(node);
164         BranchIfFalse(node, ifTaken);
165     }
166 
167     void BranchIfNullish(const ir::AstNode *node, Label *ifNullish);
168     void BranchIfNotNullish(const ir::AstNode *node, Label *ifNotNullish);
169     void AssumeNonNullish(const ir::AstNode *node, checker::Type const *targetType);
170 
JumpTo(const ir::AstNode * node,Label * labelTo)171     void JumpTo(const ir::AstNode *node, Label *labelTo)
172     {
173         Sa().Emit<Jmp>(node, labelTo);
174     }
175 
EmitThrow(const ir::AstNode * node,VReg err)176     void EmitThrow(const ir::AstNode *node, VReg err)
177     {
178         Ra().Emit<Throw>(node, err);
179     }
180 
181     void EmitNullishException(const ir::AstNode *node);
182     void ThrowException(const ir::Expression *expr);
183     bool ExtendWithFinalizer(ir::AstNode const *node, const ir::AstNode *originalNode, Label *prevFinnaly = nullptr);
184 
185     void Negate(const ir::AstNode *node);
186     void LogicalNot(const ir::AstNode *node);
187 
188     void LoadAccumulatorByte(const ir::AstNode *node, int8_t number);
189 
190     void LoadAccumulatorShort(const ir::AstNode *node, int16_t number);
191 
192     void LoadAccumulatorInt(const ir::AstNode *node, int32_t number);
193 
194     void LoadAccumulatorWideInt(const ir::AstNode *node, int64_t number);
195 
196     void LoadAccumulatorFloat(const ir::AstNode *node, float number);
197 
198     void LoadAccumulatorDouble(const ir::AstNode *node, double number);
199 
200     void LoadAccumulatorBoolean(const ir::AstNode *node, bool value);
201 
202     void LoadAccumulatorString(const ir::AstNode *node, util::StringView str);
203 
204     void LoadAccumulatorBigInt(const ir::AstNode *node, util::StringView str);
205 
206     void LoadAccumulatorUndefined(const ir::AstNode *node);
207 
208     void LoadAccumulatorNull([[maybe_unused]] const ir::AstNode *node);
209 
210     void LoadAccumulatorPoison(const ir::AstNode *node, const checker::Type *type);
211 
212     void LoadAccumulatorChar(const ir::AstNode *node, char16_t value);
213 
214     void LoadAccumulatorDynamicModule(const ir::AstNode *node, const ir::ETSImportDeclaration *import);
215 
216     void ApplyBoxingConversion(const ir::AstNode *node);
217     void ApplyUnboxingConversion(const ir::AstNode *node);
ApplyConversion(const ir::AstNode * node)218     void ApplyConversion(const ir::AstNode *node)
219     {
220         if (targetType_ != nullptr) {
221             ApplyConversion(node, targetType_);
222         }
223     }
224     void ApplyConversionCast(const ir::AstNode *node, const checker::Type *targetType);
225     void ApplyConversion(const ir::AstNode *node, const checker::Type *targetType);
226     void ApplyCast(const ir::AstNode *node, const checker::Type *targetType);
227     void ApplyCastToBoxingFlags(const ir::AstNode *node, const ir::BoxingUnboxingFlags targetType);
228     void EmitBoxingConversion(ir::BoxingUnboxingFlags boxingFlag, const ir::AstNode *node);
229     void EmitBoxingConversion(const ir::AstNode *node);
230     void SwapBinaryOpArgs(const ir::AstNode *node, VReg lhs);
231     VReg MoveAccToReg(const ir::AstNode *node);
232 
233     void LoadResizableArrayLength(const ir::AstNode *node);
234     void LoadResizableArrayElement(const ir::AstNode *node, const VReg arrObj, const VReg arrIndex);
235     void LoadArrayLength(const ir::AstNode *node, VReg arrayReg);
236     void LoadArrayElement(const ir::AstNode *node, VReg objectReg);
237     void StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index, const checker::Type *elementType);
238 
239     util::StringView GetTupleMemberNameForIndex(std::size_t index) const;
240     void LoadTupleElement(const ir::AstNode *node, VReg objectReg, const checker::Type *elementType, std::size_t index);
241     void StoreTupleElement(const ir::AstNode *node, VReg objectReg, const checker::Type *elementType,
242                            std::size_t index);
243 
244     template <typename T>
245     void MoveImmediateToRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType, T const value);
246 
247     template <typename T>
248     void IncrementImmediateRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType,
249                                     T const value);
250 
251     template <typename IntCompare>
JumpCompareRegister(const ir::AstNode * node,VReg lhs,Label * ifFalse)252     void JumpCompareRegister(const ir::AstNode *node, VReg lhs, Label *ifFalse)
253     {
254         Ra().Emit<IntCompare>(node, lhs, ifFalse);
255     }
256 
257     void LoadStringLength(const ir::AstNode *node);
258     void LoadStringChar(const ir::AstNode *node, VReg stringObj, VReg charIndex);
259 
260     void FloatIsNaN(const ir::AstNode *node);
261     void DoubleIsNaN(const ir::AstNode *node);
262 
263     void CompileStatements(const ArenaVector<ir::Statement *> &statements);
264 
265     // Cast
266     void CastToBoolean(const ir::AstNode *node);
267     void CastToByte(const ir::AstNode *node);
268     void CastToChar(const ir::AstNode *node);
269     void CastToShort(const ir::AstNode *node);
270     void CastToDouble(const ir::AstNode *node);
271     void CastToFloat(const ir::AstNode *node);
272     void CastToLong(const ir::AstNode *node);
273     void CastToInt(const ir::AstNode *node);
274     void CastToString(const ir::AstNode *node);
275     void CastToDynamic(const ir::AstNode *node, const checker::ETSDynamicType *type);
276     void CastDynamicTo(const ir::AstNode *node, enum checker::TypeFlag typeFlag);
277     void CastToReftype(const ir::AstNode *node, const checker::Type *targetType, bool unchecked);
278     void CastDynamicToObject(const ir::AstNode *node, const checker::Type *targetType);
279 
280     void InternalIsInstance(const ir::AstNode *node, const checker::Type *target);
281     void InternalCheckCast(const ir::AstNode *node, const checker::Type *target);
282     void CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target);
283     void GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target);
284 
285     // Call, Construct
286     void NewArray(const ir::AstNode *node, VReg arr, VReg dim, const checker::Type *arrType);
287     void NewObject(const ir::AstNode *node, util::StringView name, VReg athis);
288     void BuildString(const ir::Expression *node);
289     void CallBigIntUnaryOperator(const ir::Expression *node, VReg arg, util::StringView signature);
290     void CallBigIntBinaryOperator(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature);
291     void CallBigIntBinaryComparison(const ir::Expression *node, VReg lhs, VReg rhs, util::StringView signature);
292     void BuildTemplateString(const ir::TemplateLiteral *node);
InitObject(const ir::AstNode * node,checker::Signature const * signature,const ArenaVector<ir::Expression * > & arguments)293     void InitObject(const ir::AstNode *node, checker::Signature const *signature,
294                     const ArenaVector<ir::Expression *> &arguments)
295     {
296         CallImpl<InitobjShort, Initobj, InitobjRange>(node, signature, arguments);
297     }
298 
IsDevirtualizedSignature(const checker::Signature * signature)299     bool IsDevirtualizedSignature(const checker::Signature *signature)
300     {
301         ES2PANDA_ASSERT(signature != nullptr && !signature->HasSignatureFlag(checker::SignatureFlags::STATIC));
302         return signature->HasSignatureFlag(checker::SignatureFlags::FINAL | checker::SignatureFlags::PRIVATE |
303                                            checker::SignatureFlags::CONSTRUCTOR);
304     }
305 
EmitEtsTypeof(const ir::AstNode * node,const VReg reg)306     void EmitEtsTypeof([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const VReg reg)
307     {
308 #ifdef PANDA_WITH_ETS
309         Ra().Emit<EtsTypeof>(node, reg);
310 #else
311         ES2PANDA_UNREACHABLE();
312 #endif  // PANDA_WITH_ETS
313     }
314 
EmitEtsIstrue(const ir::AstNode * node,const VReg reg)315     void EmitEtsIstrue([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const VReg reg)
316     {
317 #ifdef PANDA_WITH_ETS
318         Ra().Emit<EtsIstrue>(node, reg);
319 #else
320         ES2PANDA_UNREACHABLE();
321 #endif  // PANDA_WITH_ETS
322     }
323 
CallExact(const ir::AstNode * node,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)324     void CallExact(const ir::AstNode *node, checker::Signature *signature,
325                    const ArenaVector<ir::Expression *> &arguments)
326     {
327         CallImpl<CallShort, Call, CallRange>(node, signature, arguments);
328     }
329 
CallExact(const ir::AstNode * const node,const checker::Signature * signature,const VReg arg0,const ArenaVector<ir::Expression * > & arguments)330     void CallExact(const ir::AstNode *const node, const checker::Signature *signature, const VReg arg0,
331                    const ArenaVector<ir::Expression *> &arguments)
332     {
333         CallArgStart<CallShort, Call, CallRange>(node, signature, arg0, arguments);
334     }
335 
CallExact(const ir::AstNode * const node,const util::StringView name)336     void CallExact(const ir::AstNode *const node, const util::StringView name)
337     {
338         Ra().Emit<CallShort, 0>(node, name, dummyReg_, dummyReg_);
339     }
340 
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0)341     void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0)
342     {
343         Ra().Emit<CallShort, 1>(node, name, arg0, dummyReg_);
344     }
345 
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0,const VReg arg1)346     void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1)
347     {
348         Ra().Emit<CallShort>(node, name, arg0, arg1);
349     }
350 
CallExact(const ir::AstNode * const node,const util::StringView name,const VReg arg0,const VReg arg1,const VReg arg2)351     void CallExact(const ir::AstNode *const node, const util::StringView name, const VReg arg0, const VReg arg1,
352                    const VReg arg2)
353     {
354         Ra().Emit<Call, 3U>(node, name, arg0, arg1, arg2, dummyReg_);
355     }
356 
CallByName(const ir::AstNode * const node,const checker::Signature * signature,const VReg arg0,const ArenaVector<ir::Expression * > & arguments)357     void CallByName([[maybe_unused]] const ir::AstNode *const node,
358                     [[maybe_unused]] const checker::Signature *signature, [[maybe_unused]] const VReg arg0,
359                     [[maybe_unused]] const ArenaVector<ir::Expression *> &arguments)
360     {
361 #ifdef PANDA_WITH_ETS
362         CallArgStart<EtsCallNameShort, EtsCallName, EtsCallNameRange>(node, signature, arg0, arguments);
363 #else
364         ES2PANDA_UNREACHABLE();
365 #endif
366     }
367 
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis,const ArenaVector<ir::Expression * > & arguments)368     void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis,
369                      const ArenaVector<ir::Expression *> &arguments)
370     {
371         ES2PANDA_ASSERT(!signature->HasSignatureFlag(checker::SignatureFlags::STATIC));
372         ES2PANDA_ASSERT(!signature->Owner()->GetDeclNode()->IsFinal() || signature->IsFinal());
373         if (IsDevirtualizedSignature(signature)) {
374             CallArgStart<CallShort, Call, CallRange>(node, signature, athis, arguments);
375         } else {
376             CallArgStart<CallVirtShort, CallVirt, CallVirtRange>(node, signature, athis, arguments);
377         }
378     }
379 
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis)380     void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis)
381     {
382         if (IsDevirtualizedSignature(signature)) {
383             CallExact(node, signature->InternalName(), athis);
384         } else {
385             CallVirtual(node, signature->InternalName(), athis);
386         }
387     }
388 
CallVirtual(const ir::AstNode * const node,const checker::Signature * signature,const VReg athis,const VReg arg0)389     void CallVirtual(const ir::AstNode *const node, const checker::Signature *signature, const VReg athis,
390                      const VReg arg0)
391     {
392         if (IsDevirtualizedSignature(signature)) {
393             CallExact(node, signature->InternalName(), athis, arg0);
394         } else {
395             CallVirtual(node, signature->InternalName(), athis, arg0);
396         }
397     }
398 
CallVirtual(const ir::AstNode * const node,const util::StringView name,const VReg athis)399     void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis)
400     {
401         Ra().Emit<CallVirtShort, 1>(node, name, athis, dummyReg_);
402     }
403 
CallVirtual(const ir::AstNode * const node,const util::StringView name,const VReg athis,const VReg arg0)404     void CallVirtual(const ir::AstNode *const node, const util::StringView name, const VReg athis, const VReg arg0)
405     {
406         Ra().Emit<CallVirtShort>(node, name, athis, arg0);
407     }
408 
409     struct CallDynamicData {
410         const ir::AstNode *node = nullptr;
411         VReg obj;
412         VReg param2;
413     };
414 
CallDynamic(CallDynamicData data,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)415     void CallDynamic(CallDynamicData data, checker::Signature *signature,
416                      const ArenaVector<ir::Expression *> &arguments)
417     {
418         CallDynamicImpl<CallShort, Call, CallRange>(data, signature, arguments);
419     }
420 
CallDynamic(CallDynamicData data,VReg param3,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)421     void CallDynamic(CallDynamicData data, VReg param3, checker::Signature *signature,
422                      const ArenaVector<ir::Expression *> &arguments)
423     {
424         CallDynamicImpl<CallShort, Call, CallRange>(data, param3, signature, arguments);
425     }
426 
427     // until a lowering for implicit super is available
428     void CallRangeFillUndefined(const ir::AstNode *const node, checker::Signature *const signature, const VReg thisReg);
429 
430     void CreateBigIntObject(const ir::AstNode *node, VReg arg0,
431                             std::string_view signature = Signatures::BUILTIN_BIGINT_CTOR);
432 
GetType(const ir::AstNode * node,bool isEtsPrimitive)433     void GetType(const ir::AstNode *node, bool isEtsPrimitive)
434     {
435         if (isEtsPrimitive) {
436             // NOTE: SzD. LoadStaticProperty if ETS stdlib has static TYPE constants otherwise fallback to LdaType
437         } else {
438             ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
439             auto classRef = GetAccumulatorType()->AsETSObjectType()->AssemblerName();
440             Sa().Emit<LdaType>(node, classRef);
441         }
442     }
443 
444     ~ETSGen() override = default;
445     NO_COPY_SEMANTIC(ETSGen);
446     NO_MOVE_SEMANTIC(ETSGen);
447 
448 private:
449     const VReg dummyReg_ = VReg::RegStart();
450 
451     void EmitUnboxedCall(const ir::AstNode *node, std::string_view signatureFlag, const checker::Type *targetType,
452                          const checker::Type *boxedType);
453 
454     void LoadConstantObject(const ir::Expression *node, const checker::Type *type);
455     void StringBuilderAppend(const ir::AstNode *node, VReg builder);
456     void AppendString(const ir::Expression *binExpr, VReg builder);
457     void StringBuilder(const ir::Expression *left, const ir::Expression *right, VReg builder);
458     util::StringView FormClassPropReference(varbinder::Variable const *var);
459     void UnaryMinus(const ir::AstNode *node);
460     void UnaryTilde(const ir::AstNode *node);
461 
462     util::StringView ToAssemblerType(const es2panda::checker::Type *type) const;
463     void TestIsInstanceConstituent(const ir::AstNode *node, std::tuple<Label *, Label *> label,
464                                    checker::Type const *target, bool acceptNull);
465     void CheckedReferenceNarrowingObject(const ir::AstNode *node, const checker::Type *target);
466 
467     template <bool IS_SRTICT = false>
468     void HandleDefinitelyNullishEquality(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
469     template <bool IS_SRTICT = false>
470     void HandlePossiblyNullishEquality(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse, Label *ifTrue);
471 
EmitIsNull(const ir::AstNode * node)472     void EmitIsNull([[maybe_unused]] const ir::AstNode *node)
473     {
474 #ifdef PANDA_WITH_ETS
475         Sa().Emit<EtsIsnullvalue>(node);
476 #else
477         ES2PANDA_UNREACHABLE();
478 #endif  // PANDA_WITH_ETS
479     }
480 
EmitCheckCast(const ir::AstNode * node,util::StringView target)481     void EmitCheckCast(const ir::AstNode *node, util::StringView target)
482     {
483         if (target != Signatures::BUILTIN_OBJECT) {
484             Sa().Emit<Checkcast>(node, target);
485         }
486     }
487 
EmitIsInstance(const ir::AstNode * node,util::StringView target)488     void EmitIsInstance(const ir::AstNode *node, util::StringView target)
489     {
490         if (target != Signatures::BUILTIN_OBJECT) {
491             Sa().Emit<Isinstance>(node, target);
492         } else {
493             LoadAccumulatorBoolean(node, true);
494         }
495     }
496 
497     template <bool IS_SRTICT = false>
EmitEtsEquals(const ir::AstNode * node,const VReg lhs,const VReg rhs)498     void EmitEtsEquals([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] const VReg lhs,
499                        [[maybe_unused]] const VReg rhs)
500     {
501 #ifdef PANDA_WITH_ETS
502         if constexpr (IS_SRTICT) {
503             Ra().Emit<EtsStrictequals>(node, lhs, rhs);
504         } else {
505             Ra().Emit<EtsEquals>(node, lhs, rhs);
506         }
507 #else
508         ES2PANDA_UNREACHABLE();
509 #endif  // PANDA_WITH_ETS
510     }
511 
512     template <typename T>
StoreValueIntoArray(const ir::AstNode * const node,const VReg arr,const VReg index)513     void StoreValueIntoArray(const ir::AstNode *const node, const VReg arr, const VReg index)
514     {
515         Ra().Emit<T>(node, arr, index);
516     }
517 
518     template <typename LongOp, typename IntOp, typename DoubleOp, typename FloatOp>
519     void UpdateOperator(const ir::AstNode *node);
520 
521     template <typename Br>
522     void InverseCondition(const ir::AstNode *node, Br const &br, Label *target, bool inverse = true)
523     {
524         if (!inverse) {
525             br(target);
526             return;
527         }
528         Label *loc = AllocLabel();
529         br(loc);
530         JumpTo(node, target);
531         SetLabel(node, loc);
532     }
533 
534     template <bool IS_SRTICT = false>
535     void RefEqualityLoose(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
536     template <bool IS_SRTICT = false>
537     void RefEqualityLooseDynamic(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse);
538 
539     template <typename Compare, typename Cond>
BinaryNumberComparison(const ir::AstNode * node,VReg lhs,Label * ifFalse)540     void BinaryNumberComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
541     {
542         Ra().Emit<Compare>(node, lhs);
543         Sa().Emit<Cond>(node, ifFalse);
544     }
545 
546     template <typename IntCompare, typename CondCompare, typename DynCompare, bool IS_STRICT = false>
547     void BinaryEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse);
548 
549     template <typename IntCompare, typename CondCompare, bool IS_STRICT = false>
550     void BinaryEqualityCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse);
551 
552     template <typename IntCompare, typename CondCompare>
553     void BinaryRelation(const ir::AstNode *node, VReg lhs, Label *ifFalse);
554 
555     template <typename IntCompare, typename CondCompare>
556     void BinaryRelationCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse);
557 
558     template <typename CompareGreater, typename CompareLess, typename CondCompare>
BinaryFloatingPointComparison(const ir::AstNode * node,VReg lhs,Label * ifFalse)559     void BinaryFloatingPointComparison(const ir::AstNode *node, VReg lhs, Label *ifFalse)
560     {
561         if constexpr (std::is_same_v<CondCompare, Jgez> || std::is_same_v<CondCompare, Jgtz>) {
562             BinaryNumberComparison<CompareGreater, CondCompare>(node, lhs, ifFalse);
563         } else {
564             BinaryNumberComparison<CompareLess, CondCompare>(node, lhs, ifFalse);
565         }
566     }
567 
568     template <typename IntOp, typename LongOp, typename FloatOp, typename DoubleOp>
569     void BinaryArithmetic(const ir::AstNode *node, VReg lhs);
570 
571     template <typename IntOp, typename LongOp>
572     void BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs);
573 
574 // NOLINTBEGIN(cppcoreguidelines-macro-usage, readability-container-size-empty)
575 #define COMPILE_ARG(idx)                                                                                       \
576     ES2PANDA_ASSERT((idx) < arguments.size());                                                                 \
577     ES2PANDA_ASSERT((idx) < signature->Params().size() || signature->RestVar() != nullptr);                    \
578     auto *param##idx = (idx) < signature->Params().size() ? signature->Params()[(idx)] : signature->RestVar(); \
579     auto *paramType##idx = param##idx->TsType();                                                               \
580     auto ttctx##idx = TargetTypeContext(this, paramType##idx);                                                 \
581     arguments[idx]->Compile(this);                                                                             \
582     VReg arg##idx = AllocReg();                                                                                \
583     ApplyConversion(arguments[idx], nullptr);                                                                  \
584     ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
585 
586     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)587     void CallArgStart(const ir::AstNode *const node, const checker::Signature *signature, const VReg argStart,
588                       const ArenaVector<ir::Expression *> &arguments)
589     {
590         RegScope rs(this);
591         const auto name = signature->InternalName();
592 
593         switch (arguments.size()) {
594             case 0U: {
595                 Ra().Emit<Short, 1>(node, name, argStart, dummyReg_);
596                 break;
597             }
598             case 1U: {
599                 COMPILE_ARG(0);
600                 Ra().Emit<Short>(node, name, argStart, arg0);
601                 break;
602             }
603             case 2U: {
604                 COMPILE_ARG(0);
605                 COMPILE_ARG(1);
606                 Ra().Emit<General, 3U>(node, name, argStart, arg0, arg1, dummyReg_);
607                 break;
608             }
609             case 3U: {
610                 COMPILE_ARG(0);
611                 COMPILE_ARG(1);
612                 COMPILE_ARG(2);
613                 Ra().Emit<General>(node, name, argStart, arg0, arg1, arg2);
614                 break;
615             }
616             default: {
617                 for (size_t idx = 0; idx < arguments.size(); idx++) {
618                     COMPILE_ARG(idx);
619                 }
620 
621                 Rra().Emit<Range>(node, argStart, arguments.size() + 1, name, argStart);
622                 break;
623             }
624         }
625     }
626 
627     template <typename Short, typename General, typename Range>
CallImpl(const ir::AstNode * node,checker::Signature const * signature,const ArenaVector<ir::Expression * > & arguments)628     void CallImpl(const ir::AstNode *node, checker::Signature const *signature,
629                   const ArenaVector<ir::Expression *> &arguments)
630     {
631         ES2PANDA_ASSERT(signature != nullptr);
632         RegScope rs(this);
633 
634         switch (arguments.size()) {
635             case 0U: {
636                 Ra().Emit<Short, 0U>(node, signature->InternalName(), dummyReg_, dummyReg_);
637                 break;
638             }
639             case 1U: {
640                 COMPILE_ARG(0);
641                 Ra().Emit<Short, 1U>(node, signature->InternalName(), arg0, dummyReg_);
642                 break;
643             }
644             case 2U: {
645                 COMPILE_ARG(0);
646                 COMPILE_ARG(1);
647                 Ra().Emit<Short, 2U>(node, signature->InternalName(), arg0, arg1);
648                 break;
649             }
650             case 3U: {
651                 COMPILE_ARG(0);
652                 COMPILE_ARG(1);
653                 COMPILE_ARG(2);
654                 Ra().Emit<General, 3U>(node, signature->InternalName(), arg0, arg1, arg2, dummyReg_);
655                 break;
656             }
657             case 4U: {
658                 COMPILE_ARG(0);
659                 COMPILE_ARG(1);
660                 COMPILE_ARG(2);
661                 COMPILE_ARG(3);
662                 Ra().Emit<General, 4U>(node, signature->InternalName(), arg0, arg1, arg2, arg3);
663                 break;
664             }
665             default: {
666                 VReg argStart = NextReg();
667 
668                 for (size_t idx = 0; idx < arguments.size(); idx++) {
669                     COMPILE_ARG(idx);
670                 }
671 
672                 Rra().Emit<Range>(node, argStart, arguments.size(), signature->InternalName(), argStart);
673                 break;
674             }
675         }
676     }
677 #undef COMPILE_ARG
678 
679 #define COMPILE_ARG(idx, shift)                                                                       \
680     ES2PANDA_ASSERT((idx) < arguments.size());                                                        \
681     ES2PANDA_ASSERT((idx) + (shift) < signature->Params().size() || signature->RestVar() != nullptr); \
682     auto *paramType##idx = (idx) + (shift) < signature->Params().size()                               \
683                                ? signature->Params()[(idx) + (shift)]->TsType()                       \
684                                : signature->RestVar()->TsType();                                      \
685     auto ttctx##idx = TargetTypeContext(this, paramType##idx);                                        \
686     VReg arg##idx = AllocReg();                                                                       \
687     arguments[idx]->Compile(this);                                                                    \
688     ApplyConversionAndStoreAccumulator(arguments[idx], arg##idx, paramType##idx)
689 
690     template <typename Short, typename General, typename Range>
CallDynamicImpl(CallDynamicData data,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)691     void CallDynamicImpl(CallDynamicData data, checker::Signature *signature,
692                          const ArenaVector<ir::Expression *> &arguments)
693     {
694         RegScope rs(this);
695         const auto name = signature->InternalName();
696 
697         switch (arguments.size()) {
698             case 0U: {
699                 Ra().Emit<Short>(data.node, name, data.obj, data.param2);
700                 break;
701             }
702             case 1U: {
703                 COMPILE_ARG(0, 2U);
704                 Ra().Emit<General, 3U>(data.node, name, data.obj, data.param2, arg0, dummyReg_);
705                 break;
706             }
707             case 2U: {
708                 COMPILE_ARG(0, 2U);
709                 COMPILE_ARG(1, 2U);
710                 Ra().Emit<General>(data.node, name, data.obj, data.param2, arg0, arg1);
711                 break;
712             }
713             default: {
714                 for (size_t idx = 0; idx < arguments.size(); idx++) {
715                     COMPILE_ARG(idx, 2U);
716                 }
717 
718                 Rra().Emit<Range>(data.node, data.obj, arguments.size() + 2U, name, data.obj);
719                 break;
720             }
721         }
722     }
723 
724     template <typename Short, typename General, typename Range>
CallDynamicImpl(CallDynamicData data,VReg param3,checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)725     void CallDynamicImpl(CallDynamicData data, VReg param3, checker::Signature *signature,
726                          const ArenaVector<ir::Expression *> &arguments)
727     {
728         RegScope rs(this);
729         const auto name = signature->InternalName();
730 
731         switch (arguments.size()) {
732             case 0U: {
733                 Ra().Emit<General, 3U>(data.node, name, data.obj, data.param2, param3, dummyReg_);
734                 break;
735             }
736             case 1U: {
737                 COMPILE_ARG(0, 3U);
738                 Ra().Emit<General>(data.node, name, data.obj, data.param2, param3, arg0);
739                 break;
740             }
741             default: {
742                 for (size_t idx = 0; idx < arguments.size(); idx++) {
743                     COMPILE_ARG(idx, 3U);
744                 }
745 
746                 Rra().Emit<Range>(data.node, data.obj, arguments.size() + 3U, name, data.obj);
747                 break;
748             }
749         }
750     }
751 
752 #undef COMPILE_ARG
753     // NOLINTEND(cppcoreguidelines-macro-usage, readability-container-size-empty)
754 
755     void ToBinaryResult(const ir::AstNode *node, Label *ifFalse);
756 
757     template <typename T>
758     void LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType);
759     template <typename T>
760     void SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number);
761     void InitializeContainingClass();
762 
763     util::StringView FormDynamicModulePropReference(const varbinder::Variable *var);
764     util::StringView FormDynamicModulePropReference(const ir::ETSImportDeclaration *import);
765 
766     friend class TargetTypeContext;
767 
768     VReg acc_ {};
769     const checker::Type *targetType_ {};
770     const checker::ETSObjectType *containingObjectType_ {};
771 };
772 
773 }  // namespace ark::es2panda::compiler
774 
775 #endif
776