• 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 #include "ETSGen.h"
17 #include "ETSGen-inl.h"
18 
19 #include "generated/signatures.h"
20 #include "ir/base/scriptFunction.h"
21 #include "ir/base/classDefinition.h"
22 #include "ir/statement.h"
23 #include "ir/expressions/assignmentExpression.h"
24 #include "ir/expressions/identifier.h"
25 #include "ir/expressions/binaryExpression.h"
26 #include "ir/expressions/callExpression.h"
27 #include "ir/expressions/memberExpression.h"
28 #include "ir/expressions/templateLiteral.h"
29 #include "ir/statements/breakStatement.h"
30 #include "ir/statements/continueStatement.h"
31 #include "ir/statements/tryStatement.h"
32 #include "ir/ts/tsInterfaceDeclaration.h"
33 #include "varbinder/variableFlags.h"
34 #include "compiler/base/lreference.h"
35 #include "compiler/base/catchTable.h"
36 #include "compiler/core/dynamicContext.h"
37 #include "varbinder/ETSBinder.h"
38 #include "varbinder/variable.h"
39 #include "checker/types/type.h"
40 #include "checker/types/typeFlag.h"
41 #include "checker/checker.h"
42 #include "checker/ETSchecker.h"
43 #include "checker/types/ets/etsObjectType.h"
44 #include "checker/types/ets/etsTupleType.h"
45 #include "checker/types/ets/etsAsyncFuncReturnType.h"
46 #include "parser/program/program.h"
47 #include "checker/types/globalTypesHolder.h"
48 #include "public/public.h"
49 
50 namespace ark::es2panda::compiler {
51 
IsWidePrimitiveType(checker::Type const * type)52 static inline bool IsWidePrimitiveType(checker::Type const *type)
53 {
54     return type->IsLongType() || type->IsDoubleType();
55 }
56 
ETSGen(ArenaAllocator * allocator,RegSpiller * spiller,public_lib::Context * context,std::tuple<varbinder::FunctionScope *,ProgramElement *,AstCompiler * > toCompile)57 ETSGen::ETSGen(ArenaAllocator *allocator, RegSpiller *spiller, public_lib::Context *context,
58                std::tuple<varbinder::FunctionScope *, ProgramElement *, AstCompiler *> toCompile) noexcept
59     : CodeGen(allocator, spiller, context, toCompile),
60       containingObjectType_(util::Helpers::GetContainingObjectType(RootNode()))
61 {
62     ETSFunction::Compile(this);
63 }
64 
SetAccumulatorType(const checker::Type * type)65 void ETSGen::SetAccumulatorType(const checker::Type *type)
66 {
67     SetVRegType(acc_, type);
68 }
69 
GetAccumulatorType() const70 const checker::Type *ETSGen::GetAccumulatorType() const
71 {
72     return GetVRegType(acc_);
73 }
74 
CompileAndCheck(const ir::Expression * expr)75 void ETSGen::CompileAndCheck(const ir::Expression *expr)
76 {
77     // NOTE: vpukhov. bad accumulator type leads to terrible bugs in codegen
78     // make exact types match mandatory
79     expr->Compile(this);
80 
81     auto const *const accType = GetAccumulatorType();
82     auto const *const exprType = expr->TsType();
83 
84     if (Checker()->Relation()->IsIdenticalTo(accType, exprType) || exprType->IsETSTypeParameter() ||
85         exprType->IsETSPartialTypeParameter() || exprType->IsETSNonNullishType()) {
86         return;
87     }
88 
89     ES2PANDA_ASSERT(accType != nullptr);
90     if (accType->IsETSPrimitiveType() &&
91         ((accType->TypeFlags() ^ exprType->TypeFlags()) & ~checker::TypeFlag::CONSTANT) == 0) {
92         return;
93     }
94 
95     ASSERT_PRINT(false, std::string("Type mismatch after Expression::Compile: ") + accType->ToString() +
96                             " instead of " + exprType->ToString());
97 }
98 
Checker() const99 const checker::ETSChecker *ETSGen::Checker() const noexcept
100 {
101     return Context()->checker->AsETSChecker();
102 }
103 
VarBinder() const104 const varbinder::ETSBinder *ETSGen::VarBinder() const noexcept
105 {
106     return Context()->parserProgram->VarBinder()->AsETSBinder();
107 }
108 
ReturnType() const109 const checker::Type *ETSGen::ReturnType() const noexcept
110 {
111     return RootNode()->AsScriptFunction()->Signature()->ReturnType();
112 }
113 
ContainingObjectType() const114 const checker::ETSObjectType *ETSGen::ContainingObjectType() const noexcept
115 {
116     return containingObjectType_;
117 }
118 
Acc()119 VReg &ETSGen::Acc() noexcept
120 {
121     return acc_;
122 }
123 
Acc() const124 VReg ETSGen::Acc() const noexcept
125 {
126     return acc_;
127 }
128 
ApplyConversionAndStoreAccumulator(const ir::AstNode * const node,const VReg vreg,const checker::Type * const targetType)129 void ETSGen::ApplyConversionAndStoreAccumulator(const ir::AstNode *const node, const VReg vreg,
130                                                 const checker::Type *const targetType)
131 {
132     ApplyConversion(node, targetType);
133     StoreAccumulator(node, vreg);
134 }
135 
StoreException(const ir::AstNode * node)136 VReg ETSGen::StoreException(const ir::AstNode *node)
137 {
138     VReg exception = AllocReg();
139     Ra().Emit<StaObj>(node, exception);
140 
141     SetAccumulatorType(Checker()->GlobalBuiltinExceptionType());
142     SetVRegType(exception, GetAccumulatorType());
143     return exception;
144 }
145 
StoreAccumulator(const ir::AstNode * const node,const VReg vreg)146 void ETSGen::StoreAccumulator(const ir::AstNode *const node, const VReg vreg)
147 {
148     const auto *const accType = GetAccumulatorType();
149 
150     ES2PANDA_ASSERT(accType != nullptr);
151     if (accType->IsETSReferenceType()) {
152         Ra().Emit<StaObj>(node, vreg);
153     } else if (IsWidePrimitiveType(accType)) {
154         Ra().Emit<StaWide>(node, vreg);
155     } else {
156         Ra().Emit<Sta>(node, vreg);
157     }
158 
159     SetVRegType(vreg, accType);
160 }
161 
LoadAccumulator(const ir::AstNode * node,VReg vreg)162 void ETSGen::LoadAccumulator(const ir::AstNode *node, VReg vreg)
163 {
164     const auto *const vregType = GetVRegType(vreg);
165 
166     ES2PANDA_ASSERT(vregType != nullptr);
167     if (vregType->IsETSReferenceType()) {
168         Ra().Emit<LdaObj>(node, vreg);
169     } else if (IsWidePrimitiveType(vregType)) {
170         Ra().Emit<LdaWide>(node, vreg);
171     } else {
172         Ra().Emit<Lda>(node, vreg);
173     }
174 
175     SetAccumulatorType(vregType);
176 }
177 
AllocMov(const ir::AstNode * const node,const VReg vd,const VReg vs)178 IRNode *ETSGen::AllocMov(const ir::AstNode *const node, const VReg vd, const VReg vs)
179 {
180     const auto *const sourceType = GetVRegType(vs);
181     // CC-OFFNXT(G.FMT.14-CPP) project code style
182     auto *const mov = [this, sourceType, node, vd, vs]() -> IRNode * {
183         if (sourceType->IsETSReferenceType()) {
184             return Allocator()->New<MovObj>(node, vd, vs);
185         }
186         if (IsWidePrimitiveType(sourceType)) {
187             return Allocator()->New<MovWide>(node, vd, vs);
188         }
189         return Allocator()->New<Mov>(node, vd, vs);
190     }();
191 
192     SetVRegType(vd, sourceType);
193     return mov;
194 }
195 
AllocMov(const ir::AstNode * const node,OutVReg vd,const VReg vs)196 IRNode *ETSGen::AllocMov(const ir::AstNode *const node, OutVReg vd, const VReg vs)
197 {
198     ES2PANDA_ASSERT(vd.type != OperandType::ANY && vd.type != OperandType::NONE);
199 
200     switch (vd.type) {
201         case OperandType::REF:
202             return Allocator()->New<MovObj>(node, *vd.reg, vs);
203         case OperandType::B64:
204             return Allocator()->New<MovWide>(node, *vd.reg, vs);
205         default:
206             break;
207     }
208 
209     return Allocator()->New<Mov>(node, *vd.reg, vs);
210 }
211 
TypeForVar(varbinder::Variable const * var) const212 checker::Type const *ETSGen::TypeForVar(varbinder::Variable const *var) const noexcept
213 {
214     return var->TsType();
215 }
216 
MoveVreg(const ir::AstNode * const node,const VReg vd,const VReg vs)217 void ETSGen::MoveVreg(const ir::AstNode *const node, const VReg vd, const VReg vs)
218 {
219     const auto *const sourceType = GetVRegType(vs);
220     ES2PANDA_ASSERT(sourceType != nullptr);
221 
222     if (sourceType->IsETSReferenceType()) {
223         Ra().Emit<MovObj>(node, vd, vs);
224     } else if (IsWidePrimitiveType(sourceType)) {
225         Ra().Emit<MovWide>(node, vd, vs);
226     } else {
227         Ra().Emit<Mov>(node, vd, vs);
228     }
229 
230     SetVRegType(vd, sourceType);
231 }
232 
233 // indicates that initializer is meaningless and may lead to NPEs
LoadAccumulatorPoison(const ir::AstNode * node,const checker::Type * type)234 void ETSGen::LoadAccumulatorPoison(const ir::AstNode *node, const checker::Type *type)
235 {
236     ES2PANDA_ASSERT(type->IsETSReferenceType());
237     LoadAccumulatorUndefined(node);
238     SetAccumulatorType(type);
239 }
240 
FormDynamicModulePropReference(const varbinder::Variable * var)241 util::StringView ETSGen::FormDynamicModulePropReference(const varbinder::Variable *var)
242 {
243     ES2PANDA_ASSERT(VarBinder()->IsDynamicModuleVariable(var) || VarBinder()->IsDynamicNamespaceVariable(var));
244 
245     auto *data = VarBinder()->DynamicImportDataForVar(var);
246     ES2PANDA_ASSERT(data != nullptr);
247 
248     auto *import = data->import;
249 
250     return FormDynamicModulePropReference(import);
251 }
252 
LoadAccumulatorDynamicModule(const ir::AstNode * node,const ir::ETSImportDeclaration * import)253 void ETSGen::LoadAccumulatorDynamicModule(const ir::AstNode *node, const ir::ETSImportDeclaration *import)
254 {
255     ES2PANDA_ASSERT(import->Language().IsDynamic());
256     LoadStaticProperty(node, Checker()->GlobalBuiltinDynamicType(import->Language()),
257                        FormDynamicModulePropReference(import));
258 }
259 
FormDynamicModulePropReference(const ir::ETSImportDeclaration * import)260 util::StringView ETSGen::FormDynamicModulePropReference(const ir::ETSImportDeclaration *import)
261 {
262     std::stringstream ss;
263     ss << VarBinder()->Program()->ModulePrefix();
264     ss << compiler::Signatures::DYNAMIC_MODULE_CLASS;
265     ss << '.';
266     ss << import->AssemblerName();
267     return util::UString(ss.str(), Allocator()).View();
268 }
269 
LoadDynamicModuleVariable(const ir::AstNode * node,varbinder::Variable const * const var)270 void ETSGen::LoadDynamicModuleVariable(const ir::AstNode *node, varbinder::Variable const *const var)
271 {
272     RegScope rs(this);
273 
274     auto *data = VarBinder()->DynamicImportDataForVar(var);
275     ES2PANDA_ASSERT(data != nullptr);
276 
277     auto *import = data->import;
278 
279     LoadStaticProperty(node, var->TsType(), FormDynamicModulePropReference(var));
280 
281     auto objReg = AllocReg();
282     StoreAccumulator(node, objReg);
283 
284     auto *id = data->specifier->AsImportSpecifier()->Imported();
285     auto lang = import->Language();
286     LoadPropertyDynamic(node, Checker()->GlobalBuiltinDynamicType(lang), objReg, id->Name());
287 
288     ApplyConversion(node);
289 }
290 
LoadDynamicNamespaceVariable(const ir::AstNode * node,varbinder::Variable const * const var)291 void ETSGen::LoadDynamicNamespaceVariable(const ir::AstNode *node, varbinder::Variable const *const var)
292 {
293     LoadStaticProperty(node, var->TsType(), FormDynamicModulePropReference(var));
294 }
295 
LoadVar(const ir::Identifier * node,varbinder::Variable const * const var)296 void ETSGen::LoadVar(const ir::Identifier *node, varbinder::Variable const *const var)
297 {
298     if (VarBinder()->IsDynamicModuleVariable(var)) {
299         LoadDynamicModuleVariable(node, var);
300         return;
301     }
302 
303     if (VarBinder()->IsDynamicNamespaceVariable(var)) {
304         LoadDynamicNamespaceVariable(node, var);
305         return;
306     }
307 
308     auto *local = var->AsLocalVariable();
309 
310     switch (ETSLReference::ResolveReferenceKind(var)) {
311         case ReferenceKind::STATIC_FIELD: {
312             auto fullName = FormClassPropReference(var);
313             LoadStaticProperty(node, var->TsType(), fullName);
314             break;
315         }
316         case ReferenceKind::FIELD: {
317             ES2PANDA_ASSERT(GetVRegType(GetThisReg()) != nullptr);
318             const auto fullName = FormClassPropReference(GetVRegType(GetThisReg())->AsETSObjectType(), var->Name());
319             LoadProperty(node, var->TsType(), GetThisReg(), fullName);
320             break;
321         }
322         case ReferenceKind::METHOD:
323         case ReferenceKind::STATIC_METHOD:
324         case ReferenceKind::CLASS:
325         case ReferenceKind::STATIC_CLASS: {
326             SetAccumulatorType(var->TsType());
327             break;
328         }
329         case ReferenceKind::LOCAL: {
330             LoadAccumulator(node, local->Vreg());
331             SetAccumulatorType(GetVRegType(local->Vreg()));
332             break;
333         }
334         default: {
335             ES2PANDA_UNREACHABLE();
336         }
337     }
338 }
339 
StoreVar(const ir::Identifier * node,const varbinder::ConstScopeFindResult & result)340 void ETSGen::StoreVar(const ir::Identifier *node, const varbinder::ConstScopeFindResult &result)
341 {
342     auto *local = result.variable->AsLocalVariable();
343     ApplyConversion(node, local->TsType());
344 
345     switch (ETSLReference::ResolveReferenceKind(result.variable)) {
346         case ReferenceKind::STATIC_FIELD: {
347             auto fullName = FormClassPropReference(result.variable);
348             StoreStaticProperty(node, result.variable->TsType(), fullName);
349             break;
350         }
351         case ReferenceKind::FIELD: {
352             StoreProperty(node, result.variable->TsType(), GetThisReg(), result.name);
353             break;
354         }
355         case ReferenceKind::LOCAL: {
356             StoreAccumulator(node, local->Vreg());
357             SetVRegType(local->Vreg(), GetAccumulatorType());
358             break;
359         }
360         default: {
361             ES2PANDA_UNREACHABLE();
362         }
363     }
364 }
365 
FormClassPropReference(const checker::ETSObjectType * classType,const util::StringView & name)366 util::StringView ETSGen::FormClassPropReference(const checker::ETSObjectType *classType, const util::StringView &name)
367 {
368     std::stringstream ss;
369     ES2PANDA_ASSERT(classType != nullptr);
370     ss << classType->AssemblerName().Mutf8() << Signatures::METHOD_SEPARATOR << name;
371     return util::StringView(*ProgElement()->Strings().emplace(ss.str()).first);
372 }
373 
FormClassPropReference(varbinder::Variable const * const var)374 util::StringView ETSGen::FormClassPropReference(varbinder::Variable const *const var)
375 {
376     auto containingObjectType = util::Helpers::GetContainingObjectType(var->Declaration()->Node());
377     return FormClassPropReference(containingObjectType, var->Name());
378 }
379 
StoreStaticOwnProperty(const ir::AstNode * node,const checker::Type * propType,const util::StringView & name)380 void ETSGen::StoreStaticOwnProperty(const ir::AstNode *node, const checker::Type *propType,
381                                     const util::StringView &name)
382 {
383     util::StringView fullName = FormClassPropReference(containingObjectType_, name);
384     StoreStaticProperty(node, propType, fullName);
385 }
386 
StoreStaticProperty(const ir::AstNode * const node,const checker::Type * propType,const util::StringView & fullName)387 void ETSGen::StoreStaticProperty(const ir::AstNode *const node, const checker::Type *propType,
388                                  const util::StringView &fullName)
389 {
390     if (propType->IsETSReferenceType()) {
391         Sa().Emit<StstaticObj>(node, fullName);
392     } else if (IsWidePrimitiveType(propType)) {
393         Sa().Emit<StstaticWide>(node, fullName);
394     } else {
395         Sa().Emit<Ststatic>(node, fullName);
396     }
397 }
398 
LoadStaticProperty(const ir::AstNode * const node,const checker::Type * propType,const util::StringView & fullName)399 void ETSGen::LoadStaticProperty(const ir::AstNode *const node, const checker::Type *propType,
400                                 const util::StringView &fullName)
401 {
402     ES2PANDA_ASSERT(propType != nullptr);
403     if (propType->IsETSReferenceType()) {
404         Sa().Emit<LdstaticObj>(node, fullName);
405     } else if (IsWidePrimitiveType(propType)) {
406         Sa().Emit<LdstaticWide>(node, fullName);
407     } else {
408         Sa().Emit<Ldstatic>(node, fullName);
409     }
410 
411     SetAccumulatorType(propType);
412 }
413 
StoreProperty(const ir::AstNode * const node,const checker::Type * propType,const VReg objReg,const util::StringView & name)414 void ETSGen::StoreProperty(const ir::AstNode *const node, const checker::Type *propType, const VReg objReg,
415                            const util::StringView &name)
416 {
417     ES2PANDA_ASSERT(Checker()->GetApparentType(GetVRegType(objReg)) != nullptr);
418     auto *objType = Checker()->GetApparentType(GetVRegType(objReg))->AsETSObjectType();
419     const auto fullName = FormClassPropReference(objType, name);
420 
421     if (propType->IsETSReferenceType()) {
422         Ra().Emit<StobjObj>(node, objReg, fullName);
423     } else if (IsWidePrimitiveType(propType)) {
424         Ra().Emit<StobjWide>(node, objReg, fullName);
425     } else {
426         Ra().Emit<Stobj>(node, objReg, fullName);
427     }
428 }
429 
LoadProperty(const ir::AstNode * const node,const checker::Type * propType,const VReg objReg,const util::StringView & fullName)430 void ETSGen::LoadProperty(const ir::AstNode *const node, const checker::Type *propType, const VReg objReg,
431                           const util::StringView &fullName)
432 {
433     if (propType->IsETSReferenceType()) {
434         Ra().Emit<LdobjObj>(node, objReg, fullName);
435     } else if (IsWidePrimitiveType(propType)) {
436         Ra().Emit<LdobjWide>(node, objReg, fullName);
437     } else {
438         Ra().Emit<Ldobj>(node, objReg, fullName);
439     }
440 
441     SetAccumulatorType(propType);
442 }
443 
StorePropertyByName(const ir::AstNode * node,VReg objReg,checker::ETSChecker::NamedAccessMeta const & fieldMeta)444 void ETSGen::StorePropertyByName([[maybe_unused]] const ir::AstNode *node, [[maybe_unused]] VReg objReg,
445                                  [[maybe_unused]] checker::ETSChecker::NamedAccessMeta const &fieldMeta)
446 {
447 #ifdef PANDA_WITH_ETS
448     auto [metaObj, propType, propName] = fieldMeta;
449     const auto fullName = FormClassPropReference(metaObj, propName);
450 
451     if (propType->IsETSReferenceType()) {
452         Ra().Emit<EtsStobjNameObj>(node, objReg, fullName);
453     } else if (IsWidePrimitiveType(propType)) {
454         Ra().Emit<EtsStobjNameWide>(node, objReg, fullName);
455     } else {
456         Ra().Emit<EtsStobjName>(node, objReg, fullName);
457     }
458 #else
459     ES2PANDA_UNREACHABLE();
460 #endif  // PANDA_WITH_ETS
461 }
462 
LoadPropertyByName(const ir::AstNode * const node,VReg objReg,checker::ETSChecker::NamedAccessMeta const & fieldMeta)463 void ETSGen::LoadPropertyByName([[maybe_unused]] const ir::AstNode *const node, [[maybe_unused]] VReg objReg,
464                                 [[maybe_unused]] checker::ETSChecker::NamedAccessMeta const &fieldMeta)
465 {
466 #ifdef PANDA_WITH_ETS
467     auto [metaObj, propType, propName] = fieldMeta;
468     const auto fullName = FormClassPropReference(metaObj, propName);
469 
470     if (propType->IsETSReferenceType()) {
471         Ra().Emit<EtsLdobjNameObj>(node, objReg, fullName);
472     } else if (IsWidePrimitiveType(propType)) {
473         Ra().Emit<EtsLdobjNameWide>(node, objReg, fullName);
474     } else {
475         Ra().Emit<EtsLdobjName>(node, objReg, fullName);
476     }
477     SetAccumulatorType(propType);
478 #else
479     ES2PANDA_UNREACHABLE();
480 #endif  // PANDA_WITH_ETS
481 }
482 
StorePropertyDynamic(const ir::AstNode * node,const checker::Type * propType,VReg objReg,const util::StringView & propName)483 void ETSGen::StorePropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
484                                   const util::StringView &propName)
485 {
486     ES2PANDA_ASSERT(GetVRegType(objReg) != nullptr);
487     auto const lang = GetVRegType(objReg)->AsETSDynamicType()->Language();
488     std::string_view methodName {};
489     if (propType->IsETSBooleanType()) {
490         methodName = Signatures::Dynamic::SetPropertyBooleanBuiltin(lang);
491     } else if (propType->IsByteType()) {
492         methodName = Signatures::Dynamic::SetPropertyByteBuiltin(lang);
493     } else if (propType->IsCharType()) {
494         methodName = Signatures::Dynamic::SetPropertyCharBuiltin(lang);
495     } else if (propType->IsShortType()) {
496         methodName = Signatures::Dynamic::SetPropertyShortBuiltin(lang);
497     } else if (propType->IsIntType()) {
498         methodName = Signatures::Dynamic::SetPropertyIntBuiltin(lang);
499     } else if (propType->IsLongType()) {
500         methodName = Signatures::Dynamic::SetPropertyLongBuiltin(lang);
501     } else if (propType->IsFloatType()) {
502         methodName = Signatures::Dynamic::SetPropertyFloatBuiltin(lang);
503     } else if (propType->IsDoubleType()) {
504         methodName = Signatures::Dynamic::SetPropertyDoubleBuiltin(lang);
505     } else if (propType->IsETSStringType()) {
506         methodName = Signatures::Dynamic::SetPropertyStringBuiltin(lang);
507     } else if (propType->IsETSObjectType() || propType->IsETSTypeParameter()) {
508         methodName = Signatures::Dynamic::SetPropertyDynamicBuiltin(lang);
509         // NOTE: vpukhov. add non-dynamic builtin
510         if (!propType->IsETSDynamicType()) {
511             CastToDynamic(node, Checker()->GlobalBuiltinDynamicType(lang)->AsETSDynamicType());
512         }
513     } else {
514         ASSERT_PRINT(false, "Unsupported property type");
515     }
516 
517     RegScope rs(this);
518     VReg propValueReg = AllocReg();
519     VReg propNameReg = AllocReg();
520 
521     StoreAccumulator(node, propValueReg);
522 
523     // Load property name
524     LoadAccumulatorString(node, propName);
525     StoreAccumulator(node, propNameReg);
526 
527     // Set property by name
528     Ra().Emit<Call, 3U>(node, methodName, objReg, propNameReg, propValueReg, dummyReg_);
529     SetAccumulatorType(Checker()->GlobalBuiltinJSValueType());
530 }
531 
LoadPropertyDynamic(const ir::AstNode * node,const checker::Type * propType,VReg objReg,std::variant<util::StringView,const ark::es2panda::ir::Expression * > property)532 void ETSGen::LoadPropertyDynamic(const ir::AstNode *node, const checker::Type *propType, VReg objReg,
533                                  std::variant<util::StringView, const ark::es2panda::ir::Expression *> property)
534 {
535     ES2PANDA_ASSERT(propType != nullptr && GetVRegType(objReg) != nullptr);
536     auto const lang = GetVRegType(objReg)->AsETSDynamicType()->Language();
537     auto *type = propType;
538     std::string_view methodName {};
539     if (propType->IsETSBooleanType()) {
540         methodName = Signatures::Dynamic::GetPropertyBooleanBuiltin(lang);
541     } else if (propType->IsByteType()) {
542         methodName = Signatures::Dynamic::GetPropertyByteBuiltin(lang);
543     } else if (propType->IsCharType()) {
544         methodName = Signatures::Dynamic::GetPropertyCharBuiltin(lang);
545     } else if (propType->IsShortType()) {
546         methodName = Signatures::Dynamic::GetPropertyShortBuiltin(lang);
547     } else if (propType->IsIntType()) {
548         methodName = Signatures::Dynamic::GetPropertyIntBuiltin(lang);
549     } else if (propType->IsLongType()) {
550         methodName = Signatures::Dynamic::GetPropertyLongBuiltin(lang);
551     } else if (propType->IsFloatType()) {
552         methodName = Signatures::Dynamic::GetPropertyFloatBuiltin(lang);
553     } else if (propType->IsDoubleType()) {
554         methodName = Signatures::Dynamic::GetPropertyDoubleBuiltin(lang);
555     } else if (propType->IsETSStringType()) {
556         methodName = Signatures::Dynamic::GetPropertyStringBuiltin(lang);
557     } else if (propType->IsETSObjectType() || propType->IsETSTypeParameter()) {
558         methodName = Signatures::Dynamic::GetPropertyDynamicBuiltin(lang);
559         type = Checker()->GlobalBuiltinDynamicType(lang);
560     } else {
561         ASSERT_PRINT(false, "Unsupported property type");
562     }
563 
564     RegScope rs(this);
565 
566     VReg propNameObject;
567 
568     if (node->IsMemberExpression() && node->AsMemberExpression()->IsComputed()) {
569         (std::get<const ark::es2panda::ir::Expression *>(property))->Compile(this);
570     } else {
571         // Load property name
572         LoadAccumulatorString(node, std::get<util::StringView>(property));
573     }
574 
575     propNameObject = AllocReg();
576     StoreAccumulator(node, propNameObject);
577 
578     // Get property
579     Ra().Emit<CallShort, 2U>(node, methodName, objReg, propNameObject);
580 
581     SetAccumulatorType(type);
582 
583     if (propType != type && !propType->IsETSDynamicType()) {
584         CastDynamicToObject(node, propType);
585     }
586 }
587 
StoreElementDynamic(const ir::AstNode * node,VReg objectReg,VReg index)588 void ETSGen::StoreElementDynamic(const ir::AstNode *node, VReg objectReg, VReg index)
589 {
590     ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr);
591     auto const lang = GetVRegType(objectReg)->AsETSDynamicType()->Language();
592     std::string_view methodName = Signatures::Dynamic::SetElementDynamicBuiltin(lang);
593 
594     RegScope rs(this);
595 
596     VReg valueReg = AllocReg();
597     StoreAccumulator(node, valueReg);
598 
599     // Set property by index
600     Ra().Emit<Call, 3U>(node, methodName, objectReg, index, valueReg, dummyReg_);
601     SetAccumulatorType(Checker()->GlobalVoidType());
602 }
603 
LoadElementDynamic(const ir::AstNode * node,VReg objectReg)604 void ETSGen::LoadElementDynamic(const ir::AstNode *node, VReg objectReg)
605 {
606     ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr);
607     auto const lang = GetVRegType(objectReg)->AsETSDynamicType()->Language();
608     std::string_view methodName = Signatures::Dynamic::GetElementDynamicBuiltin(lang);
609 
610     RegScope rs(this);
611 
612     VReg indexReg = AllocReg();
613     StoreAccumulator(node, indexReg);
614 
615     // Get property by index
616     Ra().Emit<CallShort, 2U>(node, methodName, objectReg, indexReg);
617     SetAccumulatorType(Checker()->GlobalBuiltinDynamicType(lang));
618 }
619 
CallRangeFillUndefined(const ir::AstNode * const node,checker::Signature * const signature,const VReg thisReg)620 void ETSGen::CallRangeFillUndefined(const ir::AstNode *const node, checker::Signature *const signature,
621                                     const VReg thisReg)
622 {
623     RegScope rs(this);
624     ES2PANDA_ASSERT(signature->MinArgCount() == 0);
625 
626     auto undef = AllocReg();
627     LoadAccumulatorUndefined(node);
628     StoreAccumulator(node, undef);
629 
630     VReg const argStart = NextReg();
631     Ra().Emit<MovObj>(node, AllocReg(), thisReg);
632 
633     for (size_t idx = 0; idx < signature->ArgCount(); idx++) {
634         Ra().Emit<MovObj>(node, AllocReg(), undef);
635     }
636     Rra().Emit<CallRange>(node, argStart, signature->ArgCount() + 1, signature->InternalName(), argStart);
637 }
638 
LoadUndefinedDynamic(const ir::AstNode * node,Language lang)639 void ETSGen::LoadUndefinedDynamic(const ir::AstNode *node, Language lang)
640 {
641     RegScope rs(this);
642     Ra().Emit<CallShort, 0>(node, Signatures::Dynamic::GetUndefinedBuiltin(lang), dummyReg_, dummyReg_);
643     SetAccumulatorType(Checker()->GlobalBuiltinDynamicType(lang));
644 }
645 
LoadThis(const ir::AstNode * node)646 void ETSGen::LoadThis(const ir::AstNode *node)
647 {
648     LoadAccumulator(node, GetThisReg());
649 }
650 
CreateBigIntObject(const ir::AstNode * node,VReg arg0,std::string_view signature)651 void ETSGen::CreateBigIntObject(const ir::AstNode *node, VReg arg0, std::string_view signature)
652 {
653     Ra().Emit<InitobjShort>(node, signature, arg0, dummyReg_);
654 }
655 
GetThisReg() const656 VReg ETSGen::GetThisReg() const
657 {
658     const auto res = Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS);
659     return res.variable->AsLocalVariable()->Vreg();
660 }
661 
LoadDefaultValue(const ir::AstNode * node,const checker::Type * type)662 const checker::Type *ETSGen::LoadDefaultValue(const ir::AstNode *node, const checker::Type *type)
663 {
664     if (type->IsETSAsyncFuncReturnType()) {
665         LoadDefaultValue(node, type->AsETSAsyncFuncReturnType()->GetPromiseTypeArg());
666         return type;
667     }
668 
669     auto const checker = const_cast<checker::ETSChecker *>(Checker());
670 
671     if (type->IsETSReferenceType()) {
672         if (checker->Relation()->IsSupertypeOf(type, checker->GlobalETSUndefinedType())) {
673             LoadAccumulatorUndefined(node);
674         } else {
675             LoadAccumulatorPoison(node, type);
676         }
677         return type;
678     }
679 
680     if (type->IsETSBooleanType()) {
681         LoadAccumulatorBoolean(node, type->AsETSBooleanType()->GetValue());
682     } else {
683         const auto ttctx = TargetTypeContext(this, type);
684         LoadAccumulatorInt(node, 0);
685     }
686     return type;
687 }
688 
EmitReturnVoid(const ir::AstNode * node)689 void ETSGen::EmitReturnVoid(const ir::AstNode *node)
690 {
691     Sa().Emit<ReturnVoid>(node);
692 }
693 
ReturnAcc(const ir::AstNode * node)694 void ETSGen::ReturnAcc(const ir::AstNode *node)
695 {
696     const auto *const accType = GetAccumulatorType();
697     ES2PANDA_ASSERT(accType != nullptr);
698 
699     if (accType->IsETSReferenceType()) {
700         Sa().Emit<ReturnObj>(node);
701     } else if (IsWidePrimitiveType(accType)) {
702         Sa().Emit<ReturnWide>(node);
703     } else {
704         Sa().Emit<Return>(node);
705     }
706 }
707 
IsNullUnsafeObjectType(checker::Type const * type)708 static bool IsNullUnsafeObjectType(checker::Type const *type)
709 {
710     ES2PANDA_ASSERT(type != nullptr);
711     return type->IsETSObjectType() && type->AsETSObjectType()->IsGlobalETSObjectType();
712 }
713 
IsInstanceDynamic(const ir::BinaryExpression * const node,const VReg srcReg,const VReg tgtReg)714 void ETSGen::IsInstanceDynamic(const ir::BinaryExpression *const node, const VReg srcReg,
715                                [[maybe_unused]] const VReg tgtReg)
716 {
717     ES2PANDA_ASSERT(node->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF);
718     const checker::Type *lhsType = node->Left()->TsType();
719     const checker::Type *rhsType = node->Right()->TsType();
720     ES2PANDA_ASSERT(rhsType->IsETSDynamicType() || lhsType->IsETSDynamicType());
721 
722     const RegScope rs(this);
723     if (rhsType->IsETSDynamicType()) {
724         ES2PANDA_ASSERT(node->Right()->TsType()->AsETSDynamicType()->HasDecl());
725         if (lhsType->IsETSDynamicType()) {
726             VReg dynTypeReg = MoveAccToReg(node);
727             // Semantics:
728             //      let dyn_val: JSValue = ...
729             //      dyn_value instanceof DynamicDecl
730             // Bytecode:
731             //      call runtime intrinsic_dynamic
732             CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_DYNAMIC, srcReg, dynTypeReg);
733         } else if (lhsType == Checker()->GlobalETSObjectType()) {
734             // Semantics:
735             //      let obj: Object = ...
736             //      obj instanceof DynamicDecl
737             // Bytecode:
738             //      if isinstance <dynamic type name>:
739             //          checkcast <dynamic type name>
740             //          return call runtime intrinsic_dynamic
741             //      return false
742             Label *ifFalse = AllocLabel();
743             Language lang = rhsType->AsETSDynamicType()->Language();
744             ES2PANDA_ASSERT(Checker()->GlobalBuiltinDynamicType(lang) != nullptr);
745             VReg dynTypeReg = MoveAccToReg(node);
746             LoadAccumulator(node, srcReg);
747             EmitIsInstance(node, Checker()->GlobalBuiltinDynamicType(lang)->AssemblerName());
748             BranchIfFalse(node, ifFalse);
749             LoadAccumulator(node, srcReg);
750             EmitCheckCast(node, Checker()->GlobalBuiltinDynamicType(lang)->AssemblerName());
751             CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_DYNAMIC, srcReg, dynTypeReg);
752             SetLabel(node, ifFalse);
753         } else {
754             // Semantics:
755             //      let obj: EtsType = ...
756             //      obj instanceof DynamicDecl
757             // Bytecode:
758             //      False
759             Sa().Emit<Ldai>(node, 0);
760         }
761     } else {
762         if (lhsType->IsETSDynamicType()) {
763             if (rhsType == Checker()->GlobalETSObjectType()) {
764                 // Semantics:
765                 //      let dyn_val: JSValue = ...
766                 //      dyn_val instanceof Object
767                 // Bytecode:
768                 //      True
769                 Sa().Emit<Ldai>(node, 1);
770             } else {
771                 // Semantics:
772                 //      let dyn_val: JSValue = ...
773                 //      dyn_val instanceof EtsType
774                 // Bytecode:
775                 //      lda.type + call runtime instrinsic_static
776                 Sa().Emit<LdaType>(node, rhsType->AsETSObjectType()->AssemblerName());
777                 VReg typeReg = MoveAccToReg(node);
778                 CallExact(node, Signatures::BUILTIN_JSRUNTIME_INSTANCE_OF_STATIC, srcReg, typeReg);
779             }
780         } else {
781             ES2PANDA_UNREACHABLE();
782         }
783     }
784     SetAccumulatorType(Checker()->GlobalETSBooleanType());
785 }
786 
787 // Implemented on top of the runtime type system, do not relax checks, do not introduce new types
TestIsInstanceConstituent(const ir::AstNode * const node,std::tuple<Label *,Label * > label,checker::Type const * target,bool acceptNull)788 void ETSGen::TestIsInstanceConstituent(const ir::AstNode *const node, std::tuple<Label *, Label *> label,
789                                        checker::Type const *target, bool acceptNull)
790 {
791     ES2PANDA_ASSERT(target != nullptr);
792     ES2PANDA_ASSERT(!target->IsETSDynamicType());
793     auto [ifTrue, ifFalse] = label;
794 
795     switch (checker::ETSChecker::ETSType(target)) {
796         case checker::TypeFlag::ETS_UNDEFINED:
797         case checker::TypeFlag::ETS_VOID:
798             BranchIfUndefined(node, ifTrue);
799             break;
800         case checker::TypeFlag::ETS_NULL:
801             BranchIfNull(node, ifTrue);
802             break;
803         case checker::TypeFlag::ETS_OBJECT:
804             if (!IsNullUnsafeObjectType(target)) {
805                 EmitIsInstance(node, ToAssemblerType(target));
806                 BranchIfTrue(node, ifTrue);
807                 break;
808             }
809             if (!acceptNull) {
810                 BranchIfNull(node, ifFalse);
811             }
812             JumpTo(node, ifTrue);
813             break;
814         case checker::TypeFlag::ETS_ARRAY:
815         case checker::TypeFlag::ETS_TUPLE:
816         case checker::TypeFlag::FUNCTION: {
817             EmitIsInstance(node, ToAssemblerType(target));
818             BranchIfTrue(node, ifTrue);
819             break;
820         }
821         default:
822             ES2PANDA_UNREACHABLE();  // other types must not appear here
823     }
824     SetAccumulatorType(nullptr);
825 }
826 
827 // Implemented on top of the runtime type system, do not relax checks, do not introduce new types
BranchIfIsInstance(const ir::AstNode * const node,const VReg srcReg,const checker::Type * target,Label * ifTrue)828 void ETSGen::BranchIfIsInstance(const ir::AstNode *const node, const VReg srcReg, const checker::Type *target,
829                                 Label *ifTrue)
830 {
831     ES2PANDA_ASSERT(target == Checker()->GetApparentType(target));
832     auto ifFalse = AllocLabel();
833 
834     if (!target->PossiblyETSUndefined()) {
835         LoadAccumulator(node, srcReg);
836         BranchIfUndefined(node, ifFalse);
837     }
838 
839     auto const checkType = [this, srcReg, ifTrue, ifFalse, acceptNull = target->PossiblyETSNull()](
840                                const ir::AstNode *const n, checker::Type const *t) {
841         LoadAccumulator(n, srcReg);
842         // #21835: type-alias in ApparentType
843         t = t->IsETSTypeAliasType() ? t->AsETSTypeAliasType()->GetTargetType() : t;
844         TestIsInstanceConstituent(n, std::tie(ifTrue, ifFalse), t, acceptNull);
845     };
846 
847     if (target->IsETSUnionType()) {
848         for (auto *ct : target->AsETSUnionType()->ConstituentTypes()) {
849             checkType(node, ct);
850         }
851     } else if (!target->IsETSNeverType()) {
852         checkType(node, target);
853     }
854 
855     SetLabel(node, ifFalse);
856     SetAccumulatorType(nullptr);
857 }
858 
859 // Implemented on top of the runtime type system, do not relax checks, do not introduce new types
IsInstance(const ir::AstNode * const node,const VReg srcReg,const checker::Type * target)860 void ETSGen::IsInstance(const ir::AstNode *const node, const VReg srcReg, const checker::Type *target)
861 {
862     target = Checker()->GetApparentType(target);
863     ES2PANDA_ASSERT(target != nullptr);
864     ES2PANDA_ASSERT(target->IsETSReferenceType() && GetAccumulatorType() != nullptr);
865 
866     if (target->IsETSAnyType()) {  // should be IsSupertypeOf(target, source)
867         LoadAccumulatorBoolean(node, true);
868         return;
869     }
870     if (target->IsETSArrayType() ||
871         (target->IsETSObjectType() && !(IsNullUnsafeObjectType(target) && GetAccumulatorType()->PossiblyETSNull()))) {
872         InternalIsInstance(node, target);
873         return;
874     }
875 
876     auto ifTrue = AllocLabel();
877     auto end = AllocLabel();
878 
879     BranchIfIsInstance(node, srcReg, target, ifTrue);
880     LoadAccumulatorBoolean(node, false);
881     JumpTo(node, end);
882 
883     SetLabel(node, ifTrue);
884     LoadAccumulatorBoolean(node, true);
885     SetLabel(node, end);
886 }
887 
888 // isinstance can only be used for Object and [] types, ensure source is not null!
InternalIsInstance(const ir::AstNode * node,const es2panda::checker::Type * target)889 void ETSGen::InternalIsInstance(const ir::AstNode *node, const es2panda::checker::Type *target)
890 {
891     ES2PANDA_ASSERT(target != nullptr);
892     ES2PANDA_ASSERT(target->IsETSObjectType() || target->IsETSArrayType());
893     if (!IsNullUnsafeObjectType(target)) {
894         EmitIsInstance(node, ToAssemblerType(target));
895         SetAccumulatorType(Checker()->GlobalETSBooleanType());
896     } else {
897         LoadAccumulatorBoolean(node, true);
898     }
899 }
900 
901 // checkcast can only be used for Object and [] types, ensure source is not nullish!
InternalCheckCast(const ir::AstNode * node,const es2panda::checker::Type * target)902 void ETSGen::InternalCheckCast(const ir::AstNode *node, const es2panda::checker::Type *target)
903 {
904     ES2PANDA_ASSERT(target != nullptr);
905     ES2PANDA_ASSERT(target->IsETSObjectType() || target->IsETSArrayType() || target->IsETSTupleType());
906     if (!IsNullUnsafeObjectType(target)) {
907         EmitCheckCast(node, ToAssemblerType(target));
908     }
909     SetAccumulatorType(target);
910 }
911 
912 // optimized specialization for object and [] targets
CheckedReferenceNarrowingObject(const ir::AstNode * node,const checker::Type * target)913 void ETSGen::CheckedReferenceNarrowingObject(const ir::AstNode *node, const checker::Type *target)
914 {
915     ES2PANDA_ASSERT(target != nullptr);
916     ES2PANDA_ASSERT(target->IsETSObjectType() || target->IsETSArrayType() || target->IsETSTupleType());
917     const RegScope rs(this);
918     const auto srcReg = AllocReg();
919     StoreAccumulator(node, srcReg);
920 
921     auto isNullish = AllocLabel();
922     auto end = AllocLabel();
923     bool nullishCheck = false;
924 
925     auto *source = GetAccumulatorType();
926     ES2PANDA_ASSERT(source != nullptr);
927     if (source->PossiblyETSUndefined()) {
928         nullishCheck = true;
929         BranchIfUndefined(node, isNullish);
930     }
931     if (source->PossiblyETSNull() && IsNullUnsafeObjectType(target)) {
932         nullishCheck = true;
933         BranchIfNull(node, isNullish);
934     }
935 
936     if (!nullishCheck) {
937         InternalCheckCast(node, target);
938     } else {
939         LoadAccumulator(node, srcReg);
940         InternalCheckCast(node, target);
941         JumpTo(node, end);
942 
943         SetLabel(node, isNullish);
944         EmitFailedTypeCastException(node, srcReg, target);
945 
946         SetLabel(node, end);
947         SetAccumulatorType(target);
948     }
949 }
950 
951 // Implemented on top of the runtime type system, do not relax checks, do not introduce new types
CheckedReferenceNarrowing(const ir::AstNode * node,const checker::Type * target)952 void ETSGen::CheckedReferenceNarrowing(const ir::AstNode *node, const checker::Type *target)
953 {
954     ES2PANDA_ASSERT(target != nullptr);
955     // NOTE(vpukhov): #19701 void refactoring
956     if (target->IsETSVoidType()) {
957         SetAccumulatorType(target);
958         return;
959     }
960 
961     target = Checker()->GetApparentType(target);
962     ES2PANDA_ASSERT(target != nullptr);
963     ES2PANDA_ASSERT(target->IsETSReferenceType());
964     if (target->IsETSAnyType()) {  // should be IsSupertypeOf(target, source)
965         SetAccumulatorType(target);
966         return;
967     }
968     if (target->HasTypeFlag(checker::TypeFlag::ETS_ARRAY_OR_OBJECT | checker::TypeFlag::ETS_TUPLE) &&
969         !target->IsConstantType()) {
970         CheckedReferenceNarrowingObject(node, target);
971         return;
972     }
973 
974     const RegScope rs(this);
975     const auto srcReg = AllocReg();
976     auto ifTrue = AllocLabel();
977 
978     StoreAccumulator(node, srcReg);
979     BranchIfIsInstance(node, srcReg, target, ifTrue);
980 
981     EmitFailedTypeCastException(node, srcReg, target);
982 
983     SetLabel(node, ifTrue);
984     LoadAccumulator(node, srcReg);
985     // Verifier can't infer type if isinstance met, help him
986     EmitCheckCast(node, ToAssemblerType(target));
987     SetAccumulatorType(target);
988 }
989 
GuardUncheckedType(const ir::AstNode * node,const checker::Type * unchecked,const checker::Type * target)990 void ETSGen::GuardUncheckedType(const ir::AstNode *node, const checker::Type *unchecked, const checker::Type *target)
991 {
992     if (unchecked != nullptr) {
993         SetAccumulatorType(unchecked);
994         // this check guards possible type violations, **do not relax it**
995         CheckedReferenceNarrowing(node, Checker()->MaybeBoxType(target));
996     }
997     SetAccumulatorType(target);
998 }
999 
EmitFailedTypeCastException(const ir::AstNode * node,const VReg src,checker::Type const * target)1000 void ETSGen::EmitFailedTypeCastException(const ir::AstNode *node, const VReg src, checker::Type const *target)
1001 {
1002     const RegScope rs(this);
1003     const auto errorReg = AllocReg();
1004 
1005     LoadAccumulatorString(node, util::UString(target->ToString(), Allocator()).View());
1006     Ra().Emit<CallAccShort, 1>(node, Signatures::BUILTIN_RUNTIME_FAILED_TYPE_CAST_EXCEPTION, src, 1);
1007     StoreAccumulator(node, errorReg);
1008     EmitThrow(node, errorReg);
1009     SetAccumulatorType(nullptr);
1010 }
1011 
LoadConstantObject(const ir::Expression * node,const checker::Type * type)1012 void ETSGen::LoadConstantObject(const ir::Expression *node, const checker::Type *type)
1013 {
1014     if (type->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL)) {
1015         LoadAccumulatorBigInt(node, type->AsETSObjectType()->AsETSBigIntType()->GetValue());
1016         const VReg value = AllocReg();
1017         StoreAccumulator(node, value);
1018         CreateBigIntObject(node, value);
1019     } else {
1020         LoadAccumulatorString(node, type->AsETSObjectType()->AsETSStringType()->GetValue());
1021         SetAccumulatorType(node->TsType());
1022     }
1023 }
1024 
ApplyConversionCast(const ir::AstNode * node,const checker::Type * targetType)1025 void ETSGen::ApplyConversionCast(const ir::AstNode *node, const checker::Type *targetType)
1026 {
1027     switch (checker::ETSChecker::TypeKind(targetType)) {
1028         case checker::TypeFlag::DOUBLE: {
1029             CastToDouble(node);
1030             break;
1031         }
1032         case checker::TypeFlag::FLOAT: {
1033             CastToFloat(node);
1034             break;
1035         }
1036         case checker::TypeFlag::LONG: {
1037             CastToLong(node);
1038             break;
1039         }
1040         case checker::TypeFlag::INT: {
1041             CastToInt(node);
1042             break;
1043         }
1044         case checker::TypeFlag::CHAR: {
1045             CastToChar(node);
1046             break;
1047         }
1048         case checker::TypeFlag::SHORT: {
1049             CastToShort(node);
1050             break;
1051         }
1052         case checker::TypeFlag::BYTE: {
1053             CastToByte(node);
1054             break;
1055         }
1056         case checker::TypeFlag::ETS_ARRAY:
1057         case checker::TypeFlag::ETS_OBJECT:
1058         case checker::TypeFlag::ETS_TYPE_PARAMETER: {
1059             ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1060             if (GetAccumulatorType() != nullptr && GetAccumulatorType()->IsETSDynamicType()) {
1061                 CastDynamicToObject(node, targetType);
1062             }
1063             break;
1064         }
1065         case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1066             CastToDynamic(node, targetType->AsETSDynamicType());
1067             break;
1068         }
1069         default: {
1070             break;
1071         }
1072     }
1073 }
1074 
ApplyBoxingConversion(const ir::AstNode * node)1075 void ETSGen::ApplyBoxingConversion(const ir::AstNode *node)
1076 {
1077     EmitBoxingConversion(node);
1078     node->SetBoxingUnboxingFlags(
1079         static_cast<ir::BoxingUnboxingFlags>(node->GetBoxingUnboxingFlags() & ~(ir::BoxingUnboxingFlags::BOXING_FLAG)));
1080 }
1081 
ApplyUnboxingConversion(const ir::AstNode * node)1082 void ETSGen::ApplyUnboxingConversion(const ir::AstNode *node)
1083 {
1084     auto const callUnbox = [this, node](std::string_view sig, checker::Type const *unboxedType) {
1085         auto boxedType = Checker()->MaybeBoxType(unboxedType)->AsETSObjectType();
1086         EmitUnboxedCall(node, sig, unboxedType, boxedType);
1087     };
1088 
1089     auto const unboxFlags =
1090         ir::BoxingUnboxingFlags(node->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG);
1091     node->RemoveBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOXING_FLAG);
1092 
1093     switch (unboxFlags) {
1094         case ir::BoxingUnboxingFlags::UNBOX_TO_BOOLEAN:
1095             callUnbox(Signatures::BUILTIN_BOOLEAN_UNBOXED, Checker()->GlobalETSBooleanType());
1096             return;
1097         case ir::BoxingUnboxingFlags::UNBOX_TO_BYTE:
1098             callUnbox(Signatures::BUILTIN_BYTE_UNBOXED, Checker()->GlobalByteType());
1099             return;
1100         case ir::BoxingUnboxingFlags::UNBOX_TO_CHAR:
1101             callUnbox(Signatures::BUILTIN_CHAR_UNBOXED, Checker()->GlobalCharType());
1102             return;
1103         case ir::BoxingUnboxingFlags::UNBOX_TO_SHORT:
1104             callUnbox(Signatures::BUILTIN_SHORT_UNBOXED, Checker()->GlobalShortType());
1105             return;
1106         case ir::BoxingUnboxingFlags::UNBOX_TO_INT:
1107             callUnbox(Signatures::BUILTIN_INT_UNBOXED, Checker()->GlobalIntType());
1108             return;
1109         case ir::BoxingUnboxingFlags::UNBOX_TO_LONG:
1110             callUnbox(Signatures::BUILTIN_LONG_UNBOXED, Checker()->GlobalLongType());
1111             return;
1112         case ir::BoxingUnboxingFlags::UNBOX_TO_FLOAT:
1113             callUnbox(Signatures::BUILTIN_FLOAT_UNBOXED, Checker()->GlobalFloatType());
1114             return;
1115         case ir::BoxingUnboxingFlags::UNBOX_TO_DOUBLE:
1116             callUnbox(Signatures::BUILTIN_DOUBLE_UNBOXED, Checker()->GlobalDoubleType());
1117             return;
1118         default:
1119             ES2PANDA_UNREACHABLE();
1120     }
1121 }
1122 
ApplyConversion(const ir::AstNode * node,const checker::Type * targetType)1123 void ETSGen::ApplyConversion(const ir::AstNode *node, const checker::Type *targetType)
1124 {
1125     auto ttctx = TargetTypeContext(this, targetType);
1126 
1127     const bool hasBoxingflags = (node->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::BOXING_FLAG) != 0U;
1128     const bool hasUnboxingflags = (node->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U;
1129     if (hasBoxingflags && !hasUnboxingflags) {
1130         ApplyBoxingConversion(node);
1131         return;
1132     }
1133 
1134     if (hasUnboxingflags) {
1135         ApplyUnboxingConversion(node);
1136     }
1137 
1138     if (targetType == nullptr) {
1139         return;
1140     }
1141 
1142     ApplyConversionCast(node, targetType);
1143 
1144     if (hasBoxingflags) {
1145         ApplyBoxingConversion(node);
1146     }
1147 }
1148 
ApplyCast(const ir::AstNode * node,const checker::Type * targetType)1149 void ETSGen::ApplyCast(const ir::AstNode *node, const checker::Type *targetType)
1150 {
1151     auto typeKind = checker::ETSChecker::TypeKind(targetType);
1152 
1153     switch (typeKind) {
1154         case checker::TypeFlag::DOUBLE: {
1155             CastToDouble(node);
1156             break;
1157         }
1158         case checker::TypeFlag::FLOAT: {
1159             CastToFloat(node);
1160             break;
1161         }
1162         case checker::TypeFlag::LONG: {
1163             CastToLong(node);
1164             break;
1165         }
1166         case checker::TypeFlag::INT: {
1167             CastToInt(node);
1168             break;
1169         }
1170         case checker::TypeFlag::SHORT: {
1171             CastToShort(node);
1172             break;
1173         }
1174         case checker::TypeFlag::BYTE: {
1175             CastToByte(node);
1176             break;
1177         }
1178         case checker::TypeFlag::CHAR: {
1179             CastToChar(node);
1180             break;
1181         }
1182         case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1183             CastToDynamic(node, targetType->AsETSDynamicType());
1184             break;
1185         }
1186         default: {
1187             break;
1188         }
1189     }
1190 }
1191 
ApplyCastToBoxingFlags(const ir::AstNode * node,const ir::BoxingUnboxingFlags targetType)1192 void ETSGen::ApplyCastToBoxingFlags(const ir::AstNode *node, const ir::BoxingUnboxingFlags targetType)
1193 {
1194     switch (targetType) {
1195         case ir::BoxingUnboxingFlags::BOX_TO_DOUBLE: {
1196             CastToDouble(node);
1197             break;
1198         }
1199         case ir::BoxingUnboxingFlags::BOX_TO_FLOAT: {
1200             CastToFloat(node);
1201             break;
1202         }
1203         case ir::BoxingUnboxingFlags::BOX_TO_LONG: {
1204             CastToLong(node);
1205             break;
1206         }
1207         case ir::BoxingUnboxingFlags::BOX_TO_INT: {
1208             CastToInt(node);
1209             break;
1210         }
1211         case ir::BoxingUnboxingFlags::BOX_TO_BYTE: {
1212             CastToByte(node);
1213             break;
1214         }
1215         default: {
1216             break;
1217         }
1218     }
1219 }
1220 
EmitUnboxedCall(const ir::AstNode * node,std::string_view signatureFlag,const checker::Type * const targetType,const checker::Type * const boxedType)1221 void ETSGen::EmitUnboxedCall(const ir::AstNode *node, std::string_view signatureFlag,
1222                              const checker::Type *const targetType, const checker::Type *const boxedType)
1223 {
1224     RegScope rs(this);
1225     // NOTE(vpukhov): #20510 lowering
1226     if (node->HasAstNodeFlags(ir::AstNodeFlags::CHECKCAST)) {
1227         CheckedReferenceNarrowing(node, boxedType);
1228     }
1229 
1230     // to cast to primitive types we probably have to cast to corresponding boxed built-in types first.
1231     auto *const checker = Checker()->AsETSChecker();
1232     auto const *accumulatorType = GetAccumulatorType();
1233     ES2PANDA_ASSERT(accumulatorType != nullptr);
1234     if (accumulatorType->IsETSObjectType() &&  //! accumulatorType->DefinitelyNotETSNullish() &&
1235         !checker->Relation()->IsIdenticalTo(const_cast<checker::Type *>(accumulatorType),
1236                                             const_cast<checker::Type *>(boxedType))) {
1237         CastToReftype(node, boxedType, false);
1238     }
1239 
1240     Ra().Emit<CallAccShort, 0>(node, signatureFlag, dummyReg_, 0);
1241     SetAccumulatorType(targetType);
1242     if (node->IsExpression()) {
1243         const_cast<ir::Expression *>(node->AsExpression())->SetTsType(const_cast<checker::Type *>(targetType));
1244     }
1245 }
1246 
1247 // NOTE(vpukhov): #20510 should be available only as a part of ApplyBoxingConversion
EmitBoxingConversion(ir::BoxingUnboxingFlags boxingFlag,const ir::AstNode * node)1248 void ETSGen::EmitBoxingConversion(ir::BoxingUnboxingFlags boxingFlag, const ir::AstNode *node)
1249 {
1250     auto const callBox = [this, node](std::string_view sig, checker::Type const *unboxedType) {
1251         Ra().Emit<CallAccShort, 0>(node, sig, dummyReg_, 0);
1252         SetAccumulatorType(Checker()->MaybeBoxType(unboxedType)->AsETSObjectType());
1253     };
1254 
1255     switch (boxingFlag) {
1256         case ir::BoxingUnboxingFlags::BOX_TO_BOOLEAN:
1257             callBox(Signatures::BUILTIN_BOOLEAN_VALUE_OF, Checker()->GlobalETSBooleanType());
1258             return;
1259         case ir::BoxingUnboxingFlags::BOX_TO_BYTE:
1260             callBox(Signatures::BUILTIN_BYTE_VALUE_OF, Checker()->GlobalByteType());
1261             return;
1262         case ir::BoxingUnboxingFlags::BOX_TO_CHAR:
1263             callBox(Signatures::BUILTIN_CHAR_VALUE_OF, Checker()->GlobalCharType());
1264             return;
1265         case ir::BoxingUnboxingFlags::BOX_TO_SHORT:
1266             callBox(Signatures::BUILTIN_SHORT_VALUE_OF, Checker()->GlobalShortType());
1267             return;
1268         case ir::BoxingUnboxingFlags::BOX_TO_INT:
1269             callBox(Signatures::BUILTIN_INT_VALUE_OF, Checker()->GlobalIntType());
1270             return;
1271         case ir::BoxingUnboxingFlags::BOX_TO_LONG:
1272             callBox(Signatures::BUILTIN_LONG_VALUE_OF, Checker()->GlobalLongType());
1273             return;
1274         case ir::BoxingUnboxingFlags::BOX_TO_FLOAT:
1275             callBox(Signatures::BUILTIN_FLOAT_VALUE_OF, Checker()->GlobalFloatType());
1276             return;
1277         case ir::BoxingUnboxingFlags::BOX_TO_DOUBLE:
1278             callBox(Signatures::BUILTIN_DOUBLE_VALUE_OF, Checker()->GlobalDoubleType());
1279             return;
1280         default:
1281             ES2PANDA_UNREACHABLE();
1282     }
1283 }
1284 
1285 // NOTE(vpukhov): #20510 should be available only as a part of ApplyBoxingConversion
EmitBoxingConversion(const ir::AstNode * node)1286 void ETSGen::EmitBoxingConversion(const ir::AstNode *node)
1287 {
1288     auto boxingFlag =
1289         static_cast<ir::BoxingUnboxingFlags>(ir::BoxingUnboxingFlags::BOXING_FLAG & node->GetBoxingUnboxingFlags());
1290 
1291     RegScope rs(this);
1292 
1293     ApplyCastToBoxingFlags(node, boxingFlag);
1294 
1295     EmitBoxingConversion(boxingFlag, node);
1296 
1297     if (node->IsExpression()) {
1298         auto boxedType = const_cast<checker::Type *>(GetAccumulatorType());
1299         const_cast<ir::Expression *>(node->AsExpression())->SetTsType(boxedType);
1300     }
1301 }
1302 
SwapBinaryOpArgs(const ir::AstNode * const node,const VReg lhs)1303 void ETSGen::SwapBinaryOpArgs(const ir::AstNode *const node, const VReg lhs)
1304 {
1305     const RegScope rs(this);
1306     const auto tmp = AllocReg();
1307 
1308     StoreAccumulator(node, tmp);
1309     LoadAccumulator(node, lhs);
1310     MoveVreg(node, lhs, tmp);
1311 }
1312 
MoveAccToReg(const ir::AstNode * const node)1313 VReg ETSGen::MoveAccToReg(const ir::AstNode *const node)
1314 {
1315     const auto newReg = AllocReg();
1316     StoreAccumulator(node, newReg);
1317     return newReg;
1318 }
1319 
CastToBoolean(const ir::AstNode * node)1320 void ETSGen::CastToBoolean([[maybe_unused]] const ir::AstNode *node)
1321 {
1322     auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1323     switch (typeKind) {
1324         case checker::TypeFlag::ETS_BOOLEAN: {
1325             return;
1326         }
1327         case checker::TypeFlag::CHAR: {
1328             Sa().Emit<U32tou1>(node);
1329             break;
1330         }
1331         case checker::TypeFlag::BYTE:
1332         case checker::TypeFlag::SHORT:
1333         case checker::TypeFlag::INT: {
1334             Sa().Emit<I32tou1>(node);
1335             return;
1336         }
1337         case checker::TypeFlag::LONG: {
1338             Sa().Emit<I64tou1>(node);
1339             break;
1340         }
1341         case checker::TypeFlag::FLOAT: {
1342             Sa().Emit<F32toi32>(node);
1343             Sa().Emit<I32tou1>(node);
1344             break;
1345         }
1346         case checker::TypeFlag::DOUBLE: {
1347             Sa().Emit<F64toi32>(node);
1348             Sa().Emit<I32tou1>(node);
1349             break;
1350         }
1351         case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1352             CastDynamicTo(node, checker::TypeFlag::ETS_BOOLEAN);
1353             ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalETSBooleanType());
1354             break;
1355         }
1356         default: {
1357             ES2PANDA_UNREACHABLE();
1358         }
1359     }
1360 
1361     SetAccumulatorType(Checker()->GlobalETSBooleanType());
1362 }
1363 
CastToByte(const ir::AstNode * node)1364 void ETSGen::CastToByte([[maybe_unused]] const ir::AstNode *node)
1365 {
1366     auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1367     switch (typeKind) {
1368         case checker::TypeFlag::BYTE: {
1369             return;
1370         }
1371         case checker::TypeFlag::ETS_BOOLEAN:
1372         case checker::TypeFlag::CHAR: {
1373             Sa().Emit<U32toi8>(node);
1374             break;
1375         }
1376         case checker::TypeFlag::SHORT:
1377         case checker::TypeFlag::INT: {
1378             Sa().Emit<I32toi8>(node);
1379             break;
1380         }
1381         case checker::TypeFlag::LONG: {
1382             Sa().Emit<I64toi32>(node);
1383             Sa().Emit<I32toi8>(node);
1384             break;
1385         }
1386         case checker::TypeFlag::FLOAT: {
1387             Sa().Emit<F32toi32>(node);
1388             Sa().Emit<I32toi8>(node);
1389             break;
1390         }
1391         case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1392             CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1393             ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1394             [[fallthrough]];
1395         }
1396         case checker::TypeFlag::DOUBLE: {
1397             Sa().Emit<F64toi32>(node);
1398             Sa().Emit<I32toi8>(node);
1399             break;
1400         }
1401         case checker::TypeFlag::ETS_OBJECT: {
1402             break;
1403         }
1404         default: {
1405             ES2PANDA_UNREACHABLE();
1406         }
1407     }
1408 
1409     SetAccumulatorType(Checker()->GlobalByteType());
1410 }
1411 
CastToChar(const ir::AstNode * node)1412 void ETSGen::CastToChar([[maybe_unused]] const ir::AstNode *node)
1413 {
1414     auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1415     switch (typeKind) {
1416         case checker::TypeFlag::CHAR: {
1417             if (node->IsCharLiteral()) {
1418                 auto type = node->AsCharLiteral()->TsType();
1419                 if (type->TypeFlags() == (checker::TypeFlag::CONSTANT | checker::TypeFlag::BYTE)) {
1420                     SetAccumulatorType(type);
1421                 }
1422             }
1423             return;
1424         }
1425         case checker::TypeFlag::ETS_BOOLEAN: {
1426             break;
1427         }
1428         case checker::TypeFlag::BYTE:
1429         case checker::TypeFlag::SHORT:
1430         case checker::TypeFlag::INT: {
1431             Sa().Emit<I32tou16>(node);
1432             break;
1433         }
1434         case checker::TypeFlag::LONG: {
1435             Sa().Emit<I64toi32>(node);
1436             Sa().Emit<I32tou16>(node);
1437             break;
1438         }
1439         case checker::TypeFlag::FLOAT: {
1440             Sa().Emit<F32toi32>(node);
1441             Sa().Emit<I32tou16>(node);
1442             break;
1443         }
1444         case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1445             CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1446             ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1447             [[fallthrough]];
1448         }
1449         case checker::TypeFlag::DOUBLE: {
1450             Sa().Emit<F64toi32>(node);
1451             Sa().Emit<I32tou16>(node);
1452             break;
1453         }
1454         case checker::TypeFlag::ETS_OBJECT: {
1455             break;
1456         }
1457         default: {
1458             ES2PANDA_UNREACHABLE();
1459         }
1460     }
1461 
1462     SetAccumulatorType(Checker()->GlobalCharType());
1463 }
1464 
CastToShort(const ir::AstNode * node)1465 void ETSGen::CastToShort([[maybe_unused]] const ir::AstNode *node)
1466 {
1467     auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1468     switch (typeKind) {
1469         case checker::TypeFlag::SHORT: {
1470             return;
1471         }
1472         case checker::TypeFlag::ETS_BOOLEAN:
1473         case checker::TypeFlag::CHAR: {
1474             Sa().Emit<U32toi16>(node);
1475             break;
1476         }
1477         case checker::TypeFlag::BYTE: {
1478             break;
1479         }
1480         case checker::TypeFlag::INT: {
1481             Sa().Emit<I32toi16>(node);
1482             break;
1483         }
1484         case checker::TypeFlag::LONG: {
1485             Sa().Emit<I64toi32>(node);
1486             Sa().Emit<I32toi16>(node);
1487             break;
1488         }
1489         case checker::TypeFlag::FLOAT: {
1490             Sa().Emit<F32toi32>(node);
1491             Sa().Emit<I32toi16>(node);
1492             break;
1493         }
1494         case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1495             CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1496             ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1497             [[fallthrough]];
1498         }
1499         case checker::TypeFlag::DOUBLE: {
1500             Sa().Emit<F64toi32>(node);
1501             Sa().Emit<I32toi16>(node);
1502             break;
1503         }
1504         case checker::TypeFlag::ETS_OBJECT: {
1505             break;
1506         }
1507         default: {
1508             ES2PANDA_UNREACHABLE();
1509         }
1510     }
1511 
1512     SetAccumulatorType(Checker()->GlobalShortType());
1513 }
1514 
CastToDouble(const ir::AstNode * node)1515 void ETSGen::CastToDouble(const ir::AstNode *node)
1516 {
1517     auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1518     switch (typeKind) {
1519         case checker::TypeFlag::DOUBLE: {
1520             return;
1521         }
1522         case checker::TypeFlag::ETS_BOOLEAN:
1523         case checker::TypeFlag::CHAR: {
1524             Sa().Emit<U32tof64>(node);
1525             break;
1526         }
1527         case checker::TypeFlag::BYTE:
1528         case checker::TypeFlag::SHORT:
1529         case checker::TypeFlag::INT: {
1530             Sa().Emit<I32tof64>(node);
1531             break;
1532         }
1533         case checker::TypeFlag::LONG: {
1534             Sa().Emit<I64tof64>(node);
1535             break;
1536         }
1537         case checker::TypeFlag::FLOAT: {
1538             Sa().Emit<F32tof64>(node);
1539             break;
1540         }
1541         case checker::TypeFlag::ETS_NEVER:
1542         case checker::TypeFlag::ETS_OBJECT: {
1543             break;
1544         }
1545         case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1546             CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1547             ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1548             break;
1549         }
1550         default: {
1551             ES2PANDA_UNREACHABLE();
1552         }
1553     }
1554 
1555     SetAccumulatorType(Checker()->GlobalDoubleType());
1556 }
1557 
CastToFloat(const ir::AstNode * node)1558 void ETSGen::CastToFloat(const ir::AstNode *node)
1559 {
1560     auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1561     switch (typeKind) {
1562         case checker::TypeFlag::FLOAT: {
1563             return;
1564         }
1565         case checker::TypeFlag::ETS_BOOLEAN:
1566         case checker::TypeFlag::CHAR: {
1567             Sa().Emit<U32tof32>(node);
1568             break;
1569         }
1570         case checker::TypeFlag::BYTE:
1571         case checker::TypeFlag::SHORT:
1572         case checker::TypeFlag::INT: {
1573             Sa().Emit<I32tof32>(node);
1574             break;
1575         }
1576         case checker::TypeFlag::LONG: {
1577             Sa().Emit<I64tof32>(node);
1578             break;
1579         }
1580         case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1581             CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1582             ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1583             [[fallthrough]];
1584         }
1585         case checker::TypeFlag::DOUBLE: {
1586             Sa().Emit<F64tof32>(node);
1587             break;
1588         }
1589         case checker::TypeFlag::ETS_OBJECT: {
1590             break;
1591         }
1592         default: {
1593             ES2PANDA_UNREACHABLE();
1594         }
1595     }
1596 
1597     SetAccumulatorType(Checker()->GlobalFloatType());
1598 }
1599 
CastToLong(const ir::AstNode * node)1600 void ETSGen::CastToLong(const ir::AstNode *node)
1601 {
1602     auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1603     switch (typeKind) {
1604         case checker::TypeFlag::LONG: {
1605             return;
1606         }
1607         case checker::TypeFlag::ETS_BOOLEAN:
1608         case checker::TypeFlag::CHAR: {
1609             Sa().Emit<U32toi64>(node);
1610             break;
1611         }
1612         case checker::TypeFlag::BYTE:
1613         case checker::TypeFlag::SHORT:
1614         case checker::TypeFlag::INT: {
1615             Sa().Emit<I32toi64>(node);
1616             break;
1617         }
1618         case checker::TypeFlag::FLOAT: {
1619             Sa().Emit<F32toi64>(node);
1620             break;
1621         }
1622         case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1623             CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1624             ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1625             [[fallthrough]];
1626         }
1627         case checker::TypeFlag::DOUBLE: {
1628             Sa().Emit<F64toi64>(node);
1629             break;
1630         }
1631         case checker::TypeFlag::ETS_ARRAY:
1632         case checker::TypeFlag::ETS_OBJECT: {
1633             break;
1634         }
1635         default: {
1636             ES2PANDA_UNREACHABLE();
1637         }
1638     }
1639 
1640     SetAccumulatorType(Checker()->GlobalLongType());
1641 }
1642 
CastToInt(const ir::AstNode * node)1643 void ETSGen::CastToInt(const ir::AstNode *node)
1644 {
1645     auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1646     switch (typeKind) {
1647         case checker::TypeFlag::INT: {
1648             return;
1649         }
1650         case checker::TypeFlag::ETS_BOOLEAN:
1651         case checker::TypeFlag::CHAR:
1652         case checker::TypeFlag::BYTE:
1653         case checker::TypeFlag::SHORT: {
1654             break;
1655         }
1656         case checker::TypeFlag::LONG: {
1657             Sa().Emit<I64toi32>(node);
1658             break;
1659         }
1660         case checker::TypeFlag::FLOAT: {
1661             Sa().Emit<F32toi32>(node);
1662             break;
1663         }
1664         case checker::TypeFlag::ETS_DYNAMIC_TYPE: {
1665             CastDynamicTo(node, checker::TypeFlag::DOUBLE);
1666             ES2PANDA_ASSERT(GetAccumulatorType() == Checker()->GlobalDoubleType());
1667             [[fallthrough]];
1668         }
1669         case checker::TypeFlag::DOUBLE: {
1670             Sa().Emit<F64toi32>(node);
1671             break;
1672         }
1673         case checker::TypeFlag::ETS_OBJECT: {
1674             break;
1675         }
1676         default: {
1677             ES2PANDA_UNREACHABLE();
1678         }
1679     }
1680 
1681     SetAccumulatorType(Checker()->GlobalIntType());
1682 }
1683 
CastToReftype(const ir::AstNode * const node,const checker::Type * const targetType,const bool unchecked)1684 void ETSGen::CastToReftype(const ir::AstNode *const node, const checker::Type *const targetType, const bool unchecked)
1685 {
1686     ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1687     ES2PANDA_ASSERT(GetAccumulatorType()->IsETSReferenceType());
1688 
1689     const auto *const sourceType = GetAccumulatorType();
1690     ES2PANDA_ASSERT(sourceType != nullptr);
1691 
1692     if (sourceType->IsETSDynamicType()) {
1693         CastDynamicToObject(node, targetType);
1694         return;
1695     }
1696     if (targetType->IsETSDynamicType()) {
1697         CastToDynamic(node, targetType->AsETSDynamicType());
1698         return;
1699     }
1700 
1701     if (targetType->IsETSStringType() && !sourceType->IsETSStringType()) {
1702         CastToString(node);
1703     }
1704 
1705     if (!unchecked) {
1706         CheckedReferenceNarrowing(node, targetType);
1707         return;
1708     }
1709 
1710     ES2PANDA_ASSERT(!targetType->IsETSTypeParameter() && !targetType->IsETSNonNullishType() &&
1711                     !targetType->IsETSPartialTypeParameter());
1712     CheckedReferenceNarrowing(node, targetType);
1713     SetAccumulatorType(targetType);
1714 }
1715 
CastDynamicToObject(const ir::AstNode * node,const checker::Type * targetType)1716 void ETSGen::CastDynamicToObject(const ir::AstNode *node, const checker::Type *targetType)
1717 {
1718     if (targetType->IsETSStringType()) {
1719         CastDynamicTo(node, checker::TypeFlag::STRING);
1720         CheckedReferenceNarrowing(node, targetType);
1721         SetAccumulatorType(targetType);
1722         return;
1723     }
1724 
1725     // NOTE(vpukhov): #14626 remove, replace targetType with interface
1726     if (targetType->IsLambdaObject()) {
1727         RegScope rs(this);
1728         VReg dynObjReg = AllocReg();
1729         StoreAccumulator(node, dynObjReg);
1730         Ra().Emit<InitobjShort>(node, targetType->AsETSObjectType()->ConstructSignatures()[0]->InternalName(),
1731                                 dynObjReg, dummyReg_);
1732         SetAccumulatorType(targetType);
1733         return;
1734     }
1735 
1736     if (targetType == Checker()->GlobalETSObjectType()) {
1737         SetAccumulatorType(targetType);
1738         return;
1739     }
1740 
1741     if (targetType->IsETSDynamicType()) {
1742         SetAccumulatorType(targetType);
1743         return;
1744     }
1745 
1746     // should be valid only for Object and [] types, other are workarounds
1747     // the DefinitelyETSNullish function has been used to add handling for null and undefined cases,
1748     // and this function will need to be refactored in the future.
1749     if (targetType->IsETSArrayType() || targetType->IsETSObjectType() || targetType->IsETSTypeParameter() ||
1750         targetType->IsETSUnionType() || targetType->IsETSFunctionType() || targetType->DefinitelyETSNullish() ||
1751         targetType->IsETSTupleType() || targetType->IsETSAnyType()) {
1752         ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1753         auto lang = GetAccumulatorType()->AsETSDynamicType()->Language();
1754         auto methodName = compiler::Signatures::Dynamic::GetObjectBuiltin(lang);
1755 
1756         RegScope rs(this);
1757         VReg dynObjReg = AllocReg();
1758         StoreAccumulator(node, dynObjReg);
1759 
1760         // try internal checkcast
1761         VReg typeReg = AllocReg();
1762         auto assemblerType = ToAssemblerType(targetType);
1763         Sa().Emit<LdaType>(node, assemblerType);
1764         StoreAccumulator(node, typeReg);
1765 
1766         Ra().Emit<CallShort, 2U>(node, methodName, dynObjReg, typeReg);
1767         EmitCheckCast(node, assemblerType);  // trick verifier
1768         SetAccumulatorType(targetType);
1769         return;
1770     }
1771 
1772     ES2PANDA_UNREACHABLE();
1773 }
1774 
CastToString(const ir::AstNode * const node)1775 void ETSGen::CastToString(const ir::AstNode *const node)
1776 {
1777     const auto *const sourceType = GetAccumulatorType();
1778     ES2PANDA_ASSERT(sourceType != nullptr);
1779     if (sourceType->IsETSStringType()) {
1780         return;
1781     }
1782 
1783     ES2PANDA_ASSERT(sourceType->IsETSReferenceType());
1784 
1785     // caller must ensure parameter is not null
1786     Ra().Emit<CallVirtAccShort, 0>(node, Signatures::BUILTIN_OBJECT_TO_STRING, dummyReg_, 0);
1787     SetAccumulatorType(Checker()->GetGlobalTypesHolder()->GlobalETSStringBuiltinType());
1788 }
1789 
CastToDynamic(const ir::AstNode * node,const checker::ETSDynamicType * type)1790 void ETSGen::CastToDynamic(const ir::AstNode *node, const checker::ETSDynamicType *type)
1791 {
1792     std::string_view methodName {};
1793     auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
1794     switch (typeKind) {
1795         case checker::TypeFlag::ETS_BOOLEAN:
1796             methodName = compiler::Signatures::Dynamic::NewBooleanBuiltin(type->Language());
1797             break;
1798         case checker::TypeFlag::CHAR:
1799         case checker::TypeFlag::BYTE:
1800         case checker::TypeFlag::SHORT:
1801         case checker::TypeFlag::INT:
1802         case checker::TypeFlag::LONG:
1803         case checker::TypeFlag::FLOAT:
1804         case checker::TypeFlag::DOUBLE:
1805             CastToDouble(node);
1806             methodName = compiler::Signatures::Dynamic::NewDoubleBuiltin(type->Language());
1807             break;
1808         case checker::TypeFlag::ETS_OBJECT:
1809         case checker::TypeFlag::ETS_TYPE_PARAMETER:
1810         case checker::TypeFlag::ETS_NONNULLISH:
1811         case checker::TypeFlag::ETS_PARTIAL_TYPE_PARAMETER:
1812         case checker::TypeFlag::ETS_UNION:  // NOTE(vpukhov): refine dynamic type cast rules
1813         case checker::TypeFlag::ETS_ANY:
1814             ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1815             if (GetAccumulatorType()->IsETSStringType()) {
1816                 methodName = compiler::Signatures::Dynamic::NewStringBuiltin(type->Language());
1817                 break;
1818             }
1819             [[fallthrough]];
1820         case checker::TypeFlag::FUNCTION:
1821             ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1822             ES2PANDA_ASSERT(!GetAccumulatorType()->IsETSMethodType());
1823             [[fallthrough]];
1824         case checker::TypeFlag::ETS_ARRAY:
1825         case checker::TypeFlag::ETS_TUPLE:
1826             methodName = compiler::Signatures::Dynamic::NewObjectBuiltin(type->Language());
1827             break;
1828         case checker::TypeFlag::ETS_DYNAMIC_TYPE:
1829             SetAccumulatorType(type);
1830             return;
1831         default:
1832             ES2PANDA_UNREACHABLE();
1833     }
1834 
1835     ES2PANDA_ASSERT(!methodName.empty());
1836 
1837     RegScope rs(this);
1838     // Load value
1839     VReg valReg = AllocReg();
1840     StoreAccumulator(node, valReg);
1841 
1842     // Create new JSValue and initialize it
1843     Ra().Emit<CallShort, 1>(node, methodName, valReg, dummyReg_);
1844     SetAccumulatorType(Checker()->GlobalBuiltinDynamicType(type->Language()));
1845 }
1846 
CastDynamicTo(const ir::AstNode * node,enum checker::TypeFlag typeFlag)1847 void ETSGen::CastDynamicTo(const ir::AstNode *node, enum checker::TypeFlag typeFlag)
1848 {
1849     std::string_view methodName {};
1850     checker::Type *objectType {};
1851     ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
1852     auto type = GetAccumulatorType()->AsETSDynamicType();
1853     switch (typeFlag) {
1854         case checker::TypeFlag::ETS_BOOLEAN: {
1855             methodName = compiler::Signatures::Dynamic::GetBooleanBuiltin(type->Language());
1856             objectType = Checker()->GlobalETSBooleanType();
1857             break;
1858         }
1859         case checker::TypeFlag::DOUBLE: {
1860             methodName = compiler::Signatures::Dynamic::GetDoubleBuiltin(type->Language());
1861             objectType = Checker()->GlobalDoubleType();
1862             break;
1863         }
1864         case checker::TypeFlag::STRING: {
1865             methodName = compiler::Signatures::Dynamic::GetStringBuiltin(type->Language());
1866             objectType = Checker()->GlobalBuiltinETSStringType();
1867             break;
1868         }
1869         default: {
1870             ES2PANDA_UNREACHABLE();
1871         }
1872     }
1873 
1874     RegScope rs(this);
1875     // Load dynamic object
1876     VReg dynObjReg = AllocReg();
1877     StoreAccumulator(node, dynObjReg);
1878 
1879     // Get value from dynamic object
1880     Ra().Emit<CallShort, 1>(node, methodName, dynObjReg, dummyReg_);
1881     SetAccumulatorType(objectType);
1882 }
1883 
ToBinaryResult(const ir::AstNode * node,Label * ifFalse)1884 void ETSGen::ToBinaryResult(const ir::AstNode *node, Label *ifFalse)
1885 {
1886     Label *end = AllocLabel();
1887     Sa().Emit<Ldai>(node, 1);
1888     Sa().Emit<Jmp>(node, end);
1889     SetLabel(node, ifFalse);
1890     Sa().Emit<Ldai>(node, 0);
1891     SetLabel(node, end);
1892     SetAccumulatorType(Checker()->GlobalETSBooleanType());
1893 }
1894 
BinaryLogic(const ir::AstNode * node,lexer::TokenType op,VReg lhs)1895 void ETSGen::BinaryLogic(const ir::AstNode *node, lexer::TokenType op, VReg lhs)
1896 {
1897     switch (op) {
1898         case lexer::TokenType::PUNCTUATOR_MOD:
1899         case lexer::TokenType::PUNCTUATOR_MOD_EQUAL: {
1900             SwapBinaryOpArgs(node, lhs);
1901             BinaryArithmetic<Mod2, Mod2Wide, Fmod2, Fmod2Wide>(node, lhs);
1902             break;
1903         }
1904         case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT:
1905         case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL: {
1906             SwapBinaryOpArgs(node, lhs);
1907             BinaryBitwiseArithmetic<Shl2, Shl2Wide>(node, lhs);
1908             break;
1909         }
1910         case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT:
1911         case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL: {
1912             SwapBinaryOpArgs(node, lhs);
1913             BinaryBitwiseArithmetic<Ashr2, Ashr2Wide>(node, lhs);
1914             break;
1915         }
1916         case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT:
1917         case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL: {
1918             SwapBinaryOpArgs(node, lhs);
1919             BinaryBitwiseArithmetic<Shr2, Shr2Wide>(node, lhs);
1920             break;
1921         }
1922         case lexer::TokenType::PUNCTUATOR_BITWISE_AND:
1923         case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL: {
1924             BinaryBitwiseArithmetic<And2, And2Wide>(node, lhs);
1925             break;
1926         }
1927         case lexer::TokenType::PUNCTUATOR_BITWISE_OR:
1928         case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: {
1929             BinaryBitwiseArithmetic<Or2, Or2Wide>(node, lhs);
1930             break;
1931         }
1932         case lexer::TokenType::PUNCTUATOR_BITWISE_XOR:
1933         case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL: {
1934             BinaryBitwiseArithmetic<Xor2, Xor2Wide>(node, lhs);
1935             break;
1936         }
1937         default: {
1938             ES2PANDA_UNREACHABLE();
1939         }
1940     }
1941 
1942     ES2PANDA_ASSERT(node->IsAssignmentExpression() || node->IsBinaryExpression());
1943     ES2PANDA_ASSERT(Checker()->Relation()->IsIdenticalTo(GetAccumulatorType(), node->AsExpression()->TsType()));
1944 }
1945 
BinaryArithmLogic(const ir::AstNode * node,lexer::TokenType op,VReg lhs)1946 void ETSGen::BinaryArithmLogic(const ir::AstNode *node, lexer::TokenType op, VReg lhs)
1947 {
1948     switch (op) {
1949         case lexer::TokenType::PUNCTUATOR_PLUS:
1950         case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: {
1951             SwapBinaryOpArgs(node, lhs);
1952             BinaryArithmetic<Add2, Add2Wide, Fadd2, Fadd2Wide>(node, lhs);
1953             break;
1954         }
1955         case lexer::TokenType::PUNCTUATOR_MINUS:
1956         case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL: {
1957             SwapBinaryOpArgs(node, lhs);
1958             BinaryArithmetic<Sub2, Sub2Wide, Fsub2, Fsub2Wide>(node, lhs);
1959             break;
1960         }
1961         case lexer::TokenType::PUNCTUATOR_MULTIPLY:
1962         case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: {
1963             SwapBinaryOpArgs(node, lhs);
1964             BinaryArithmetic<Mul2, Mul2Wide, Fmul2, Fmul2Wide>(node, lhs);
1965             break;
1966         }
1967         case lexer::TokenType::PUNCTUATOR_DIVIDE:
1968         case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL: {
1969             SwapBinaryOpArgs(node, lhs);
1970             BinaryArithmetic<Div2, Div2Wide, Fdiv2, Fdiv2Wide>(node, lhs);
1971             break;
1972         }
1973         default: {
1974             BinaryLogic(node, op, lhs);
1975             break;
1976         }
1977     }
1978 
1979     ES2PANDA_ASSERT(node->IsAssignmentExpression() || node->IsBinaryExpression());
1980     ES2PANDA_ASSERT(Checker()->Relation()->IsIdenticalTo(GetAccumulatorType(), node->AsExpression()->TsType()));
1981 }
1982 
Binary(const ir::AstNode * node,lexer::TokenType op,VReg lhs)1983 void ETSGen::Binary(const ir::AstNode *node, lexer::TokenType op, VReg lhs)
1984 {
1985     Label *ifFalse = AllocLabel();
1986     switch (op) {
1987         case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: {
1988             BinaryEquality<Jne, Jnez, Jeqz, true>(node, lhs, ifFalse);
1989             break;
1990         }
1991         case lexer::TokenType::PUNCTUATOR_EQUAL: {
1992             BinaryEquality<Jne, Jnez, Jeqz>(node, lhs, ifFalse);
1993             break;
1994         }
1995         case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: {
1996             BinaryEquality<Jeq, Jeqz, Jnez, true>(node, lhs, ifFalse);
1997             break;
1998         }
1999         case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
2000             BinaryEquality<Jeq, Jeqz, Jnez>(node, lhs, ifFalse);
2001             break;
2002         }
2003         case lexer::TokenType::PUNCTUATOR_LESS_THAN: {
2004             BinaryRelation<Jle, Jlez>(node, lhs, ifFalse);
2005             break;
2006         }
2007         case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: {
2008             BinaryRelation<Jlt, Jltz>(node, lhs, ifFalse);
2009             break;
2010         }
2011         case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
2012             BinaryRelation<Jge, Jgez>(node, lhs, ifFalse);
2013             break;
2014         }
2015         case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
2016             BinaryRelation<Jgt, Jgtz>(node, lhs, ifFalse);
2017             break;
2018         }
2019         default: {
2020             BinaryArithmLogic(node, op, lhs);
2021             break;
2022         }
2023     }
2024 
2025     ES2PANDA_ASSERT(node->IsAssignmentExpression() || node->IsBinaryExpression());
2026     ES2PANDA_ASSERT(Checker()->Relation()->IsIdenticalTo(GetAccumulatorType(), node->AsExpression()->TsType()));
2027 }
2028 
Condition(const ir::AstNode * node,lexer::TokenType op,VReg lhs,Label * ifFalse)2029 void ETSGen::Condition(const ir::AstNode *node, lexer::TokenType op, VReg lhs, Label *ifFalse)
2030 {
2031     switch (op) {
2032         case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL: {
2033             BinaryEqualityCondition<Jne, Jnez, true>(node, lhs, ifFalse);
2034             break;
2035         }
2036         case lexer::TokenType::PUNCTUATOR_EQUAL: {
2037             BinaryEqualityCondition<Jne, Jnez>(node, lhs, ifFalse);
2038             break;
2039         }
2040         case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: {
2041             BinaryEqualityCondition<Jeq, Jeqz, true>(node, lhs, ifFalse);
2042             break;
2043         }
2044         case lexer::TokenType::PUNCTUATOR_NOT_EQUAL: {
2045             BinaryEqualityCondition<Jeq, Jeqz>(node, lhs, ifFalse);
2046             break;
2047         }
2048         case lexer::TokenType::PUNCTUATOR_LESS_THAN: {
2049             BinaryRelationCondition<Jle, Jlez>(node, lhs, ifFalse);
2050             break;
2051         }
2052         case lexer::TokenType::PUNCTUATOR_LESS_THAN_EQUAL: {
2053             BinaryRelationCondition<Jlt, Jltz>(node, lhs, ifFalse);
2054             break;
2055         }
2056         case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
2057             BinaryRelationCondition<Jge, Jgez>(node, lhs, ifFalse);
2058             break;
2059         }
2060         case lexer::TokenType::PUNCTUATOR_GREATER_THAN_EQUAL: {
2061             BinaryRelationCondition<Jgt, Jgtz>(node, lhs, ifFalse);
2062             break;
2063         }
2064         default: {
2065             ES2PANDA_UNREACHABLE();
2066         }
2067     }
2068 }
2069 
2070 template <typename CondCompare, bool BEFORE_LOGICAL_NOT>
ResolveConditionalResultFloat(const ir::AstNode * node,Label * realEndLabel)2071 void ETSGen::ResolveConditionalResultFloat(const ir::AstNode *node, Label *realEndLabel)
2072 {
2073     auto type = GetAccumulatorType();
2074     ES2PANDA_ASSERT(type != nullptr);
2075     VReg tmpReg = AllocReg();
2076     StoreAccumulator(node, tmpReg);
2077     if (type->IsFloatType()) {
2078         FloatIsNaN(node);
2079     } else {
2080         DoubleIsNaN(node);
2081     }
2082     Sa().Emit<Xori>(node, 1);
2083 
2084     BranchIfFalse(node, realEndLabel);
2085     LoadAccumulator(node, tmpReg);
2086     VReg zeroReg = AllocReg();
2087 
2088     if (type->IsFloatType()) {
2089         MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::FLOAT, 0);
2090         BinaryNumberComparison<Fcmpl, Jeqz>(node, zeroReg, realEndLabel);
2091     } else {
2092         MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::DOUBLE, 0);
2093         BinaryNumberComparison<FcmplWide, Jeqz>(node, zeroReg, realEndLabel);
2094     }
2095 }
2096 
2097 template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
ResolveConditionalResultNumeric(const ir::AstNode * node,Label * ifFalse,Label ** end)2098 void ETSGen::ResolveConditionalResultNumeric(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse, Label **end)
2099 {
2100     auto type = GetAccumulatorType();
2101     ES2PANDA_ASSERT(type != nullptr);
2102     auto realEndLabel = [end, ifFalse, this](bool useFalseLabel) {
2103         if (useFalseLabel) {
2104             return ifFalse;
2105         }
2106         if ((*end) == nullptr) {
2107             (*end) = AllocLabel();
2108         }
2109         return (*end);
2110     }(USE_FALSE_LABEL);
2111     if (type->IsDoubleType() || type->IsFloatType()) {
2112         ResolveConditionalResultFloat<CondCompare, BEFORE_LOGICAL_NOT>(node, realEndLabel);
2113     }
2114     if (type->IsLongType()) {
2115         VReg zeroReg = AllocReg();
2116         MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::LONG, 0);
2117         BinaryNumberComparison<CmpWide, CondCompare>(node, zeroReg, realEndLabel);
2118     }
2119     if constexpr (BEFORE_LOGICAL_NOT) {
2120         Label *zeroPrimitive = AllocLabel();
2121         BranchIfFalse(node, zeroPrimitive);
2122         ToBinaryResult(node, zeroPrimitive);
2123     }
2124 }
2125 
2126 template <typename CondCompare, bool BEFORE_LOGICAL_NOT>
ResolveConditionalResultReference(const ir::AstNode * node)2127 void ETSGen::ResolveConditionalResultReference(const ir::AstNode *node)
2128 {
2129     auto const testString = [this, node]() {
2130         LoadStringLength(node);
2131         if constexpr (BEFORE_LOGICAL_NOT) {
2132             Label *zeroLenth = AllocLabel();
2133             BranchIfFalse(node, zeroLenth);
2134             ToBinaryResult(node, zeroLenth);
2135         }
2136     };
2137 
2138     auto type = GetAccumulatorType();
2139     ES2PANDA_ASSERT(type != nullptr);
2140     if (!type->PossiblyETSString()) {
2141         Sa().Emit<Ldai>(node, 1);
2142         return;
2143     }
2144     if (type->IsETSStringType()) {  // should also be valid for string|null|undefined
2145         testString();
2146         return;
2147     }
2148 
2149     Label *isString = AllocLabel();
2150     Label *end = AllocLabel();
2151     compiler::VReg objReg = AllocReg();
2152     StoreAccumulator(node, objReg);
2153 
2154     ES2PANDA_ASSERT(Checker()->GlobalBuiltinETSStringType() != nullptr);
2155     EmitIsInstance(node, Checker()->GlobalBuiltinETSStringType()->AssemblerName());
2156     BranchIfTrue(node, isString);
2157     Sa().Emit<Ldai>(node, 1);
2158     Branch(node, end);
2159     SetLabel(node, isString);
2160     LoadAccumulator(node, objReg);
2161     InternalCheckCast(node, Checker()->GlobalBuiltinETSStringType());  // help verifier
2162     testString();
2163     SetLabel(node, end);
2164 }
2165 
2166 template <typename CondCompare, bool BEFORE_LOGICAL_NOT, bool USE_FALSE_LABEL>
ResolveConditionalResult(const ir::AstNode * node,Label * ifFalse)2167 void ETSGen::ResolveConditionalResult(const ir::AstNode *node, [[maybe_unused]] Label *ifFalse)
2168 {
2169     auto type = GetAccumulatorType();
2170     ES2PANDA_ASSERT(type != nullptr);
2171 #ifdef PANDA_WITH_ETS
2172     if (type->IsETSReferenceType()) {
2173         VReg valReg = AllocReg();
2174         StoreAccumulator(node, valReg);
2175         EmitEtsIstrue(node, valReg);
2176         return;
2177     }
2178 #endif  // PANDA_WITH_ETS
2179     if (type->IsETSBooleanType()) {
2180         return;
2181     }
2182     Label *ifNullish {nullptr};
2183     Label *end {nullptr};
2184     if (type->PossiblyETSNullish()) {
2185         if constexpr (USE_FALSE_LABEL) {
2186             BranchIfNullish(node, ifFalse);
2187         } else {
2188             ifNullish = AllocLabel();
2189             end = AllocLabel();
2190             BranchIfNullish(node, ifNullish);
2191         }
2192     }
2193     if (type->DefinitelyETSNullish()) {
2194         // skip
2195     } else if (type->IsETSReferenceType()) {
2196         ResolveConditionalResultReference<CondCompare, BEFORE_LOGICAL_NOT>(node);
2197     } else {
2198         ResolveConditionalResultNumeric<CondCompare, BEFORE_LOGICAL_NOT, USE_FALSE_LABEL>(node, ifFalse, &end);
2199     }
2200     if (ifNullish != nullptr) {
2201         Branch(node, end);
2202         SetLabel(node, ifNullish);
2203         Sa().Emit<Ldai>(node, 0);
2204     }
2205     if (end != nullptr) {
2206         SetLabel(node, end);
2207     }
2208 }
2209 
2210 template <bool BEFORE_LOGICAL_NOT, bool FALSE_LABEL_EXISTED>
ResolveConditionalResultIfFalse(const ir::AstNode * node,Label * ifFalse)2211 void ETSGen::ResolveConditionalResultIfFalse(const ir::AstNode *node, Label *ifFalse)
2212 {
2213     ResolveConditionalResult<Jeqz, BEFORE_LOGICAL_NOT, FALSE_LABEL_EXISTED>(node, ifFalse);
2214 }
2215 
2216 template void ETSGen::ResolveConditionalResultIfFalse<false, true>(const ir::AstNode *node, Label *ifFalse);
2217 template void ETSGen::ResolveConditionalResultIfFalse<true, false>(const ir::AstNode *node, Label *ifFalse);
2218 template void ETSGen::ResolveConditionalResultIfFalse<false, false>(const ir::AstNode *node, Label *ifFalse);
2219 
2220 template <bool BEFORE_LOGICAL_NOT, bool FALSE_LABEL_EXISTED>
ResolveConditionalResultIfTrue(const ir::AstNode * node,Label * ifFalse)2221 void ETSGen::ResolveConditionalResultIfTrue(const ir::AstNode *node, Label *ifFalse)
2222 {
2223     ResolveConditionalResult<Jnez, BEFORE_LOGICAL_NOT, FALSE_LABEL_EXISTED>(node, ifFalse);
2224 }
2225 
2226 template void ETSGen::ResolveConditionalResultIfTrue<false, true>(const ir::AstNode *node, Label *ifFalse);
2227 template void ETSGen::ResolveConditionalResultIfTrue<false, false>(const ir::AstNode *node, Label *ifFalse);
2228 
2229 template <typename CondCompare, typename NegCondCompare>
BranchConditional(const ir::AstNode * node,Label * endLabel)2230 void ETSGen::BranchConditional(const ir::AstNode *node, Label *endLabel)
2231 {
2232     auto type = GetAccumulatorType();
2233     ES2PANDA_ASSERT(type != nullptr);
2234     if (type->IsETSReferenceType()) {
2235         VReg valReg = AllocReg();
2236         StoreAccumulator(node, valReg);
2237         EmitEtsIstrue(node, valReg);
2238     } else if (type->IsDoubleType() || type->IsFloatType()) {
2239         ConditionalFloat(node);
2240     } else if (type->IsLongType()) {
2241         VReg zeroReg = AllocReg();
2242         MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::LONG, 0);
2243         Ra().Emit<CmpWide>(node, zeroReg);
2244     }
2245 
2246     Sa().Emit<CondCompare>(node, endLabel);
2247 }
2248 
ConditionalFloat(const ir::AstNode * node)2249 void ETSGen::ConditionalFloat(const ir::AstNode *node)
2250 {
2251     auto type = GetAccumulatorType();
2252     ES2PANDA_ASSERT(type != nullptr);
2253     VReg tmpReg = AllocReg();
2254     VReg isNaNReg = AllocReg();
2255 
2256     StoreAccumulator(node, tmpReg);
2257     if (type->IsFloatType()) {
2258         FloatIsNaN(node);
2259     } else {
2260         DoubleIsNaN(node);
2261     }
2262     Sa().Emit<Xori>(node, 1);
2263     StoreAccumulator(node, isNaNReg);
2264     LoadAccumulator(node, tmpReg);
2265 
2266     VReg zeroReg = AllocReg();
2267 
2268     if (type->IsFloatType()) {
2269         MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::FLOAT, 0);
2270         Ra().Emit<Fcmpl>(node, zeroReg);
2271     } else {
2272         MoveImmediateToRegister(node, zeroReg, checker::TypeFlag::DOUBLE, 0);
2273         Ra().Emit<FcmplWide>(node, zeroReg);
2274     }
2275     Sa().Emit<Xori>(node, 0);
2276     Sa().Emit<And2>(node, isNaNReg);
2277 }
2278 
BranchConditionalIfFalse(const ir::AstNode * node,Label * endLabel)2279 void ETSGen::BranchConditionalIfFalse(const ir::AstNode *node, Label *endLabel)
2280 {
2281     BranchConditional<Jeqz, Jnez>(node, endLabel);
2282 }
2283 
BranchConditionalIfTrue(const ir::AstNode * node,Label * endLabel)2284 void ETSGen::BranchConditionalIfTrue(const ir::AstNode *node, Label *endLabel)
2285 {
2286     BranchConditional<Jnez, Jeqz>(node, endLabel);
2287 }
2288 
BranchIfNullish(const ir::AstNode * node,Label * ifNullish)2289 void ETSGen::BranchIfNullish(const ir::AstNode *node, Label *ifNullish)
2290 {
2291     auto *const type = GetAccumulatorType();
2292     ES2PANDA_ASSERT(type != nullptr);
2293 
2294     if (type->IsETSVoidType()) {
2295         // NOTE(): #19701 need void refactoring
2296         Sa().Emit<Jmp>(node, ifNullish);
2297     } else if (type->DefinitelyNotETSNullish()) {
2298         // no action
2299     } else if (type->DefinitelyETSNullish()) {
2300         Sa().Emit<Jmp>(node, ifNullish);
2301     } else if (!type->PossiblyETSNull()) {
2302         Sa().Emit<JeqzObj>(node, ifNullish);
2303     } else {
2304         RegScope rs(this);
2305         auto tmpObj = AllocReg();
2306         auto notTaken = AllocLabel();
2307 
2308         if (type->PossiblyETSUndefined()) {
2309             Sa().Emit<JeqzObj>(node, ifNullish);
2310         }
2311 
2312         Sa().Emit<StaObj>(node, tmpObj);
2313         EmitIsNull(node);
2314         Sa().Emit<Jeqz>(node, notTaken);
2315 
2316         Sa().Emit<LdaObj>(node, tmpObj);
2317         Sa().Emit<Jmp>(node, ifNullish);
2318 
2319         SetLabel(node, notTaken);
2320         Sa().Emit<LdaObj>(node, tmpObj);
2321     }
2322 }
2323 
BranchIfNotNullish(const ir::AstNode * node,Label * ifNotNullish)2324 void ETSGen::BranchIfNotNullish(const ir::AstNode *node, Label *ifNotNullish)
2325 {
2326     auto notTaken = AllocLabel();
2327     BranchIfNullish(node, notTaken);
2328     JumpTo(node, ifNotNullish);
2329     SetLabel(node, notTaken);
2330 }
2331 
AssumeNonNullish(const ir::AstNode * node,checker::Type const * targetType)2332 void ETSGen::AssumeNonNullish(const ir::AstNode *node, checker::Type const *targetType)
2333 {
2334     auto const *nullishType = GetAccumulatorType();
2335     ES2PANDA_ASSERT(nullishType != nullptr);
2336     if (nullishType->PossiblyETSNull()) {
2337         // clear 'null' dataflow
2338         EmitCheckCast(node, ToAssemblerType(targetType));
2339     }
2340     SetAccumulatorType(targetType);
2341 }
2342 
EmitNullishException(const ir::AstNode * node)2343 void ETSGen::EmitNullishException(const ir::AstNode *node)
2344 {
2345     RegScope ra(this);
2346     VReg undef = AllocReg();
2347     LoadAccumulatorUndefined(node);
2348     StoreAccumulator(node, undef);
2349     VReg exception = AllocReg();
2350     NewObject(node, Signatures::BUILTIN_NULLPOINTER_ERROR, exception);
2351     CallExact(node, Signatures::BUILTIN_NULLPOINTER_ERROR_CTOR, exception, undef, undef);
2352     EmitThrow(node, exception);
2353     SetAccumulatorType(nullptr);
2354 }
2355 
2356 template <bool IS_STRICT>
RefEqualityLooseDynamic(const ir::AstNode * node,VReg lhs,VReg rhs,Label * ifFalse)2357 void ETSGen::RefEqualityLooseDynamic(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse)
2358 {
2359     // NOTE(vpukhov): implement
2360     EmitEtsEquals<IS_STRICT>(node, lhs, rhs);
2361     BranchIfFalse(node, ifFalse);
2362 }
2363 
2364 template <typename IntCompare, typename CondCompare, typename DynCompare, bool IS_STRICT>
BinaryEquality(const ir::AstNode * node,VReg lhs,Label * ifFalse)2365 void ETSGen::BinaryEquality(const ir::AstNode *node, VReg lhs, Label *ifFalse)
2366 {
2367     BinaryEqualityCondition<IntCompare, CondCompare, IS_STRICT>(node, lhs, ifFalse);
2368     ToBinaryResult(node, ifFalse);
2369     SetAccumulatorType(Checker()->GlobalETSBooleanType());
2370 }
2371 
2372 template <typename IntCompare, typename CondCompare, bool IS_STRICT>
BinaryEqualityCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)2373 void ETSGen::BinaryEqualityCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
2374 {
2375     if (targetType_->IsETSReferenceType()) {
2376         RegScope rs(this);
2377         VReg arg0 = AllocReg();
2378         StoreAccumulator(node, arg0);
2379         InverseCondition(
2380             node, [this, node, lhs, arg0](Label *tgt) { RefEqualityLoose<IS_STRICT>(node, lhs, arg0, tgt); }, ifFalse,
2381             std::is_same_v<CondCompare, Jeqz>);
2382         SetAccumulatorType(Checker()->GlobalETSBooleanType());
2383         return;
2384     }
2385 
2386     auto typeKind = checker::ETSChecker::TypeKind(targetType_);
2387 
2388     switch (typeKind) {
2389         case checker::TypeFlag::DOUBLE: {
2390             BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
2391             break;
2392         }
2393         case checker::TypeFlag::FLOAT: {
2394             BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
2395             break;
2396         }
2397         case checker::TypeFlag::LONG: {
2398             BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
2399             break;
2400         }
2401         case checker::TypeFlag::ETS_BOOLEAN:
2402         case checker::TypeFlag::BYTE:
2403         case checker::TypeFlag::CHAR:
2404         case checker::TypeFlag::SHORT:
2405         case checker::TypeFlag::INT: {
2406             Ra().Emit<IntCompare>(node, lhs, ifFalse);
2407             break;
2408         }
2409         default: {
2410             ES2PANDA_UNREACHABLE();
2411         }
2412     }
2413 
2414     SetAccumulatorType(Checker()->GlobalETSBooleanType());
2415 }
2416 
2417 template <typename IntCompare, typename CondCompare>
BinaryRelation(const ir::AstNode * node,VReg lhs,Label * ifFalse)2418 void ETSGen::BinaryRelation(const ir::AstNode *node, VReg lhs, Label *ifFalse)
2419 {
2420     BinaryRelationCondition<IntCompare, CondCompare>(node, lhs, ifFalse);
2421     ToBinaryResult(node, ifFalse);
2422 }
2423 
2424 template <typename IntCompare, typename CondCompare>
BinaryRelationCondition(const ir::AstNode * node,VReg lhs,Label * ifFalse)2425 void ETSGen::BinaryRelationCondition(const ir::AstNode *node, VReg lhs, Label *ifFalse)
2426 {
2427     auto typeKind = checker::ETSChecker::TypeKind(targetType_);
2428 
2429     switch (typeKind) {
2430         case checker::TypeFlag::DOUBLE: {
2431             BinaryFloatingPointComparison<FcmpgWide, FcmplWide, CondCompare>(node, lhs, ifFalse);
2432             break;
2433         }
2434         case checker::TypeFlag::FLOAT: {
2435             BinaryFloatingPointComparison<Fcmpg, Fcmpl, CondCompare>(node, lhs, ifFalse);
2436             break;
2437         }
2438         case checker::TypeFlag::LONG: {
2439             BinaryNumberComparison<CmpWide, CondCompare>(node, lhs, ifFalse);
2440             break;
2441         }
2442         case checker::TypeFlag::ETS_BOOLEAN:
2443         case checker::TypeFlag::BYTE:
2444         case checker::TypeFlag::SHORT:
2445         case checker::TypeFlag::CHAR:
2446         case checker::TypeFlag::INT: {
2447             Ra().Emit<IntCompare>(node, lhs, ifFalse);
2448             break;
2449         }
2450         default: {
2451             ES2PANDA_UNREACHABLE();
2452         }
2453     }
2454 
2455     SetAccumulatorType(Checker()->GlobalETSBooleanType());
2456 }
2457 
2458 template <typename IntOp, typename LongOp, typename FloatOp, typename DoubleOp>
BinaryArithmetic(const ir::AstNode * node,VReg lhs)2459 void ETSGen::BinaryArithmetic(const ir::AstNode *node, VReg lhs)
2460 {
2461     auto typeKind = checker::ETSChecker::TypeKind(targetType_);
2462 
2463     switch (typeKind) {
2464         case checker::TypeFlag::DOUBLE: {
2465             Ra().Emit<DoubleOp>(node, lhs);
2466             SetAccumulatorType(Checker()->GlobalDoubleType());
2467             break;
2468         }
2469         case checker::TypeFlag::FLOAT: {
2470             Ra().Emit<FloatOp>(node, lhs);
2471             SetAccumulatorType(Checker()->GlobalFloatType());
2472             break;
2473         }
2474         default: {
2475             BinaryBitwiseArithmetic<IntOp, LongOp>(node, lhs);
2476             break;
2477         }
2478     }
2479 
2480     ApplyCast(node, node->AsExpression()->TsType());
2481 }
2482 
2483 template <typename IntOp, typename LongOp>
BinaryBitwiseArithmetic(const ir::AstNode * node,VReg lhs)2484 void ETSGen::BinaryBitwiseArithmetic(const ir::AstNode *node, VReg lhs)
2485 {
2486     auto typeKind = checker::ETSChecker::TypeKind(targetType_);
2487 
2488     switch (typeKind) {
2489         case checker::TypeFlag::LONG: {
2490             Ra().Emit<LongOp>(node, lhs);
2491             SetAccumulatorType(Checker()->GlobalLongType());
2492             break;
2493         }
2494         case checker::TypeFlag::BYTE:
2495         case checker::TypeFlag::SHORT:
2496         case checker::TypeFlag::CHAR:
2497         case checker::TypeFlag::INT: {
2498             Ra().Emit<IntOp>(node, lhs);
2499             SetAccumulatorType(Checker()->GlobalIntType());
2500             break;
2501         }
2502         case checker::TypeFlag::ETS_BOOLEAN: {
2503             Ra().Emit<IntOp>(node, lhs);
2504             SetAccumulatorType(Checker()->GlobalETSBooleanType());
2505             break;
2506         }
2507         default: {
2508             ES2PANDA_UNREACHABLE();
2509         }
2510     }
2511 
2512     ApplyCast(node, node->AsExpression()->TsType());
2513 }
2514 
2515 template <bool IS_STRICT>
HandleDefinitelyNullishEquality(const ir::AstNode * node,VReg lhs,VReg rhs,Label * ifFalse)2516 void ETSGen::HandleDefinitelyNullishEquality(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse)
2517 {
2518     if constexpr (IS_STRICT) {
2519         LoadAccumulator(node, lhs);
2520         Ra().Emit<JneObj>(node, rhs, ifFalse);
2521     } else {
2522         auto *checker = const_cast<checker::ETSChecker *>(Checker());
2523         auto ltype = checker->GetNonConstantType(const_cast<checker::Type *>(GetVRegType(lhs)));
2524         ES2PANDA_ASSERT(ltype != nullptr);
2525         LoadAccumulator(node, ltype->DefinitelyETSNullish() ? rhs : lhs);
2526         BranchIfNotNullish(node, ifFalse);
2527     }
2528 }
2529 
2530 template <bool IS_STRICT>
HandlePossiblyNullishEquality(const ir::AstNode * node,VReg lhs,VReg rhs,Label * ifFalse,Label * ifTrue)2531 void ETSGen::HandlePossiblyNullishEquality(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse, Label *ifTrue)
2532 {
2533     Label *ifLhsNullish = AllocLabel();
2534     Label *out = AllocLabel();
2535 
2536     LoadAccumulator(node, lhs);
2537     BranchIfNullish(node, ifLhsNullish);
2538 
2539     LoadAccumulator(node, rhs);
2540     BranchIfNullish(node, ifFalse);
2541     JumpTo(node, out);
2542 
2543     SetLabel(node, ifLhsNullish);
2544     if constexpr (IS_STRICT) {
2545         Ra().Emit<JneObj>(node, rhs, ifFalse);
2546     } else {
2547         LoadAccumulator(node, rhs);
2548         BranchIfNotNullish(node, ifFalse);
2549     }
2550     JumpTo(node, ifTrue);
2551 
2552     SetLabel(node, out);
2553     SetAccumulatorType(nullptr);
2554 }
2555 
SelectLooseObjComparator(checker::ETSChecker * checker,checker::Type * lhs,checker::Type * rhs)2556 static std::optional<std::pair<checker::Type const *, util::StringView>> SelectLooseObjComparator(
2557     checker::ETSChecker *checker, checker::Type *lhs, checker::Type *rhs)
2558 {
2559     auto alhs = checker->GetApparentType(checker->GetNonNullishType(lhs));
2560     auto arhs = checker->GetApparentType(checker->GetNonNullishType(rhs));
2561     ES2PANDA_ASSERT(alhs != nullptr && arhs != nullptr);
2562     alhs = alhs->IsETSStringType() ? checker->GlobalBuiltinETSStringType() : alhs;
2563     arhs = arhs->IsETSStringType() ? checker->GlobalBuiltinETSStringType() : arhs;
2564     if (!alhs->IsETSObjectType() || !arhs->IsETSObjectType()) {
2565         return std::nullopt;
2566     }
2567     if (!checker->Relation()->IsIdenticalTo(alhs, arhs)) {
2568         return std::nullopt;
2569     }
2570     auto obj = alhs->AsETSObjectType();
2571     if (!obj->HasObjectFlag(checker::ETSObjectFlags::VALUE_TYPED)) {
2572         return std::nullopt;
2573     }
2574     // NOTE(vpukhov): emit faster code
2575     auto methodSig =
2576         util::UString(std::string(obj->AssemblerName()) + ".equals:std.core.Object;u1;", checker->Allocator()).View();
2577     return std::make_pair(checker->GetNonConstantType(obj), methodSig);
2578 }
2579 
2580 template <typename LongOp, typename IntOp, typename DoubleOp, typename FloatOp>
UpdateOperator(const ir::AstNode * node)2581 void ETSGen::UpdateOperator(const ir::AstNode *node)
2582 {
2583     switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
2584         case checker::TypeFlag::LONG: {
2585             RegScope scope(this);
2586             VReg reg = AllocReg();
2587             Ra().Emit<MoviWide>(node, reg, 1LL);
2588             Ra().Emit<LongOp>(node, reg);
2589             break;
2590         }
2591         case checker::TypeFlag::INT: {
2592             Sa().Emit<IntOp>(node, 1);
2593             break;
2594         }
2595         case checker::TypeFlag::CHAR: {
2596             Sa().Emit<IntOp>(node, 1);
2597             Sa().Emit<I32tou16>(node);
2598             break;
2599         }
2600         case checker::TypeFlag::SHORT: {
2601             Sa().Emit<IntOp>(node, 1);
2602             Sa().Emit<I32toi16>(node);
2603             break;
2604         }
2605         case checker::TypeFlag::BYTE: {
2606             Sa().Emit<IntOp>(node, 1);
2607             Sa().Emit<I32toi8>(node);
2608             break;
2609         }
2610         case checker::TypeFlag::DOUBLE: {
2611             RegScope scope(this);
2612             VReg reg = AllocReg();
2613             Ra().Emit<FmoviWide>(node, reg, 1.0);
2614             Ra().Emit<DoubleOp>(node, reg);
2615             break;
2616         }
2617         case checker::TypeFlag::FLOAT: {
2618             RegScope scope(this);
2619             VReg reg = AllocReg();
2620             Ra().Emit<Fmovi>(node, reg, 1.0F);
2621             Ra().Emit<FloatOp>(node, reg);
2622             break;
2623         }
2624         default: {
2625             ES2PANDA_UNREACHABLE();
2626         }
2627     }
2628 }
2629 
2630 template <bool IS_STRICT>
RefEqualityLoose(const ir::AstNode * node,VReg lhs,VReg rhs,Label * ifFalse)2631 void ETSGen::RefEqualityLoose(const ir::AstNode *node, VReg lhs, VReg rhs, Label *ifFalse)
2632 {
2633     auto *checker = const_cast<checker::ETSChecker *>(Checker());
2634     auto ltype = checker->GetNonConstantType(const_cast<checker::Type *>(GetVRegType(lhs)));
2635     auto rtype = checker->GetNonConstantType(const_cast<checker::Type *>(GetVRegType(rhs)));
2636     ES2PANDA_ASSERT(ltype != nullptr && rtype != nullptr);
2637     if (ltype->IsETSDynamicType() || rtype->IsETSDynamicType()) {
2638         RefEqualityLooseDynamic<IS_STRICT>(node, lhs, rhs, ifFalse);
2639         return;
2640     }
2641 
2642     if (ltype->DefinitelyETSNullish() || rtype->DefinitelyETSNullish()) {
2643         HandleDefinitelyNullishEquality<IS_STRICT>(node, lhs, rhs, ifFalse);
2644     } else if (!ltype->PossiblyETSValueTypedExceptNullish() || !rtype->PossiblyETSValueTypedExceptNullish()) {
2645         auto ifTrue = AllocLabel();
2646         if ((ltype->PossiblyETSUndefined() && rtype->PossiblyETSNull()) ||
2647             (rtype->PossiblyETSUndefined() && ltype->PossiblyETSNull())) {
2648             HandlePossiblyNullishEquality(node, lhs, rhs, ifFalse, ifTrue);
2649         }
2650         LoadAccumulator(node, lhs);
2651         Ra().Emit<JneObj>(node, rhs, ifFalse);
2652         SetLabel(node, ifTrue);
2653     } else if (auto spec = SelectLooseObjComparator(  // try to select specific type
2654                                                       // CC-OFFNXT(G.FMT.06-CPP) project code style
2655                    const_cast<checker::ETSChecker *>(Checker()), const_cast<checker::Type *>(ltype),
2656                    const_cast<checker::Type *>(rtype));  // CC-OFF(G.FMT.02) project code style
2657                spec.has_value()) {                       // CC-OFF(G.FMT.02-CPP) project code style
2658         auto ifTrue = AllocLabel();
2659         if (ltype->PossiblyETSNullish() || rtype->PossiblyETSNullish()) {
2660             HandlePossiblyNullishEquality<IS_STRICT>(node, lhs, rhs, ifFalse, ifTrue);
2661         }
2662         LoadAccumulator(node, rhs);
2663         AssumeNonNullish(node, spec->first);
2664         StoreAccumulator(node, rhs);
2665         LoadAccumulator(node, lhs);
2666         AssumeNonNullish(node, spec->first);
2667         CallExact(node, spec->second, lhs, rhs);
2668         BranchIfFalse(node, ifFalse);
2669         SetLabel(node, ifTrue);
2670     } else {
2671         EmitEtsEquals<IS_STRICT>(node, lhs, rhs);
2672         BranchIfFalse(node, ifFalse);
2673     }
2674     SetAccumulatorType(nullptr);
2675 }
2676 
CompileStatements(const ArenaVector<ir::Statement * > & statements)2677 void ETSGen::CompileStatements(const ArenaVector<ir::Statement *> &statements)
2678 {
2679     for (const auto *stmt : statements) {
2680         stmt->Compile(this);
2681     }
2682 }
2683 
Negate(const ir::AstNode * node)2684 void ETSGen::Negate(const ir::AstNode *node)
2685 {
2686     auto typeKind = checker::ETSChecker::TypeKind(GetAccumulatorType());
2687 
2688     switch (typeKind) {
2689         case checker::TypeFlag::BYTE:
2690         case checker::TypeFlag::SHORT:
2691         case checker::TypeFlag::CHAR:
2692         case checker::TypeFlag::INT: {
2693             Sa().Emit<Neg>(node);
2694             return;
2695         }
2696         case checker::TypeFlag::LONG: {
2697             Sa().Emit<NegWide>(node);
2698             break;
2699         }
2700         case checker::TypeFlag::FLOAT: {
2701             Sa().Emit<Fneg>(node);
2702             break;
2703         }
2704         case checker::TypeFlag::DOUBLE: {
2705             Sa().Emit<FnegWide>(node);
2706             break;
2707         }
2708         default: {
2709             ES2PANDA_UNREACHABLE();
2710         }
2711     }
2712 }
2713 
LogicalNot(const ir::AstNode * node)2714 void ETSGen::LogicalNot(const ir::AstNode *node)
2715 {
2716     ResolveConditionalResultIfFalse<true, false>(node);
2717     Sa().Emit<Xori>(node, 1);
2718     SetAccumulatorType(Checker()->GlobalETSBooleanType());
2719 }
2720 
LoadAccumulatorByte(const ir::AstNode * node,int8_t number)2721 void ETSGen::LoadAccumulatorByte(const ir::AstNode *node, int8_t number)
2722 {
2723     LoadAccumulatorNumber<int8_t>(node, number, checker::TypeFlag::BYTE);
2724 }
2725 
LoadAccumulatorShort(const ir::AstNode * node,int16_t number)2726 void ETSGen::LoadAccumulatorShort(const ir::AstNode *node, int16_t number)
2727 {
2728     LoadAccumulatorNumber<int16_t>(node, number, checker::TypeFlag::SHORT);
2729 }
2730 
LoadAccumulatorInt(const ir::AstNode * node,int32_t number)2731 void ETSGen::LoadAccumulatorInt(const ir::AstNode *node, int32_t number)
2732 {
2733     LoadAccumulatorNumber<int32_t>(node, number, checker::TypeFlag::INT);
2734 }
2735 
LoadAccumulatorWideInt(const ir::AstNode * node,int64_t number)2736 void ETSGen::LoadAccumulatorWideInt(const ir::AstNode *node, int64_t number)
2737 {
2738     LoadAccumulatorNumber<int64_t>(node, number, checker::TypeFlag::LONG);
2739 }
2740 
LoadAccumulatorFloat(const ir::AstNode * node,float number)2741 void ETSGen::LoadAccumulatorFloat(const ir::AstNode *node, float number)
2742 {
2743     LoadAccumulatorNumber<float>(node, number, checker::TypeFlag::FLOAT);
2744 }
2745 
LoadAccumulatorDouble(const ir::AstNode * node,double number)2746 void ETSGen::LoadAccumulatorDouble(const ir::AstNode *node, double number)
2747 {
2748     LoadAccumulatorNumber<double>(node, number, checker::TypeFlag::DOUBLE);
2749 }
2750 
LoadAccumulatorBoolean(const ir::AstNode * node,bool value)2751 void ETSGen::LoadAccumulatorBoolean(const ir::AstNode *node, bool value)
2752 {
2753     Sa().Emit<Ldai>(node, value ? 1 : 0);
2754     SetAccumulatorType(Checker()->GlobalETSBooleanType());
2755     ApplyConversion(node, Checker()->GlobalETSBooleanType());
2756 }
2757 
LoadAccumulatorString(const ir::AstNode * node,util::StringView str)2758 void ETSGen::LoadAccumulatorString(const ir::AstNode *node, util::StringView str)
2759 {
2760     Sa().Emit<LdaStr>(node, str);
2761     SetAccumulatorType(Checker()->GlobalETSStringLiteralType());
2762 }
2763 
LoadAccumulatorBigInt(const ir::AstNode * node,util::StringView str)2764 void ETSGen::LoadAccumulatorBigInt(const ir::AstNode *node, util::StringView str)
2765 {
2766     Sa().Emit<LdaStr>(node, str);
2767     SetAccumulatorType(Checker()->GlobalETSBigIntType());
2768 }
2769 
LoadAccumulatorUndefined(const ir::AstNode * node)2770 void ETSGen::LoadAccumulatorUndefined(const ir::AstNode *node)
2771 {
2772     Sa().Emit<LdaNull>(node);
2773     SetAccumulatorType(Checker()->GlobalETSUndefinedType());
2774 }
2775 
LoadAccumulatorNull(const ir::AstNode * node)2776 void ETSGen::LoadAccumulatorNull([[maybe_unused]] const ir::AstNode *node)
2777 {
2778 #ifdef PANDA_WITH_ETS
2779     Sa().Emit<EtsLdnullvalue>(node);
2780     SetAccumulatorType(Checker()->GlobalETSNullType());
2781 #else
2782     ES2PANDA_UNREACHABLE();
2783 #endif  // PANDA_WITH_ETS
2784 }
2785 
LoadAccumulatorChar(const ir::AstNode * node,char16_t value)2786 void ETSGen::LoadAccumulatorChar(const ir::AstNode *node, char16_t value)
2787 {
2788     Sa().Emit<Ldai>(node, value);
2789     SetAccumulatorType(Checker()->GlobalCharType());
2790     ApplyConversion(node, Checker()->GlobalCharType());
2791 }
2792 
Unary(const ir::AstNode * node,lexer::TokenType op)2793 void ETSGen::Unary(const ir::AstNode *node, lexer::TokenType op)
2794 {
2795     switch (op) {
2796         case lexer::TokenType::PUNCTUATOR_PLUS:
2797             break;  // NOP -> Unary numeric promotion is performed
2798         case lexer::TokenType::PUNCTUATOR_MINUS:
2799             UnaryMinus(node);
2800             break;
2801         case lexer::TokenType::PUNCTUATOR_TILDE:
2802             UnaryTilde(node);
2803             break;
2804         case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK:
2805             LogicalNot(node);
2806             break;
2807         default:
2808             ES2PANDA_UNREACHABLE();
2809     }
2810 }
2811 
UnaryMinus(const ir::AstNode * node)2812 void ETSGen::UnaryMinus(const ir::AstNode *node)
2813 {
2814     ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
2815     if (GetAccumulatorType()->IsETSBigIntType()) {
2816         const VReg value = AllocReg();
2817         StoreAccumulator(node, value);
2818         CallExact(node, Signatures::BUILTIN_BIGINT_NEGATE, value);
2819         return;
2820     }
2821 
2822     switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
2823         case checker::TypeFlag::LONG: {
2824             Sa().Emit<NegWide>(node);
2825             break;
2826         }
2827         case checker::TypeFlag::INT:
2828         case checker::TypeFlag::SHORT:
2829         case checker::TypeFlag::CHAR:
2830         case checker::TypeFlag::BYTE: {
2831             Sa().Emit<Neg>(node);
2832             break;
2833         }
2834         case checker::TypeFlag::DOUBLE: {
2835             Sa().Emit<FnegWide>(node);
2836             break;
2837         }
2838         case checker::TypeFlag::FLOAT: {
2839             Sa().Emit<Fneg>(node);
2840             break;
2841         }
2842         default: {
2843             ES2PANDA_UNREACHABLE();
2844         }
2845     }
2846 }
2847 
UnaryTilde(const ir::AstNode * node)2848 void ETSGen::UnaryTilde(const ir::AstNode *node)
2849 {
2850     ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
2851     if (GetAccumulatorType()->IsETSBigIntType()) {
2852         const VReg value = AllocReg();
2853         StoreAccumulator(node, value);
2854         CallExact(node, Signatures::BUILTIN_BIGINT_OPERATOR_BITWISE_NOT, value);
2855         SetAccumulatorType(Checker()->GlobalETSBigIntType());
2856         return;
2857     }
2858 
2859     switch (checker::ETSChecker::ETSType(GetAccumulatorType())) {
2860         case checker::TypeFlag::LONG: {
2861             Sa().Emit<NotWide>(node);
2862             break;
2863         }
2864         case checker::TypeFlag::INT:
2865         case checker::TypeFlag::SHORT:
2866         case checker::TypeFlag::CHAR:
2867         case checker::TypeFlag::BYTE: {
2868             Sa().Emit<Not>(node);
2869             break;
2870         }
2871         default: {
2872             ES2PANDA_UNREACHABLE();
2873         }
2874     }
2875 }
2876 
Update(const ir::AstNode * node,lexer::TokenType op)2877 void ETSGen::Update(const ir::AstNode *node, lexer::TokenType op)
2878 {
2879     switch (op) {
2880         case lexer::TokenType::PUNCTUATOR_PLUS_PLUS: {
2881             UpdateOperator<Add2Wide, Addi, Fadd2Wide, Fadd2>(node);
2882             break;
2883         }
2884         case lexer::TokenType::PUNCTUATOR_MINUS_MINUS: {
2885             UpdateOperator<Sub2Wide, Subi, Fsub2Wide, Fsub2>(node);
2886             break;
2887         }
2888         default: {
2889             ES2PANDA_UNREACHABLE();
2890         }
2891     }
2892 }
2893 
UpdateBigInt(const ir::Expression * node,VReg arg,lexer::TokenType op)2894 void ETSGen::UpdateBigInt(const ir::Expression *node, VReg arg, lexer::TokenType op)
2895 {
2896     switch (op) {
2897         case lexer::TokenType::PUNCTUATOR_PLUS_PLUS: {
2898             CallBigIntUnaryOperator(node, arg, compiler::Signatures::BUILTIN_BIGINT_OPERATOR_INCREMENT);
2899             break;
2900         }
2901         case lexer::TokenType::PUNCTUATOR_MINUS_MINUS: {
2902             CallBigIntUnaryOperator(node, arg, compiler::Signatures::BUILTIN_BIGINT_OPERATOR_DECREMENT);
2903             break;
2904         }
2905         default: {
2906             ES2PANDA_UNREACHABLE();
2907         }
2908     }
2909 }
2910 
StringBuilderAppend(const ir::AstNode * node,VReg builder)2911 void ETSGen::StringBuilderAppend(const ir::AstNode *node, VReg builder)
2912 {
2913     RegScope rs(this);
2914     util::StringView signature {};
2915 
2916     node->Compile(this);
2917 
2918     std::unordered_map<checker::TypeFlag, std::string_view> typeFlagToSignaturesMap {
2919         {checker::TypeFlag::ETS_BOOLEAN, Signatures::BUILTIN_STRING_BUILDER_APPEND_BOOLEAN},
2920         {checker::TypeFlag::CHAR, Signatures::BUILTIN_STRING_BUILDER_APPEND_CHAR},
2921         {checker::TypeFlag::SHORT, Signatures::BUILTIN_STRING_BUILDER_APPEND_INT},
2922         {checker::TypeFlag::BYTE, Signatures::BUILTIN_STRING_BUILDER_APPEND_INT},
2923         {checker::TypeFlag::INT, Signatures::BUILTIN_STRING_BUILDER_APPEND_INT},
2924         {checker::TypeFlag::LONG, Signatures::BUILTIN_STRING_BUILDER_APPEND_LONG},
2925         {checker::TypeFlag::FLOAT, Signatures::BUILTIN_STRING_BUILDER_APPEND_FLOAT},
2926         {checker::TypeFlag::DOUBLE, Signatures::BUILTIN_STRING_BUILDER_APPEND_DOUBLE},
2927     };
2928 
2929     auto search = typeFlagToSignaturesMap.find(checker::ETSChecker::ETSType(GetAccumulatorType()));
2930     if (search != typeFlagToSignaturesMap.end()) {
2931         signature = search->second;
2932     } else {
2933         signature = Signatures::BUILTIN_STRING_BUILDER_APPEND_BUILTIN_STRING;
2934     }
2935 
2936     ES2PANDA_ASSERT(GetAccumulatorType() != nullptr);
2937     if (GetAccumulatorType()->IsETSReferenceType() && !GetAccumulatorType()->IsETSStringType()) {
2938         if (GetAccumulatorType()->PossiblyETSUndefined()) {
2939             Label *ifUndefined = AllocLabel();
2940             Label *end = AllocLabel();
2941             BranchIfUndefined(node, ifUndefined);
2942             Ra().Emit<CallVirtAccShort, 0>(node, Signatures::BUILTIN_OBJECT_TO_STRING, dummyReg_, 0);
2943             JumpTo(node, end);
2944 
2945             SetLabel(node, ifUndefined);
2946             LoadAccumulatorString(node, "undefined");
2947 
2948             SetLabel(node, end);
2949         } else {
2950             Ra().Emit<CallVirtAccShort, 0>(node, Signatures::BUILTIN_OBJECT_TO_STRING, dummyReg_, 0);
2951         }
2952     }
2953 
2954     VReg arg0 = AllocReg();
2955     StoreAccumulator(node, arg0);
2956 
2957     CallExact(node, signature, builder, arg0);
2958     SetAccumulatorType(Checker()->GetGlobalTypesHolder()->GlobalStringBuilderBuiltinType());
2959 }
2960 
AppendString(const ir::Expression * const expr,const VReg builder)2961 void ETSGen::AppendString(const ir::Expression *const expr, const VReg builder)
2962 {
2963     ES2PANDA_ASSERT((expr->IsBinaryExpression() &&
2964                      expr->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS) ||
2965                     (expr->IsAssignmentExpression() &&
2966                      expr->AsAssignmentExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS_EQUAL));
2967 
2968     if (expr->IsBinaryExpression()) {
2969         StringBuilder(expr->AsBinaryExpression()->Left(), expr->AsBinaryExpression()->Right(), builder);
2970     } else {
2971         StringBuilder(expr->AsAssignmentExpression()->Left(), expr->AsAssignmentExpression()->Right(), builder);
2972     }
2973 }
2974 
StringBuilder(const ir::Expression * const left,const ir::Expression * const right,const VReg builder)2975 void ETSGen::StringBuilder(const ir::Expression *const left, const ir::Expression *const right, const VReg builder)
2976 {
2977     if (left->IsBinaryExpression() && left->TsType()->IsETSStringType()) {
2978         AppendString(left->AsBinaryExpression(), builder);
2979     } else {
2980         StringBuilderAppend(left, builder);
2981     }
2982 
2983     StringBuilderAppend(right, builder);
2984 }
2985 
BuildString(const ir::Expression * node)2986 void ETSGen::BuildString(const ir::Expression *node)
2987 {
2988     RegScope rs(this);
2989 
2990     Ra().Emit<InitobjShort, 0>(node, Signatures::BUILTIN_STRING_BUILDER_CTOR, dummyReg_, dummyReg_);
2991     SetAccumulatorType(Checker()->GlobalStringBuilderBuiltinType());
2992 
2993     auto builder = AllocReg();
2994     StoreAccumulator(node, builder);
2995 
2996     AppendString(node, builder);
2997     CallExact(node, Signatures::BUILTIN_STRING_BUILDER_TO_STRING, builder);
2998 
2999     SetAccumulatorType(node->TsType());
3000 }
3001 
CallBigIntUnaryOperator(const ir::Expression * node,VReg arg,const util::StringView signature)3002 void ETSGen::CallBigIntUnaryOperator(const ir::Expression *node, VReg arg, const util::StringView signature)
3003 {
3004     LoadAccumulator(node, arg);
3005     CallExact(node, signature, arg);
3006     SetAccumulatorType(Checker()->GlobalETSBigIntType());
3007 }
3008 
CallBigIntBinaryOperator(const ir::Expression * node,VReg lhs,VReg rhs,const util::StringView signature)3009 void ETSGen::CallBigIntBinaryOperator(const ir::Expression *node, VReg lhs, VReg rhs, const util::StringView signature)
3010 {
3011     LoadAccumulator(node, lhs);
3012     CallExact(node, signature, lhs, rhs);
3013     SetAccumulatorType(Checker()->GlobalETSBigIntType());
3014 }
3015 
CallBigIntBinaryComparison(const ir::Expression * node,VReg lhs,VReg rhs,const util::StringView signature)3016 void ETSGen::CallBigIntBinaryComparison(const ir::Expression *node, VReg lhs, VReg rhs,
3017                                         const util::StringView signature)
3018 {
3019     LoadAccumulator(node, lhs);
3020     CallExact(node, signature, lhs, rhs);
3021     SetAccumulatorType(Checker()->GlobalETSBooleanType());
3022 }
3023 
BuildTemplateString(const ir::TemplateLiteral * node)3024 void ETSGen::BuildTemplateString(const ir::TemplateLiteral *node)
3025 {
3026     RegScope rs(this);
3027 
3028     Ra().Emit<InitobjShort, 0>(node, Signatures::BUILTIN_STRING_BUILDER_CTOR, dummyReg_, dummyReg_);
3029     SetAccumulatorType(Checker()->GlobalStringBuilderBuiltinType());
3030 
3031     auto builder = AllocReg();
3032     StoreAccumulator(node, builder);
3033 
3034     // Just to reduce extra nested level(s):
3035     auto const appendExpressions = [this, &builder](ArenaVector<ir::Expression *> const &expressions,
3036                                                     ArenaVector<ir::TemplateElement *> const &quasis) -> void {
3037         auto const num = expressions.size();
3038         std::size_t i = 0U;
3039 
3040         while (i < num) {
3041             StringBuilderAppend(expressions[i], builder);
3042             if (!quasis[++i]->Raw().Empty()) {
3043                 StringBuilderAppend(quasis[i], builder);
3044             }
3045         }
3046     };
3047 
3048     if (auto const &quasis = node->Quasis(); !quasis.empty()) {
3049         if (!quasis[0]->Raw().Empty()) {
3050             StringBuilderAppend(quasis[0], builder);
3051         }
3052 
3053         if (auto const &expressions = node->Expressions(); !expressions.empty()) {
3054             appendExpressions(expressions, quasis);
3055         }
3056     }
3057 
3058     CallExact(node, Signatures::BUILTIN_STRING_BUILDER_TO_STRING, builder);
3059 
3060     SetAccumulatorType(node->TsType());
3061 }
3062 
NewObject(const ir::AstNode * const node,const util::StringView name,VReg athis)3063 void ETSGen::NewObject(const ir::AstNode *const node, const util::StringView name, VReg athis)
3064 {
3065     Ra().Emit<Newobj>(node, athis, name);
3066     SetVRegType(athis, Checker()->GlobalETSObjectType());
3067 }
3068 
NewArray(const ir::AstNode * const node,const VReg arr,const VReg dim,const checker::Type * const arrType)3069 void ETSGen::NewArray(const ir::AstNode *const node, const VReg arr, const VReg dim, const checker::Type *const arrType)
3070 {
3071     std::stringstream ss;
3072     arrType->ToAssemblerTypeWithRank(ss);
3073     const auto res = ProgElement()->Strings().emplace(ss.str());
3074 
3075     Ra().Emit<Newarr>(node, arr, dim, util::StringView(*res.first));
3076     SetVRegType(arr, arrType);
3077 }
3078 
LoadResizableArrayLength(const ir::AstNode * node)3079 void ETSGen::LoadResizableArrayLength(const ir::AstNode *node)
3080 {
3081     Ra().Emit<CallAccShort, 0>(node, Signatures::BUILTIN_ARRAY_LENGTH, dummyReg_, 0);
3082     Sa().Emit<F64toi32>(node);
3083     SetAccumulatorType(Checker()->GlobalIntType());
3084 }
3085 
LoadResizableArrayElement(const ir::AstNode * node,const VReg arrObj,const VReg arrIndex)3086 void ETSGen::LoadResizableArrayElement(const ir::AstNode *node, const VReg arrObj, const VReg arrIndex)
3087 {
3088     auto *vRegType = GetVRegType(arrObj);
3089     ES2PANDA_ASSERT(vRegType != nullptr);
3090     auto *elementType = vRegType->AsETSResizableArrayType()->ElementType();
3091     Ra().Emit<CallVirtShort>(node, Signatures::BUILTIN_ARRAY_GET_ELEMENT, arrObj, arrIndex);
3092     SetAccumulatorType(elementType);
3093 }
3094 
LoadArrayLength(const ir::AstNode * node,VReg arrayReg)3095 void ETSGen::LoadArrayLength(const ir::AstNode *node, VReg arrayReg)
3096 {
3097     Ra().Emit<Lenarr>(node, arrayReg);
3098     SetAccumulatorType(Checker()->GlobalIntType());
3099 }
3100 
LoadArrayElement(const ir::AstNode * node,VReg objectReg)3101 void ETSGen::LoadArrayElement(const ir::AstNode *node, VReg objectReg)
3102 {
3103     ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr);
3104     auto *elementType = GetVRegType(objectReg)->AsETSArrayType()->ElementType();
3105     if (elementType->IsETSReferenceType()) {
3106         Ra().Emit<LdarrObj>(node, objectReg);
3107         SetAccumulatorType(elementType);
3108         return;
3109     }
3110     switch (checker::ETSChecker::ETSType(elementType)) {
3111         case checker::TypeFlag::ETS_BOOLEAN:
3112         case checker::TypeFlag::BYTE: {
3113             Ra().Emit<Ldarr8>(node, objectReg);
3114             break;
3115         }
3116         case checker::TypeFlag::CHAR: {
3117             Ra().Emit<Ldarru16>(node, objectReg);
3118             break;
3119         }
3120         case checker::TypeFlag::SHORT: {
3121             Ra().Emit<Ldarr16>(node, objectReg);
3122             break;
3123         }
3124         case checker::TypeFlag::INT: {
3125             Ra().Emit<Ldarr>(node, objectReg);
3126             break;
3127         }
3128         case checker::TypeFlag::LONG: {
3129             Ra().Emit<LdarrWide>(node, objectReg);
3130             break;
3131         }
3132         case checker::TypeFlag::FLOAT: {
3133             Ra().Emit<Fldarr32>(node, objectReg);
3134             break;
3135         }
3136         case checker::TypeFlag::DOUBLE: {
3137             Ra().Emit<FldarrWide>(node, objectReg);
3138             break;
3139         }
3140 
3141         default: {
3142             ES2PANDA_UNREACHABLE();
3143         }
3144     }
3145 
3146     SetAccumulatorType(elementType);
3147 }
3148 
StoreArrayElement(const ir::AstNode * node,VReg objectReg,VReg index,const checker::Type * elementType)3149 void ETSGen::StoreArrayElement(const ir::AstNode *node, VReg objectReg, VReg index, const checker::Type *elementType)
3150 {
3151     if (elementType->IsETSReferenceType()) {
3152         Ra().Emit<StarrObj>(node, objectReg, index);
3153         SetAccumulatorType(elementType);
3154         return;
3155     }
3156     switch (checker::ETSChecker::ETSType(elementType)) {
3157         case checker::TypeFlag::ETS_BOOLEAN:
3158         case checker::TypeFlag::BYTE: {
3159             Ra().Emit<Starr8>(node, objectReg, index);
3160             break;
3161         }
3162         case checker::TypeFlag::CHAR:
3163         case checker::TypeFlag::SHORT: {
3164             Ra().Emit<Starr16>(node, objectReg, index);
3165             break;
3166         }
3167         case checker::TypeFlag::INT: {
3168             Ra().Emit<Starr>(node, objectReg, index);
3169             break;
3170         }
3171         case checker::TypeFlag::LONG: {
3172             Ra().Emit<StarrWide>(node, objectReg, index);
3173             break;
3174         }
3175         case checker::TypeFlag::FLOAT: {
3176             Ra().Emit<Fstarr32>(node, objectReg, index);
3177             break;
3178         }
3179         case checker::TypeFlag::DOUBLE: {
3180             Ra().Emit<FstarrWide>(node, objectReg, index);
3181             break;
3182         }
3183 
3184         default: {
3185             ES2PANDA_UNREACHABLE();
3186         }
3187     }
3188 
3189     SetAccumulatorType(elementType);
3190 }
3191 
GetTupleMemberNameForIndex(const std::size_t index) const3192 util::StringView ETSGen::GetTupleMemberNameForIndex(const std::size_t index) const
3193 {
3194     return util::UString("$" + std::to_string(index), Allocator()).View();
3195 }
3196 
LoadTupleElement(const ir::AstNode * node,VReg objectReg,const checker::Type * elementType,std::size_t index)3197 void ETSGen::LoadTupleElement(const ir::AstNode *node, VReg objectReg, const checker::Type *elementType,
3198                               std::size_t index)
3199 {
3200     ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr && GetVRegType(objectReg)->IsETSTupleType());
3201     const auto propName = FormClassPropReference(GetVRegType(objectReg)->AsETSTupleType()->GetWrapperType(),
3202                                                  GetTupleMemberNameForIndex(index));
3203 
3204     // NOTE (smartin): remove after generics without type erasure is possible
3205     const auto *const boxedElementType = Checker()->MaybeBoxType(elementType);
3206     LoadProperty(node, boxedElementType, objectReg, propName);
3207 }
3208 
StoreTupleElement(const ir::AstNode * node,VReg objectReg,const checker::Type * elementType,std::size_t index)3209 void ETSGen::StoreTupleElement(const ir::AstNode *node, VReg objectReg, const checker::Type *elementType,
3210                                std::size_t index)
3211 {
3212     ES2PANDA_ASSERT(GetVRegType(objectReg) != nullptr && GetVRegType(objectReg)->IsETSTupleType());
3213     const auto *const tupleType = GetVRegType(objectReg)->AsETSTupleType();
3214     SetVRegType(objectReg, tupleType->GetWrapperType());
3215 
3216     // NOTE (smartin): remove after generics without type erasure is possible
3217     const auto *const boxedElementType = Checker()->MaybeBoxType(elementType);
3218     StoreProperty(node, boxedElementType, objectReg, GetTupleMemberNameForIndex(index));
3219 }
3220 
3221 template <typename T>
IncrementImmediateRegister(const ir::AstNode * node,VReg reg,const checker::TypeFlag valueType,T const value)3222 void ETSGen::IncrementImmediateRegister(const ir::AstNode *node, VReg reg, const checker::TypeFlag valueType,
3223                                         T const value)
3224 {
3225     switch (valueType) {
3226         // NOTE: operand of increment instruction (INCI) is defined in spec as 32-bit integer,
3227         // but its current implementation actually can work with 64-bit integers as well.
3228         case checker::TypeFlag::INT: {
3229             Ra().Emit<Inci>(node, reg, static_cast<checker::IntType::UType>(value));
3230             break;
3231         }
3232         case checker::TypeFlag::CHAR: {
3233             Ra().Emit<Inci>(node, reg, static_cast<checker::CharType::UType>(value));
3234             break;
3235         }
3236         case checker::TypeFlag::SHORT: {
3237             Ra().Emit<Inci>(node, reg, static_cast<checker::ShortType::UType>(value));
3238             break;
3239         }
3240         case checker::TypeFlag::ETS_BOOLEAN:
3241             [[fallthrough]];
3242         case checker::TypeFlag::BYTE: {
3243             Ra().Emit<Inci>(node, reg, static_cast<checker::ByteType::UType>(value));
3244             break;
3245         }
3246         default: {
3247             ES2PANDA_UNREACHABLE();
3248         }
3249     }
3250 }
3251 
3252 template void ETSGen::IncrementImmediateRegister<int32_t>(const ir::AstNode *node, VReg reg,
3253                                                           const checker::TypeFlag valueType, int32_t const value);
3254 
LoadStringLength(const ir::AstNode * node)3255 void ETSGen::LoadStringLength(const ir::AstNode *node)
3256 {
3257     Ra().Emit<CallAccShort, 0>(node, Signatures::BUILTIN_STRING_LENGTH, dummyReg_, 0);
3258     SetAccumulatorType(Checker()->GlobalIntType());
3259 }
3260 
FloatIsNaN(const ir::AstNode * node)3261 void ETSGen::FloatIsNaN(const ir::AstNode *node)
3262 {
3263     Ra().Emit<CallAccShort, 0>(node, Signatures::BUILTIN_FLOAT_IS_NAN, dummyReg_, 0);
3264     SetAccumulatorType(Checker()->GlobalETSBooleanType());
3265 }
3266 
DoubleIsNaN(const ir::AstNode * node)3267 void ETSGen::DoubleIsNaN(const ir::AstNode *node)
3268 {
3269     Ra().Emit<CallAccShort, 0>(node, Signatures::BUILTIN_DOUBLE_IS_NAN, dummyReg_, 0);
3270     SetAccumulatorType(Checker()->GlobalETSBooleanType());
3271 }
3272 
LoadStringChar(const ir::AstNode * node,const VReg stringObj,const VReg charIndex)3273 void ETSGen::LoadStringChar(const ir::AstNode *node, const VReg stringObj, const VReg charIndex)
3274 {
3275     Ra().Emit<CallShort>(node, Signatures::BUILTIN_STRING_CHAR_AT, stringObj, charIndex);
3276     SetAccumulatorType(Checker()->GlobalCharType());
3277 }
3278 
ThrowException(const ir::Expression * expr)3279 void ETSGen::ThrowException(const ir::Expression *expr)
3280 {
3281     RegScope rs(this);
3282 
3283     expr->Compile(this);
3284     VReg arg = AllocReg();
3285     StoreAccumulator(expr, arg);
3286     EmitThrow(expr, arg);
3287 }
3288 
ExtendWithFinalizer(ir::AstNode const * node,const ir::AstNode * originalNode,Label * prevFinnaly)3289 bool ETSGen::ExtendWithFinalizer(ir::AstNode const *node, const ir::AstNode *originalNode, Label *prevFinnaly)
3290 {
3291     ES2PANDA_ASSERT(originalNode != nullptr);
3292 
3293     if (node == nullptr || !node->IsStatement()) {
3294         return false;
3295     }
3296 
3297     if ((originalNode->IsContinueStatement() && originalNode->AsContinueStatement()->Target() == node) ||
3298         (originalNode->IsBreakStatement() && originalNode->AsBreakStatement()->Target() == node)) {
3299         return false;
3300     }
3301 
3302     if (node->IsTryStatement() && node->AsTryStatement()->HasFinalizer()) {
3303         Label *beginLabel = nullptr;
3304 
3305         if (prevFinnaly == nullptr) {
3306             beginLabel = AllocLabel();
3307             Branch(originalNode, beginLabel);
3308         } else {
3309             beginLabel = prevFinnaly;
3310         }
3311 
3312         Label *endLabel = AllocLabel();
3313 
3314         if (node->Parent() != nullptr && node->Parent()->IsStatement()) {
3315             if (!ExtendWithFinalizer(node->Parent(), originalNode, endLabel)) {
3316                 endLabel = nullptr;
3317             }
3318         } else {
3319             endLabel = nullptr;
3320         }
3321 
3322         LabelPair insertion = compiler::LabelPair(beginLabel, endLabel);
3323 
3324         auto *tryStatement = const_cast<ir::AstNode *>(node)->AsTryStatement();
3325         tryStatement->AddFinalizerInsertion(insertion, originalNode->AsStatement());
3326 
3327         return true;
3328     }
3329 
3330     auto *parent = node->Parent();
3331 
3332     if (parent == nullptr || !parent->IsStatement()) {
3333         return false;
3334     }
3335 
3336     if (parent->IsTryStatement() && node->IsBlockStatement() &&
3337         parent->AsTryStatement()->FinallyBlock() == node->AsBlockStatement()) {
3338         parent = parent->Parent();
3339     }
3340 
3341     return ExtendWithFinalizer(parent, originalNode, prevFinnaly);
3342 }
3343 
ToAssemblerType(const es2panda::checker::Type * type) const3344 util::StringView ETSGen::ToAssemblerType(const es2panda::checker::Type *type) const
3345 {
3346     ES2PANDA_ASSERT(type != nullptr && type->IsETSReferenceType());
3347 
3348     std::stringstream ss;
3349     type->ToAssemblerTypeWithRank(ss);
3350     return util::UString(ss.str(), Allocator()).View();
3351 }
3352 
3353 template <typename T>
SetAccumulatorTargetType(const ir::AstNode * node,checker::TypeFlag typeKind,T number)3354 void ETSGen::SetAccumulatorTargetType(const ir::AstNode *node, checker::TypeFlag typeKind, T number)
3355 {
3356     switch (typeKind) {
3357         case checker::TypeFlag::ETS_BOOLEAN:
3358         case checker::TypeFlag::BYTE: {
3359             Sa().Emit<Ldai>(node, static_cast<checker::ByteType::UType>(number));
3360             SetAccumulatorType(Checker()->GlobalByteType());
3361             break;
3362         }
3363         case checker::TypeFlag::CHAR: {
3364             Sa().Emit<Ldai>(node, static_cast<checker::CharType::UType>(number));
3365             SetAccumulatorType(Checker()->GlobalCharType());
3366             break;
3367         }
3368         case checker::TypeFlag::SHORT: {
3369             Sa().Emit<Ldai>(node, static_cast<checker::ShortType::UType>(number));
3370             SetAccumulatorType(Checker()->GlobalShortType());
3371             break;
3372         }
3373         case checker::TypeFlag::INT: {
3374             Sa().Emit<Ldai>(node, static_cast<checker::IntType::UType>(number));
3375             SetAccumulatorType(Checker()->GlobalIntType());
3376             break;
3377         }
3378         case checker::TypeFlag::LONG: {
3379             Sa().Emit<LdaiWide>(node, static_cast<checker::LongType::UType>(number));
3380             SetAccumulatorType(Checker()->GlobalLongType());
3381             break;
3382         }
3383         case checker::TypeFlag::FLOAT: {
3384             Sa().Emit<Fldai>(node, static_cast<checker::FloatType::UType>(number));
3385             SetAccumulatorType(Checker()->GlobalFloatType());
3386             break;
3387         }
3388         case checker::TypeFlag::DOUBLE: {
3389             Sa().Emit<FldaiWide>(node, static_cast<checker::DoubleType::UType>(number));
3390             SetAccumulatorType(Checker()->GlobalDoubleType());
3391             break;
3392         }
3393         default: {
3394             ES2PANDA_UNREACHABLE();
3395         }
3396     }
3397 }
3398 
3399 template <typename T>
LoadAccumulatorNumber(const ir::AstNode * node,T number,checker::TypeFlag targetType)3400 void ETSGen::LoadAccumulatorNumber(const ir::AstNode *node, T number, checker::TypeFlag targetType)
3401 {
3402     // NOTE (smartin): refactor this to do at a more appropriate place
3403     const bool isContextTargetTypeValid =
3404         targetType_ != nullptr &&
3405         (!targetType_->IsETSObjectType() && !targetType_->IsETSUnionType() && !targetType_->IsETSArrayType() &&
3406          !targetType_->IsETSTupleType() && !targetType_->IsETSTypeParameter());
3407 
3408     auto typeKind = isContextTargetTypeValid ? checker::ETSChecker::TypeKind(targetType_) : targetType;
3409 
3410     SetAccumulatorTargetType(node, typeKind, number);
3411 
3412     if (targetType_ && (targetType_->IsETSObjectType() || targetType_->IsETSUnionType())) {
3413         ApplyConversion(node, targetType_);
3414     }
3415 }
3416 }  // namespace ark::es2panda::compiler
3417